
Config locali dell'IDE. BACKLOG.md. Appunti di lavoro. Una cartella scratch/ con esperimenti e test che non vuoi perdere. .claude/ con i prompt dell'assistente AI. docker-compose.override.yml per il tuo ambiente di sviluppo locale.
Tutti li hanno. Non tutti li vogliono/possono versionare.
E quando li perdi (un rm -rf di troppo, un cambio di macchina, un collega che clona il progetto e non trova più nulla) ti accorgi che quei file erano più importanti di quanto pensavi.
Le soluzioni artigianali non mancano:
- Cartella Dropbox a fianco del repo. Funziona finché ti ricordi di usarla.
- Gist sparsi. Impossibili da tenere sincronizzati.
- Branch dedicato. Inquina la storia del progetto.
- Repo separato gestito a mano. Funziona per un progetto. Non per venti.
Esistono anche tool dedicati. vcsh usa un bare repository nella home directory ed è pensato per i dotfile personali: .bashrc, .vimrc, configurazioni globali. Ha il suo caso d'uso ed è buono in quello, ma non gestisce file legati a un singolo progetto. yadm segue la stessa filosofia: dotfile globali, un solo repository per tutta la home. Entrambi ragionano a livello di macchina, non di progetto. Il problema che serviva risolvere è diverso: versionare file che appartengono a quel specifico repository, che sono identificati dal progetto stesso, ma che non possono stare nel repo principale. Né vcsh né yadm hanno questo concetto di identità progetto.
Il problema è sempre lo stesso: quei file vivono accanto al tuo codice, ma Git non li vuole. E le alternative (artigianali o strutturate che siano) non risolvono il nodo centrale: tenere i file legati al progetto, con il rigore che Git ti dà gratuitamente (storia, diff, remote, branch), senza lasciare tracce nel repo principale.
L'esigenza
Solexma aveva bisogno di uno strumento per gestire file di contesto (appunti, backlog, configurazioni locali, scratch) su decine di repository, senza inquinare i repo dei clienti e senza dipendere da soluzioni artigianali.
Il requisito era chiaro: versionamento reale, Git-native, ma completamente invisibile al repository principale. Niente submodule, niente branch orfani, niente file di configurazione nel repo del cliente.
Ho sviluppato git-side per risolvere esattamente questo problema.
Come funziona
Un bare repository separato, invisibile al repo principale, dedicato esclusivamente ai file "a lato" del progetto. Zero inquinamento: il repo principale non sa nemmeno che git-side esiste.
Il side repo vive fuori dalla directory del progetto, in una posizione standard di sistema (che è ovviamente differente tra MacOS e Linux).
Ogni progetto ha il suo, identificato in modo univoco dallo SHA del primo commit: un'identità immutabile, stabile tra clone, indipendente dal path del filesystem o dal remote URL.
Nella pratica:
# Traccia un file
git side add BACKLOG.md
# Traccia un'intera directory (tutto il contenuto, ricorsivamente)
git side add scratch/
# Commit manuale
git side commit -m "Aggiunto backlog e appunti"
# Oppure: auto-sync che usa il messaggio dell'ultimo commit
git side auto
# Installa un hook post-commit per sincronizzare automaticamente
git side hook install
Il workflow più comune dopo il setup iniziale: non devi fare nulla. L'hook post-commit si occupa di tutto. Ogni volta che fai commit nel repo principale, il side repo si sincronizza in automatico.
Per condividere i file "a lato" tra macchine o con colleghi:
git side remote add origin [email protected]:user/project-side.git
git side push # force push: il locale vince sempre
git side pull # fetch + reset: il remoto vince sempre
Scelte di design
Ogni decisione in git-side è stata deliberata. Alcune sono opinionate.
Force push/pull senza merge. Non c'è risoluzione di conflitti. Push = il locale vince, sempre. Pull = il remoto vince, sempre. Per file "a lato" (appunti, config locali, scratch) la complessità del merge non ha senso. La semplicità qui è una feature, non una limitazione.
Bypass delle ignore rules. git side add usa internamente git add -f. Non è un workaround: i file tracciati da git-side sono per definizione in .gitignore. "Ignorato dal repo principale" non significa "non versionabile". Sono due concetti distinti.
Shell-out a git binary. Niente libgit2, niente binding nativi. git-side chiama direttamente il git installato sulla macchina. Questo garantisce comportamento identico a quello che l'utente conosce, zero dipendenze native, build più semplice.
Self-versioning della lista tracciata. Il file .side-tracked, la lista di cosa è tracciato, vive dentro il side repo stesso, versionato tramite git plumbing (hash-object + update-index). Quando fai push, la lista viaggia con i file. Chi fa pull sa esattamente cosa tracciare.
Non è per i segreti. File come .env, API key, token, credenziali non vanno committati da nessuna parte. Per quelli serve un secrets manager. La tentazione di "versionare tutto per sicurezza" è forte, soprattutto in ambienti dove i dati non sono governati. Ma la sicurezza si ottiene con strumenti dedicati, non con un commit.
Ogni decisione è documentata in dettaglio nei relativi Architecture Decision Record.
Primi risultati
git-side è stato testato su oltre 150 repository nelle prime 48 ore dal rilascio, da utenti interni Solexma, da me direttamente e da utenti esterni.
Il campione non era omogeneo, e questo è stato il punto di forza del test. C'erano monorepo con decine di package, polyrepo classici con un servizio per repository, repository con submodule annidati, repository con worktree multipli. Il meccanismo di identificazione tramite SHA del primo commit ha retto in tutti i casi: anche quando lo stesso progetto veniva clonato in path diversi, o quando il remote URL cambiava (migrazione da GitLab a GitHub, per esempio), il side repo restava lo stesso. Con i worktree c'è stata una sorpresa positiva: ogni worktree condivide lo stesso primo commit del repository originale, quindi lo stesso side repo. Esattamente il comportamento desiderato.
I primi 150 repository hanno fatto emergere due cose che non avevo previsto. La prima: la dimensione dei file tracciati. git-side era pensato per file piccoli (appunti, config, backlog), ma alcuni utenti ci hanno messo dentro intere cartelle di screenshot e diagrammi. Ha funzionato, ma ha evidenziato la necessità di un avviso quando il side repo supera una certa dimensione, quanto meno il semplice suggerire l'uso di git-lfs.
Un terzo caso emerso dal test riguarda i repository con file .side-tracked già presenti da un push precedente. Se un collega aveva già configurato il side repo con una lista di file tracciati, il pull portava con sé anche quella lista. Chi faceva pull per la prima volta si trovava il tracking già configurato, senza dover sapere quali file tracciare. Questo non era un obiettivo di design esplicito, ma una conseguenza del self-versioning della lista tracciata. Si è rivelato uno dei vantaggi più apprezzati nel contesto di team.
La seconda: la resistenza. Quando l'ho pubblicato, la reazione più frequente è stata "usa .gitignore", seguita da "usa un submodule". Entrambe risposte ragionevoli, da parte di chi non ha il problema che git-side risolve. Il punto è proprio quello: il repo principale non deve sapere che quei file esistono. Niente riferimenti, niente .gitmodules, niente tracce. Un submodule lascia una referenza. Un .gitignore ignora i file, ma non li versiona altrove. git-side fa entrambe le cose: ignora e versiona, senza toccare il repo principale.
Qualcuno l'ha definito "insanely overcomplicated". Ha ragione, in un certo senso: se non hai il problema, la soluzione sembra assurda. Ma chi lavora come terza parte in aziende con policy rigide su cosa può e cosa non può finire nel repository sa che il problema esiste. E chi ha iniziato a usarlo per versionare i propri file CLAUDE.md locali, o le configurazioni IDE personali, ha capito subito perché serviva. Una PR per la compatibilità Windows è arrivata il primo giorno.
Il tool è scritto in Rust, distribuito come singolo binario, e rilasciato open-source.
Codice sorgente: github.com/Solexma/git-side
Queste decisioni le ho documentate in modo più strutturato come Architecture Decision Record, con contesto reale, trade-off valutati e risultati osservati.
ADR260201a: Identità progetto tramite initial commit SHA → ADR260201b: Bare repository esterno come strategia di isolamento → ADR260201c: Shell-out a git binary invece di libgit2 → ADR260201d: Force push/pull senza merge resolution → ADR260201e: Self-versioning di .side-tracked via git plumbing → ADR260201f: Bypass delle ignore rules come scelta deliberata →Se anche tu hai file che vivono ai margini del tuo progetto (appunti, config, esperimenti) e ogni volta che li perdi pensi "avrei dovuto versionarli"... prova git-side.
È esattamente per quello che l'ho costruito.

In sintesi (TL;DR)
- Ogni progetto ha file che non appartengono al repo (appunti, config IDE, scratch) ma che vale la pena versionare.
- git-side usa un bare repository separato, invisibile al repo principale, identificato dallo SHA del primo commit.
- Force push/pull senza merge: per file di contesto la semplicità è una feature, non una limitazione.
- Scritto in Rust, open-source, testato su oltre 150 repository nelle prime 48 ore dal rilascio.