Qual è la differenza tra `git merge` e` git merge --no-ff`?


977

Usando gitk log, non sono riuscito a individuare una differenza tra i due. Come posso osservare la differenza (con un comando git o qualche strumento)?




Risposte:


1080

Il --no-ffflag impedisce git mergedi eseguire un "avanzamento rapido" se rileva che la tua corrente HEADè un antenato del commit che stai cercando di unire. Un avanzamento rapido è quando, invece di costruire un commit di unione, git sposta semplicemente il puntatore del ramo per puntare al commit in entrata. Ciò si verifica comunemente quando si esegue una git pullsenza modifiche locali.

Tuttavia, occasionalmente si desidera impedire che questo comportamento si verifichi, in genere perché si desidera mantenere una topologia di ramo specifica (ad esempio, si sta unendo un ramo di argomento e si desidera assicurarsi che appaia così quando si legge la cronologia). Per fare ciò, puoi passare la --no-ffbandiera e lo git mergefarai sempre costruire una fusione al posto di fast-forwarding.

Allo stesso modo, se si desidera eseguire un git pullo utilizzare git mergeper avanzare esplicitamente in avanti e si desidera salvare se non è possibile avanzare rapidamente, è possibile utilizzare il --ff-onlyflag. In questo modo puoi fare regolarmente qualcosa di simile git pull --ff-onlysenza pensare, e quindi se si rompe, puoi tornare indietro e decidere se vuoi unire o riformulare.


87
Per rispondere più direttamente alla domanda del PO: non sono sempre diversi, ma se lo sono, è chiaro da gitko git log --graphche l'unione di avanzamento rapido non ha creato un commit di unione, mentre quello di non avanzamento rapido l'ha fatto.
Cascabel,

11
Sarebbe bello ampliare il motivo per evitare ff: l'autore ha menzionato "topologia di ramo specifica" significa che in caso --no-ff un commit di unione extra funge da marker di unione. I professionisti sono marcatori di unione espliciti con nomi dell'autore e della fusione. Il contro è una storia non lineare che sembra un insieme di binari ferroviari convergenti. Un possibile effetto psicologico della fusione è che i partecipanti perdono interesse a causa di un processo di revisione più lungo: blog.spreedly.com/2014/06/24/…
Vlad

6
Sarebbe giusto dire che --no-ffda una funzionalità da sviluppare o sviluppare a master è simile a unire una richiesta pull?
merlinpatt,

9
@merlinpatt Certo. Se unisci una richiesta pull in GitHub, equivale a --no-ff.
Lily Ballard,

1
Questa è una buona spiegazione Amico, non ci sono abbastanza buone spiegazioni git là fuori per le persone a capire perché la storia del git pulito è davvero molto importante (me incluso!)
Dudewad,

1037

Risposta grafica a questa domanda

Ecco un sito con una chiara spiegazione e un'illustrazione grafica dell'uso git merge --no-ff:

differenza tra git merge --no-ff e git merge

Fino a quando ho visto questo, ero completamente perso con Git. L'utilizzo --no-ffconsente a qualcuno che sta esaminando la cronologia di vedere chiaramente la filiale su cui hai eseguito il checkout per lavorare. (quel link punta allo strumento di visualizzazione "network" di github) Ed ecco un altro grande riferimento con le illustrazioni. Questo riferimento integra perfettamente il primo con una maggiore attenzione a coloro che non hanno familiarità con Git.


Informazioni di base per i principianti come me

Se sei come me, e non un Git-guru, la mia risposta qui descrive la gestione dell'eliminazione dei file dal tracciamento di git senza eliminarli dal filesystem locale, che sembra scarsamente documentato ma spesso si verifica. Un'altra nuova situazione sta ottenendo il codice attuale , che riesce ancora a sfuggirmi.


Esempio di flusso di lavoro

Ho aggiornato un pacchetto sul mio sito Web e ho dovuto tornare alle mie note per vedere il mio flusso di lavoro; Ho ritenuto utile aggiungere un esempio a questa risposta.

Il mio flusso di lavoro di comandi git:

git checkout -b contact-form
(do your work on "contact-form")
git status
git commit -am  "updated form in contact module"
git checkout master
git merge --no-ff contact-form
git branch -d contact-form
git push origin master

Sotto: utilizzo effettivo, comprese le spiegazioni.
Nota: l'output seguente è frammentato; git è abbastanza dettagliato.

$ git status
# On branch master
# Changed but not updated:
#   (use "git add/rm <file>..." to update what will be committed)
#   (use "git checkout -- <file>..." to discard changes in working directory)
#
#       modified:   ecc/Desktop.php
#       modified:   ecc/Mobile.php
#       deleted:    ecc/ecc-config.php
#       modified:   ecc/readme.txt
#       modified:   ecc/test.php
#       deleted:    passthru-adapter.igs
#       deleted:    shop/mickey/index.php
#
# Untracked files:
#   (use "git add <file>..." to include in what will be committed)
#
#       ecc/upgrade.php
#       ecc/webgility-config.php
#       ecc/webgility-config.php.bak
#       ecc/webgility-magento.php

Nota 3 cose sopra:
1) Nell'output puoi vedere le modifiche dall'aggiornamento del pacchetto ECC, inclusa l'aggiunta di nuovi file.
2) Notare anche che ci sono due file (non nella /ecccartella) che ho eliminato indipendentemente da questa modifica. Invece di confondere quelle eliminazioni di file ecc, in seguito creerò un cleanupramo diverso per riflettere l'eliminazione di quei file.
3) Non ho seguito il mio flusso di lavoro! Mi sono dimenticato di Git mentre stavo cercando di far funzionare di nuovo ecc.

Sotto: piuttosto che fare tutto compreso git commit -am "updated ecc package"normalmente, volevo solo aggiungere i file nella /ecccartella. Quei file eliminati non facevano parte del mio git add, ma poiché erano già stati tracciati in git, ho bisogno di rimuoverli dal commit di questo ramo:

$ git checkout -b ecc
$ git add ecc/*
$ git reset HEAD passthru-adapter.igs
$ git reset HEAD shop/mickey/index.php
Unstaged changes after reset:
M       passthru-adapter.igs
M       shop/mickey/index.php

$ git commit -m "Webgility ecc desktop connector files; integrates with Quickbooks"

$ git checkout master
D       passthru-adapter.igs
D       shop/mickey/index.php
Switched to branch 'master'
$ git merge --no-ff ecc
$ git branch -d ecc
Deleted branch ecc (was 98269a2).
$ git push origin master
Counting objects: 22, done.
Delta compression using up to 4 threads.
Compressing objects: 100% (14/14), done.
Writing objects: 100% (14/14), 59.00 KiB, done.
Total 14 (delta 10), reused 0 (delta 0)
To git@github.com:me/mywebsite.git
   8a0d9ec..333eff5  master -> master



Script per automatizzare quanto sopra

Avendo utilizzato questo processo più di 10 volte in un giorno, ho iniziato a scrivere script batch per eseguire i comandi, quindi ho creato uno git_update.sh <branch> <"commit message">script quasi corretto per eseguire i passaggi precedenti. Ecco la fonte Gist per quella sceneggiatura.

Invece di git commit -amselezionare i file dall'elenco "modificato" prodotto tramite git statuse quindi incollare quelli in questo script. Ciò è avvenuto perché ho apportato dozzine di modifiche, ma volevo vari nomi di rami per aiutare a raggruppare le modifiche.


11
Puoi ancora eliminare in modo sicuro un ramo dopo aver eseguito l'unione con l' --no-ffopzione?
Andy Fleming,

17
@DesignerGuy sì, puoi eliminare in sicurezza il vecchio ramo. Pensa ai rami come semplici puntatori a un commit specifico.
Zyphrax,

2
Ho trovato utile questo testo dalla pagina collegata: senza --no-ff "è impossibile vedere dalla cronologia di Git quali degli oggetti di commit hanno implementato una funzione insieme - dovresti leggere manualmente tutti i messaggi di registro."
Lorne Laliberte,

un'immagine sempre migliore di mille parole!
Rupps,

1
L'immagine non mostra il ramo delle caratteristiche nella seconda immagine, perché no? Esiste ancora vero?
ADJenks

269

Unisci strategie

Unione esplicita : crea un nuovo commit di unione. (Questo è ciò che otterrai se lo hai usato --no-ff.)

inserisci qui la descrizione dell'immagine

Unione rapida in avanti: avanti rapidamente, senza creare un nuovo commit:

inserisci qui la descrizione dell'immagine

Rebase : stabilire un nuovo livello base:

inserisci qui la descrizione dell'immagine

Squash: schiaccia o schiaccia (qualcosa) con forza in modo che diventi piatto:

inserisci qui la descrizione dell'immagine


11
Grafica interessante ma in realtà non mostra il caso --no-ff e quindi non risponde abbastanza alla domanda qui
Vib,

22
Il primo, "unione esplicita", qui intitolato "unione", è un'unione non ff.
bkribbs,

3
quella grafica mi ha davvero aiutato molto
exexzian il

2
non risponde alla domanda per dire, ma questo grafico è sorprendente, UPVOTE!
SovietFrontier,

3
Allora, qual è la differenza tra Rebase e l'unione di avanzamento rapido allora?
ThanosFisherman,

217

L' --no-ffopzione garantisce che non si verifichi una fusione in avanti veloce e che venga sempre creato un nuovo oggetto commit . Questo può essere desiderabile se vuoi che git mantenga una cronologia dei rami delle caratteristiche.             git merge --no-ff vs git merge Nell'immagine sopra, il lato sinistro è un esempio della cronologia git dopo l'uso git merge --no-ffe il lato destro è un esempio dell'uso in git mergecui era possibile una fusione ff.

EDIT : una versione precedente di questa immagine indicava un solo genitore per il commit unione. I commit di merge hanno più commit parent che git usa per mantenere una cronologia del "ramo delle caratteristiche" e del ramo originale. I collegamenti multipli padre sono evidenziati in verde.


3
Cosa indica la freccia verde rispetto alla freccia nera?
domenica

11
la freccia verde indica un collegamento tra un commit e un genitore in cui il commit ha più di un genitore. I commit normali (neri) hanno un solo genitore.
Daniel Smith,

9
@DanielSmith Come si disegna questo elegante grafico?
Chancy,

4
@chancyWu con Adobe Illustrator
Daniel Smith il

Sembra che nella mia azienda tutte le richieste pull vengano unite con l'opzione --no-ff. In tali circostanze, ha ancora senso rifare prima di creare una richiesta pull?
Nickpick,

37

Questa è una vecchia domanda, e questo è in qualche modo sottilmente menzionato negli altri post, ma la spiegazione che ha fatto questo clic per me è che le fusioni non veloci richiederanno un commit separato .


potresti per favore rivedere il flusso di lavoro nella mia risposta sopra: questo significa che ho bisogno di ulteriori commit oltre a quello che ho ora? Grazie.
Chris K,

3
@ChrisK git merge --no-ff eccavrai semplicemente un ulteriore merge commit nel git logfor branch master. Ciò non è tecnicamente necessario nel caso in cui master stia indicando un antenato diretto del commit ecc è attivo ma specificando l'opzione --no-ff si sta forzando la creazione di quel commit di merge. Avrà il titolo:Merge branch 'ecc'
Ankur Agarwal

Ottimo riassunto! :)
Eduard,

4

Il flag --no-ff fa sì che l'unione crei sempre un nuovo oggetto commit, anche se l'unione potrebbe essere eseguita con un avanzamento veloce. Ciò evita di perdere informazioni sull'esistenza storica di un ramo di funzionalità e raggruppa tutti i commit che insieme hanno aggiunto la funzionalità

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.