Git non è uno strumento molto adatto per questo. In particolare, git è stato creato per tenere traccia della cronologia delle modifiche esatte dei dati. Mentre il sistema di ramificazione offre supporto per mantenere aggiornati i diversi fork, questo non è molto conveniente. Supponiamo che tu abbia il seguente grafico cronologico:
a1--a2 branch a
\
b1 branch b
\
c1--c2 branch c
Ora vuoi correggere un errore sul ramo a
. Lo facciamo e commettiamo la modifica come a3
:
a1--a2--a3 branch a
\
b1 branch b
\
c1--c2 branch c
Per ottenere la modifica agli altri rami, possiamo rebase a ogni ramo sul suo predecessore, che rigirerà i commit di ogni ramo che viene ridefinito:
a1--a2--a3 branch a
\
b1' branch b
\
c1'--c2' branch c
Oppure potremmo unire ogni ramo nel suo successore (non è possibile l'inoltro veloce, quindi otterremo commit espliciti di fusione b2
e c3
):
a1--a2------a3 branch a
\ \
b1------b2 branch b
\ \
c1--c2--c3 branch c
Mentre il rebasing rende la storia più "carina", l'unione si impegna a dirci la vera storia del progetto. In entrambi i casi, qualsiasi modifica modificherà tutti i rami dipendenti e la cronologia mostrerà chiaramente il commit di correzione a3
. Git non automatizza mantenendo aggiornati i rami dipendenti, ma potresti scrivere uno script per aiutarti. Tieni presente che l'unione o il rebase possono essere interrotti se le modifiche non possono essere risolte automaticamente, nel qual caso dovrai risolverlo manualmente.
Usando questo schema, le differenze tra i rami rimarranno abbastanza costanti per le modifiche nel ramo base, poiché la modifica verrà propagata a tutti i rami dipendenti.
Strategia alternativa: tag ✘
Se vuoi essere in grado di correggere le "versioni" precedenti, semplicemente registrare la cronologia e contrassegnare gli stati interessanti non è un'opzione praticabile. Potremmo iniziare con una storia come questa:
a1--a2--b1--c1--c2 branch master
↑ ↑ ↑
tag a tag b tag c
Potremmo diramarti per creare un ramo fix
dove eseguiamo il commit della correzione a3
:
a3 branch fix
/
a1--a2--b1--c1--c2 branch master
↑ ↑ ↑
tag a tag b tag c
Potremmo quindi rebase il ramo master
in fix
:
a3--b1'--c1'--c2' branch master
/
a1--a2--b1--c1--c2
↑ ↑ ↑
tag a tag b tag c
Sfortunatamente, i tag si riferiscono ai loro commit originali e non sono stati aggiornati.
Strategia alternativa: più cartelle con git remote
✔
Quindi, c'è la possibilità di memorizzare semplicemente ogni iterazione in una directory separata. Questa è in realtà l'opzione più user-friendly poiché non richiede la conoscenza git per accedere a uno stato specifico, ma questo rende più difficile monitorare correttamente le modifiche. Una possibilità di usare git sarebbe quella di avere un repository git separato in ogni cartella di iterazione. Ogni iterazione contrassegna quindi il repository dell'iterazione precedente come repository upstream. Dopo ogni modifica, dovresti git pull
le modifiche per ciascun repository dipendente.
Sebbene questo sia il più difficile da configurare, questa è probabilmente la soluzione migliore e più intuitiva per tutti i partecipanti.
Ecco un codice per impostare un progetto di esempio usando questa strategia:
#!/bin/sh
# create the three iterations a, b, c:
mkdir a b c
# fill each repository and set up upstream repos
cd a
git init
git commit --allow-empty -m 'a1'
git commit --allow-empty -m 'a2'
cd ../b
git clone --branch master ../a .
git commit --allow-empty -m 'b1'
cd ../c
git clone --branch master ../b .
git commit --allow-empty -m 'c1'
git commit --allow-empty -m 'c2'
cd ..
Ora creiamo un commit a3
:
cd a
git commit --allow-empty -m 'a3'
cd ..
Per aggiornare gli altri repository, pubblichiamo semplicemente un git pull
:
cd b
git pull # enter message for merge commit
cd ../c
git pull # enter message for merge commit
cd ..
Dopo aver eseguito quei comandi, il log risultante in c
sembrava tipo
* d8943fb c3
|\
| * c670d4f b2
| |\
| | * f2ae31e a3
* | | e362453 c2
* | | 6e2a2d7 c1
|/ /
* | c78583c b1
|/
* 3c377e4 a2
* 098f321 a1
Questa strategia finisce per essere funzionalmente equivalente a propagare le modifiche tramite fusione, ma potrebbe essere un po 'più facile da usare, specialmente per i neofiti che lavorano attraverso il tuo tutorial se hanno scaricato il codice come archivio zip. D'altra parte, questo rende più difficile distribuire il tutorial completo come un repository git, poiché tutto è distribuito su più repository collegati. Se questo è importante, l'unione ordinaria equivalente descritta in alto è più probabile che sia migliore.