Come posso visualizzare in anteprima un'unione in git?


397

Ho un ramo git (la linea principale, per esempio) e voglio unirmi in un altro ramo di sviluppo. O io?

Per decidere se voglio davvero unire questo ramo, vorrei vedere una sorta di anteprima di ciò che farà l'unione. Preferibilmente con la possibilità di vedere l'elenco dei commit che vengono applicati.

Finora, il meglio che posso inventare è merge --no-ff --no-commit, e quindi diff HEAD.


17
Vorrei solo git mergee git reset --keep HEAD@{1}se non mi piace il risultato.
Jan Hudec,

3
Nota che vedere l'elenco dei commit con il loro diff non racconta necessariamente l'intera storia - se l'unione non è banale, e specialmente se ci sono conflitti, il risultato effettivo della fusione potrebbe essere un po 'interessante.
Cascabel,

2
Il tuo metodo originale fa esattamente questo. Il punto del mio commento è che sebbene guardare le differenze individuali sia tutto a posto, se hai una fusione complessa, potresti ottenere risultati sorprendenti anche se tutti gli commit uniti sono indipendentemente buoni.
Cascabel,

2
@Jan: per alcuni motivi, ho git reset --keep HEAD@{1}restituito fatal: Cannot do a keep reset in the middle of a merge.aiuto?
Moey,

3
Perché non c'è --previewun'opzione in git-merge?
Gaui,

Risposte:


285

Ho scoperto che la soluzione che funziona meglio per me è semplicemente eseguire l'unione e interromperla in caso di conflitti . Questa sintassi particolare mi sembra pulita e semplice. Questa è la strategia 2 di seguito.

Tuttavia, se vuoi assicurarti di non confondere il tuo ramo attuale, o non sei pronto a unirti a prescindere dall'esistenza di conflitti, crea semplicemente un nuovo ramo secondario da esso e unisci che:

Strategia 1: il modo sicuro - unire un ramo temporaneo:

git checkout mybranch
git checkout -b mynew-temporary-branch
git merge some-other-branch

In questo modo puoi semplicemente buttare via il ramo temporaneo se vuoi solo vedere quali sono i conflitti. Non devi preoccuparti di "interrompere" l'unione e puoi tornare al tuo lavoro - semplicemente controlla di nuovo "mybranch" e non avrai alcun codice unito o unisci conflitti nel tuo ramo.

Questo è fondamentalmente una corsa a secco.

Strategia 2: quando vuoi assolutamente unirti, ma solo se non ci sono conflitti

git checkout mybranch
git merge some-other-branch

Se git segnala conflitti (e SOLO SE SONO PRESENTI conflitti) puoi quindi fare:

git merge --abort

Se l'unione ha esito positivo, non è possibile interromperla (reimpostare solo).

Se non sei pronto per unirti, usa il modo più sicuro sopra.

[EDIT: 2016-Nov - Ho scambiato la strategia 1 per 2, perché sembra che la maggior parte delle persone stia cercando "il modo sicuro". La strategia 2 ora è più di una nota che puoi semplicemente interrompere l'unione se l'unione ha conflitti che non sei pronto per affrontare. Tieni presente se leggi i commenti!]


2
+1 per la strategia 2. Ramo temporaneo che mostra esattamente cosa accadrà durante l'unione. La strategia 1 è ciò che sto cercando di evitare per questo caso particolare.
Gordolio,

2
Suggerisco di cambiare le strategie in modo che la prima sia la più sicura (il mio allenamento psichico sta arrivando - la maggior parte delle persone presumerebbe che l'opzione migliore sarebbe la prima, nonostante il chiaro uso della parola "più sicuro") ma, a parte questo, eccellente lavoro.
paxdiablo,

6
Si potrebbe fare un git merge --no-ff --no-commitcaso se non si desidera eseguire il commit delle modifiche direttamente. Ciò elimina la "necessità" di un ramo diverso e rende un po 'più semplice rivedere le modifiche, imo.
Gerard van Helden,

e se decidi di non voler unirti affatto e in effetti preferiresti scrivere ancora un po 'di codice e poi impegnarti nel tuo ramo in modo da poter distribuire SOLO il tuo ramo su un server di prova, non dovresti comunque ripristinare git merge --no-ff --no-commit? Immagino che tu possa ancora fare una cosa git merge --abortdopo se non ti piace quello che vedi? Anche se l'unione non produce conflitti?
Kasapo,

Alla maggior parte delle persone piace copiare e incollare e non pensare troppo. Pertanto, per la strategia 1, forse aggiungere anche come interrompere l'unione nel ramo locale temporaneo, git reset --hard HEADquindi effettuare il checkout di un ramo diverso git checkout <different branch name>ed eliminare il ramo temporaneo git delete -b <name of temporary branch>.
user3613932

402
  • git log ..otherbranch
    • elenco di modifiche che verranno unite nel ramo corrente.
  • git diff ...otherbranch
    • diff da antenato comune (base di unione) alla testa di ciò che verrà unito. Nota i tre punti , che hanno un significato speciale rispetto ai due punti (vedi sotto).
  • gitk ...otherbranch
    • rappresentazione grafica dei rami da quando sono stati fusi l'ultima volta.

La stringa vuota implica HEAD, quindi ecco perché ..otherbranchinvece di HEAD..otherbranch.

I due punti rispetto ai tre hanno un significato leggermente diverso per diff rispetto ai comandi che elencano le revisioni (log, gitk ecc.). Per log e altri due punti ( a..b) significano tutto ciò che è dentro bma non ae tre punti ( a...b) indicano tutto ciò che è solo in uno ao b. Ma diff funziona con due revisioni e lì il caso più semplice rappresentato da due punti ( a..b) è una differenza semplice da aa be tre punti ( a...b) differenza media tra antenato comune e b( git diff $(git merge-base a b)..b).


7
Il terzo punto era la parte che mi mancava, grazie! L'approccio log funziona bene anche, log -p --reverse ..otherbranch sembra un buon modo per vedere cosa sarebbe unito.
Glenjamin

1
Mi mancava git checkout masterprima di provare questo. Mi chiedevo perché dicesse che tutti i miei cambiamenti sarebbero stati
sovrascritti

5
git show ..otherbranchmostrerà un elenco di modifiche e differenze che verranno unite nel ramo corrente.
Gaui,

4
Questo non è completamente accurato, specialmente per le ciliegie.
void.pointer

2
@ void.pointer, git non tratta le selezioni di ciliegie specialmente in un'unione normale, quindi neanche qui. Si potrebbe scrivere una strategia di unione che, per quanto ne so, non lo è mai stata.
Jan Hudec,

19

Se sei come me, stai cercando l'equivalente di svn update -n. Quanto segue sembra fare il trucco. Nota che assicurati di fare un git fetchprimo in modo che il tuo repository locale abbia gli aggiornamenti appropriati con cui confrontare.

$ git fetch origin
$ git diff --name-status origin/master
D       TableAudit/Step0_DeleteOldFiles.sh
D       TableAudit/Step1_PopulateRawTableList.sh
A       manbuild/staff_companies.sql
M       update-all-slave-dbs.sh

o se vuoi un diff dalla tua testa al telecomando:

$ git fetch origin
$ git diff origin/master

IMO questa soluzione è molto più semplice e meno soggetta a errori (e quindi molto meno rischiosa) rispetto alla soluzione migliore che propone "unisci e poi interrompi".


1
dovrebbe essere $ git checkout target-branche quindi $ git diff --name-status ...branch-to-be-merged(tre punti sono essenziali, quindi digitarli così come sono)
Lu55

Manca una cosa: non mostra quali file potrebbero avere un conflitto: hanno lo stesso marcatore "M" dei file che sono stati modificati sul ramo ma possono essere uniti senza conflitti.
Peterflynn,

Forse le cose erano diverse nel 2012, ma in questi giorni l'interruzione di una fusione sembra abbastanza affidabile, quindi non credo sia giusto caratterizzarlo come "molto più facile e meno soggetto a errori" in questo momento.
David Z,

8

La maggior parte delle risposte qui richiede una directory di lavoro pulita e più passaggi interattivi (non valido per gli script) o non funziona per tutti i casi, ad esempio fusioni passate che portano già alcune delle modifiche in sospeso nel ramo di destinazione o ciliegie che eseguono il stesso.

Per vedere veramente cosa cambierebbe nel masterramo se ti fossi unito developin esso, proprio ora:

git merge-tree $(git merge-base master develop) master develop

Poiché è un comando idraulico, non indovina cosa intendi, devi essere esplicito. Inoltre non colora l'output né usa il tuo cercapersone, quindi il comando completo sarebbe:

git merge-tree $(git merge-base master develop) master develop | colordiff | less -R

- https://git.seveas.net/previewing-a-merge-result.html

(grazie a David Normington per il link)

PS:

Se si ottengono conflitti di unione, questi verranno visualizzati con i soliti indicatori di conflitto nell'output, ad esempio:

$ git merge-tree $(git merge-base a b ) a b 
added in both
  our    100644 78981922613b2afb6025042ff6bd878ac1994e85 a
  their  100644 61780798228d17af2d34fce4cfbdf35556832472 a
@@ -1 +1,5 @@
+<<<<<<< .our
 a
+=======
+b
+>>>>>>> .their

L'utente @dreftymac ha un buon punto: questo lo rende inadatto per gli script, perché non è possibile individuarlo facilmente dal codice di stato. I marker di conflitto possono essere abbastanza diversi a seconda delle circostanze (cancellati vs modificati, ecc.), Il che rende difficile anche il grep. Attenzione.


1
@hraban Sembra la risposta corretta, tuttavia a quanto pare manca ancora un elemento. Questo approccio richiede all'utente di "controllare" l'output per vedere se sono presenti gli indicatori di conflitto. Hai un approccio che semplicemente ritorna truese ci sono conflitti e falsese non ci sono conflitti (ad esempio, un valore booleano che non richiede il test "bulbo oculare" o qualsiasi intervento umano).
Dreftymac,

1
@dreftymac non riesce a trovare nulla in tal senso. potresti usare qualcosa del genere git merge-tree ... | grep -q '^[a-z].*in both$' && echo conflict || echo safe to mergema è finnicky; Probabilmente sto dimenticando un caso. forse vuoi controllare gli indicatori di conflitto, invece? ad esempio, questo probabilmente non rileva conflitti di stile "cancellati, cambiati". (Ho appena controllato e questo non mostra nemmeno i marker di conflitto, quindi hai bisogno di una regex meticolosa per essere al sicuro qui)
hraban

1
Utilizzare less -Rper gestire l'output colorato senza modificare la configurazione.
Giacomo Alzetta,

Grazie @GiacomoAlzetta, l'ho aggiornato.
hraban,

6

Se hai già recuperato le modifiche, il mio preferito è:

git log ...@{u}

Ciò ha bisogno di git 1.7.x credo però. La @{u}notazione è una "scorciatoia" per il ramo a monte, quindi è un po 'più versatile di git log ...origin/master.

Nota: se usi zsh e l'estensione estesa del glog, probabilmente dovrai fare qualcosa del tipo:

git log ...@\{u\}

6

Aggiungendo alle risposte esistenti, è possibile creare un alias per mostrare il diff e / o il registro prima di un'unione. Molte risposte omettono il fetchda fare prima di "visualizzare in anteprima" l'unione; questo è un alias che combina questi due passaggi in uno (emulando qualcosa di simile a quello di mercurial hg incoming/ outgoing)

Quindi, basandosi su " git log ..otherbranch", puoi aggiungere quanto segue a ~/.gitconfig:

...
[alias]
    # fetch and show what would be merged (use option "-p" to see patch)
    incoming = "!git remote update -p; git log ..@{u}"

Per simmetria, il seguente alias può essere utilizzato per mostrare ciò che è stato impegnato e verrebbe spinto, prima di spingere:

    # what would be pushed (currently committed)
    outgoing = log @{u}..

E quindi puoi eseguire " git incoming" per mostrare molte modifiche, o " git incoming -p" per mostrare la patch (cioè, "diff"), " git incoming --pretty=oneline", per un sommario sintetico, ecc. Puoi quindi (facoltativamente) eseguire " git pull" su fondersi davvero. (Tuttavia, poiché hai già recuperato, l'unione potrebbe essere fatta direttamente.)

Allo stesso modo, " git outgoing" mostra cosa verrebbe spinto se dovessi correre " git push".


3

git log currentbranch..otherbranchti darà l'elenco dei commit che andranno nel ramo corrente se fai una fusione. I soliti argomenti da registrare che forniscono dettagli sugli commit ti daranno maggiori informazioni.

git diff currentbranch otherbranchti darà la differenza tra i due commit che diventeranno uno. Questo sarà un diff che ti darà tutto ciò che verrà unito.

Questi aiuterebbero?


2
In realtà, sbagliato. git log otherbranch..currentbranchfornisce un elenco di commit su currentbranch . Il git diff otherbranch currentbranchti dà diff dalla versione to-be-risultante dalla concentrazione di punta attuale, che è quanto di più inutile come può essere, perché ciò che si vuole è diff dalla base fusione alla testa unione.
Jan Hudec,

Grazie. Ho cambiato i nomi degli alberi.
Noufal Ibrahim,

3

Richiesta pull: ho usato la maggior parte delle idee già inviate, ma una che utilizzo spesso è (soprattutto se proviene da un altro sviluppatore) fare una richiesta pull che offre un modo pratico per rivedere tutte le modifiche in una fusione prima che posto. So che GitHub non è git ma sicuramente è utile.


2

A meno che non si esegua effettivamente l'unione a un tiro di schioppo (vedi la risposta di Kasapo), non sembra esserci un modo affidabile per vederlo.

Detto questo, ecco un metodo che si avvicina leggermente:

git log TARGET_BRANCH...SOURCE_BRANCH --cherry

Ciò fornisce un'equa indicazione di quali impegni faranno parte della fusione. Per vedere le differenze, aggiungi -p. Per visualizzare i nomi dei file, aggiungere qualsiasi --raw, --stat, --name-only, --name-status.

Il problema con l' git diff TARGET_BRANCH...SOURCE_BRANCHapproccio (vedi la risposta di Jan Hudec) è che vedrai differenze per le modifiche già nel tuo ramo di destinazione se il tuo ramo di origine contiene unioni incrociate.


1

Forse questo può aiutarti? git-diff-tree - Confronta il contenuto e la modalità dei BLOB trovati tramite due oggetti ad albero


1

Non voglio usare il comando git merge come precursore per la revisione di file in conflitto. Non voglio fare un'unione, voglio identificare potenziali problemi prima di unirmi - problemi che l'auto-fusione potrebbe nascondermi. La soluzione che stavo cercando è come fare in modo che git sputi un elenco di file che sono stati cambiati in entrambi i rami che verranno uniti in futuro, rispetto ad alcuni antenati comuni. Una volta che ho quell'elenco, posso usare altri strumenti di confronto dei file per esplorare ulteriormente le cose. Ho cercato più volte e non ho ancora trovato quello che voglio in un comando git nativo.

Ecco la mia soluzione alternativa, nel caso in cui aiuti chiunque altro là fuori:

In questo scenario ho un ramo chiamato QA che presenta molti cambiamenti dall'ultima versione di produzione. La nostra ultima versione di produzione è taggata con "15.20.1". Ho un altro ramo di sviluppo chiamato new_stuff che voglio unire nel ramo QA. Sia QA che new_stuff indicano che "seguono" (come riportato da Gitk) il tag 15.20.1.

git checkout QA
git pull
git diff 15.20.1 --name-only > QA_files
git checkout new_stuff
git pull
git diff 15.20.1 --name-only > new_stuff_files
comm -12 QA_files new_stuff_files

Ecco alcune discussioni che hanno colpito il motivo per cui sono interessato a scegliere come target questi file specifici:

Come posso fidarmi di Git Merge?

/software/199780/how-far-do-you-trust-automerge

Utilizzando il nostro sito, riconosci di aver letto e compreso le nostre Informativa sui cookie e Informativa sulla privacy.
Licensed under cc by-sa 3.0 with attribution required.