2013-08-01 25 views
17

Domanda

Premesso che ho un impegno simile al seguente:Come linearizzare banalmente la mia cronologia git?

A - B - C - D - G - H 
    \  /
     - E - F - 

Come faccio ad avere una storia perfettamente lineare seguendo solo i primi genitori?

Ad esempio, voglio ottenere:

A - B - C - D - G'- H' 

Si prevede che le SHA1 di di G e H cambieranno, come osservato in precedenza. (Per ovvie ragioni). Se cambiano anche gli sha1 per A, B, C e D, allora voglio sapere perché.

Sfondo

L'intento è quello di evitare il potenziale fusione impegna che deriverebbero quando si fa un git rebase -i ingenuo.

Il punto vero è quello di ottenere spera ulteriori indizi per determinare che impegna trovano in un ramo particolare, ad esempio, se abbiamo il seguente grafico:

I - J - K - L - M 
\ ///
    - N - O - P - 

Dove N è stata incorporata in K, ma O è stato fuso con --strategy = ours in L, e P è stato fuso in M.

Desidero essere in grado di linearizzare la mia cronologia in modo che tali commit problematici possano essere identificati e controllati. Voglio avere un albero in cui posso identificare che O non è stato inserito nel ramo upstream, anche se potenzialmente identifico N e P come potenzialmente mancanti dal ramo upstream, usando git cherry, tuttavia qualsiasi suggerimento qui sarebbe apprezzato .

+2

Per riformulare il problema, si desidera modificare ogni commit di unione per conservare solo il suo primo genitore, dimenticando il ramo che è stato unito (mantenendo le differenze ovviamente) – CharlesB

+0

@CharlesB: Sì, è vero! – Arafangion

risposta

17

Come detto, le seguenti opere: comando

git filter-branch --parent-filter 'cut -f 2,3 -d " "' 

Perché?

Il problema che si pone viene risolto trasformando ogni commit di unione con un commit semplice: questo rimuoverà semplicemente i rami di feature che sono stati uniti, poiché diventeranno orfani.

Ogni commit ha uno o più commit padre. Unisci commit è quello che ottiene più di uno. Git lo memorizza in ogni oggetto di commit della cronologia.

Il comando , con l'opzione --parent-filter, consente di riscrivere ogni padre del commit, passato al filtro come -p SHA1, ripetuto se ci sono più di un genitore.Il nostro filtro taglia il genitore e forza ogni commit ad avere un solo genitore.

Per bonus, ecco come farlo manualmente su un preciso commit, ricreando un nuovo commit:

  • ottenere l'albero ed il primo genitore commit

    tree=`git show -s --format=%T SHA1` 
    parent=`git show -s --format=%P SHA1 | cut -d " " -f1` 
    
  • make un nuovo commit con lo stesso albero, lo stesso messaggio e mantenere solo il primo genitore come antenato

    git show -s --format=%B SHA1 | git commit-tree $tree -p $parent 
    
+1

Questo mi ha ispirato a provare: 'git filter-branch --parent-filter 'cut -f 2,3 -d" "'' Sembra funzionare, ma non sono ancora sicuro dei casi limite o delle prestazioni. Ho molte centinaia di commit piuttosto grandi, però. Aspetterò la tua risposta, potresti avere una soluzione migliore/migliore. – Arafangion

+0

Non lo so, ma sembra molto più semplice. Volevo usare '--commit-filter' con questo tipo di script, ma ho anche visto l'opzione' --parent-filter' anche se non capisco cosa sia il 'cut -f 2,3' nel tuo comando? – CharlesB

+0

Il filtro principale specifica un programma che si aspetta un input su stdin come '-p sha1 -p sha1 -p sha1', ogni -p specifica un genitore. (Quella stringa c'era un commit di unione con tre genitori). Il '-d ""' specifica che lo interpretiamo come una lista, delimitata da spazi. -f ci specifica 'Prendi il secondo e il terzo oggetto' .Questo risulta nel primo '-p sha1' e il gioco è fatto. – Arafangion

5

farlo manualmente con il seguente:

git rebase -i D-sha1 

Modificare il file di avere G impostato su "modifica". Mantieni "seleziona" per H. Salva e chiudi editor.

Ora Git ti consente di modificare G commit, cambialo in un'unione di squash del commit F. Si desidera:

#cancel the merge commit and position HEAD to D 
git reset --hard HEAD~ 
#squash merge and continue 
git merge --squash F-sha1 
git rebase --continue 

noti che dovrete risolvere gli stessi conflitti se si dovesse in primo merge ...

come dici tu dovrai solo nuova SHA1 per G e H.

+0

Come identifico 'G' e 'H'? Diciamo che ho un grande repository, non solo il caso banale che ho evidenziato, e ci sono molti di questi commit di unione. – Arafangion

+0

Se è troppo complicata la cronologia con l'unione diversa che si impegna a linearizzare, si potrebbe stare lontani da questo, quindi. Git rebase non lo farà correttamente – CharlesB

+0

Sono pienamente consapevole che git rebase non lo farà. Mi aspetto che la risposta riguardi il ramo del filtro git con il set del filtro -parente. – Arafangion

Problemi correlati