2012-09-17 8 views
25

Supponiamo che ho appena ribasato il ramo foo su master, con conflitti. Voglio assicurarmi di non danneggiare accidentalmente il contenuto di foo durante la risoluzione dei conflitti introducendo modifiche extra o perdendo le modifiche (oltre a quelle appropriate per la risoluzione dei conflitti). Ho fatto questo tramite:Confronto delle differenze attraverso un rebase in Git

diff -u <(git diff `git merge-base master [email protected]{1}` [email protected]{1}) \ 
     <(git diff `git merge-base master foo ` foo ) 

(aggiornamento: o l'equivalente ... sintassi per git-diff che ho appena ricordato :)

diff -u <(git diff [email protected]{1}) <(git diff master...foo) | mate 

Questo mi mostra tutti i cambiamenti che si sono verificati a master..foo considerato come una patch, che è esattamente quello che voglio controllare per essere minimo. Tuttavia, l'invocazione è complessa e l'output non è del tutto semplice da interpretare.

Esiste un modo migliore per eseguire questa attività: fornire le stesse informazioni, ma con un metodo o un formato migliore, oppure devo semplicemente prendere quanto sopra e racchiuderlo in uno script?

+0

Non è essenzialmente equivalente a 'git diff foo @ {1} foo'? – twalberg

+1

@twalberg No; 'git diff foo @ {1} foo' mostra sia le mie risoluzioni di conflitto sia le modifiche apportate al master su cui mi sono basato, piuttosto che solo le risoluzioni dei conflitti. –

+0

Ah, giusto ... Stavo solo considerando il mio caso Rebase più comune, in cui la fusione tra origine e destinazione rimane la stessa (riscrittura/schiacciamento di un ramo prima di premere). – twalberg

risposta

2

Differenze tra master e foo ricalcolato:

git diff master..foo

vs.

differenze di pre-rebase foo dopo dirama master (nota tre punti):

git diff [email protected]{1}

?

+0

Grazie, usando' ... "accorcia il comando, e in realtà l'ho usato prima Avrei dovuto pensarci. Tuttavia, mi rimane ancora il confronto esplicito tra queste due differenze. Hai un suggerimento per questo? –

2

Poiché si tratta di rebase e non di un'unione, è possibile confrontare foo con se stesso ma prima dell'unione. Se ricordo correttamente, [email protected]{1} produrrà il genitore del commit corrente per foo, e forse questo non è quello che stai cercando.

Penso che si possa fare qualcosa di simile alla seguente (supponendo che non avete fatto un git gc):

Sul ramo foo dopo il rebase:

$ git reflog 

questo mostrerà come la testa del ramo è stato spostato Si dovrebbe vedere alcuni record come questo (a seconda se si rebase interattivo o meno):

64d3c9e [email protected]{15}: rebase -i (finish): returning to refs/heads/master 
64d3c9e [email protected]{16}: rebase -i (fixup): Fixes external dependencies 
049169e [email protected]{17}: rebase -i (fixup): updating HEAD 
d4c2e69 [email protected]{18}: rebase -i (pick): Fixes external dependencies 
049169e [email protected]{19}: rebase -i (fixup): Imports futures 
e490bed [email protected]{20}: rebase -i (fixup): updating HEAD 
... 
etc 
... 

guardare avanti per la ultimo dei commit foo prima della fusione. A seconda di ciò che hai fatto, potrebbe essere difficile o no. Questo è il motivo per cui non ho fornito un copione fai-da-te.

Raccogliere l'ID di commit che ha l'ultimo commit per foo prima del rebase e quindi confrontare con quell'ID di commit. Dire che Commit ID è: XXXX:

git diff XXXX...foo 

Forse è questo ciò che si desidera.

+0

Come ho risposto a twalberg, questo non escluderà le modifiche al master su cui mi sono basato e quindi è molto rumoroso. (A proposito, scavare attraverso il reflog di HEAD non è necessario, il reflog di * branch * conterrà solo gli stati before e after, e 'foo @ {1}' è esattamente l'impegno che si propone di cercare manualmente. '@' guarda nel reflog, non nell'ascendenza.) –

+0

@KevinReid Hai ragione, ho confuso '@' con '~'. Non li uso spesso. Scusate. – manu

1

Un approccio leggermente diverso: Git ha un buon supporto per mostrarlo nel caso di unioni. cioè"cosa è cambiato rispetto a parent-1 che non può essere spiegato da parent-2?"

ci sono un paio di modi si potrebbe sfruttare tale supporto per il vostro rebase:

Opzione 1:

  1. fare una rimessa via merge prima (al posto di un rebase). Quindi il normale display di fusione mostrerà cosa è stata cambiata la fusione. Controlla questo è quello che volevi.

  2. Torna indietro e rebase e confronta il suggerimento rebasato con il risultato della fusione: devono essere identici.

Opzione 2 (se rebasing è più facile che la fusione):

  1. fare il rebase.
  2. Utilizzare un innesto per renderlo simile a un'unione localmente/temporaneamente.

Per fare questo, creare un .git/informazioni/innesti con una sola riga:

TIP UPSTREAM OLDTIP 

Dove TIP è l'ID commit del ramo ricalcolato e gli altri sono i due genitori desiderati (vale a dire le due cose che avresti fuso, se avessi fatto una fusione). Quindi esaminalo come se fosse una vera fusione.

Non sono sicuro di automatizzarlo; Tendo a fare queste cose con gitk open, perché rende facile confrontare le revisioni giuste (usando il menu del tasto destro del mouse).

Se si desidera confrontare due diffs, è possibile che si desideri esaminare l'interdiff (1), ma non sono sicuro di quanto possa gestire i file di base in modo diverso.

+0

Per quanto riguarda l'opzione 1, nei casi in cui voglio effettuare questo controllo, è molto probabile che un'unione abbia * conflitti diversi * rispetto a un rebase e, in ogni caso, sto cercando di verificare il rebase effettivamente accaduto. Per quanto riguarda l'opzione 2, l'ho provato e ho scoperto che la vista è confusa con le modifiche introdotte sul master, che è esattamente ciò che voglio nascondere. Non ho "interdiff", ma la sua pagina man dice "Le differenze devono essere entrambe relative agli stessi file." –

+0

Ho una risposta simile a questa che funziona per una serie ed è fondamentalmente "usa solo commit-tree per generare il merge post-rebase assumendo che il risultato dell'operazione di fusione debba essere qualunque l'albero del rebase finito sia Questo semplicemente ci consente di fare un'unione essenzialmente da upstream in v1 e forzare il risultato ad avere l'albero di v2. passato a git-show e produrrà l'adorabile formato combinato, senza dover fare affidamento sugli innesti, e quindi può essere automatizzato – Jake

2

git-cherry cerca i commit su un ramo che non sono su un altro. Devi usare come:

git cherry -v OLDTIP TIP UPSTREAM 

vale a dire cercare i commit che erano sul OLDTIP ma che non sono in UPSTREAM..TIP. Osserva la firma della patch per vedere se è inclusa una patch, quindi se una patch viene aggiunta, eliminata o modificata durante il rebase, verrà visualizzata nell'elenco. Le cose che sono state applicate senza modifiche non appariranno.

Un effetto simile può essere ottenuto tornando a OLDTIP e facendo git rebase -i TIP, poiché utilizza la stessa logica per popolare l'elenco di commit.

+0

Questo non sembra essere d'aiuto nell'esaminare i cambiamenti intra-file. cambiamenti di livello persi o danneggiati a causa di risoluzioni di conflitto errate, non di commit completi. –

+0

L'idea qui è di ridurre il numero di commit da confrontare (idealmente a zero). con un piccolo numero di commit grandi e in conflitto, ma potrebbe essere utile con un gran numero di piccoli commit. –

+0

Ah, direi che con questo standard ho un piccolo numero di grandi commit, perché la domanda successiva che mi viene in mente è "Una volta che ho i doveri da confrontare, come proponi che li paragono, a parte l'esecuzione del diff-diff con cui ho iniziato? " –

3

Quello che vogliamo veramente mostrare è il conflitto combinato diff che viene generato come se avessimo fatto una fusione per ottenere da (a) a (b) dove (a) era precedentemente basato su (upstream-old) e (b) è ora basato su (upstream-new).

Non vogliamo solo una differenza diretta.

Invece, possiamo essenzialmente fare l'unione ma forzare l'albero risultante ad essere $ b^{tree} che sappiamo già essere il punto di "fine" corretto di ciò che vogliamo.

Più o meno, supponiamo che abbiamo

newcommit -> the new version of the series 
oldcommit -> the old version of the series 
upstream -> the (new) version of the base of the series 

possiamo generare l'unione tramite

git commit-tree newcommit^{tree} -p oldcommit -p upstream -m "message" 

e poi mostrare il risultato con "git show" e questo genererà un diff combinato formato che mostra tutti i bit necessari come risoluzione dei conflitti che ignora automaticamente le modifiche che non erano in realtà parte della risoluzione dei conflitti.

funziona questo anche per semplice ammending un cambiamento così, e si può fare un po 'più di lavoro per garantire che l'unione generato commettere dispone autore precisa e impegnarsi timestamp in modo che sia coerente su più chiamate (dal momento che siamo memorizzazione un libero riferimento nel database degli oggetti).

Purtroppo non sono stato in grado di capire come ottenere semplicemente "git diff" per diff allo stesso modo senza generare effettivamente un albero ancora. Non sono sicuro di quali argomenti dovremmo passare per arrivare a quel punto.

+0

Un'idea intelligente! Dovrò vedere quanto sono utili i risultati la prossima volta che ho questo problema. –

+0

Ho appena avuto l'opportunità di provarlo. Ho trovato un avvertimento da notare: 'upstream' _eliminato_ un file che aveva delle modifiche (in modo da dividerlo in molti altri file), e la diff prodotta da questa tecnica non menziona affatto quel file, quindi non mostrerebbe la perdita di quei cambiamenti. –