Jaka jest różnica między `git merge` a` git merge --no-ff`?

Odpowiedzi:

1080

W --no-ffzapobiega flag git mergez wykonaniem „fast-forward”, jeśli wykryje, że prąd HEADjest przodkiem commit próbujesz scalić. Szybkie przewijanie do przodu występuje, gdy zamiast konstruować zatwierdzenie scalania, git po prostu przesuwa wskaźnik gałęzi, aby wskazywał na przychodzące zatwierdzenie. Zdarza się to często, gdy wykonuje się git pullbez żadnych lokalnych zmian.

Czasami jednak chcesz zapobiec temu zachowaniu, zwykle dlatego, że chcesz zachować określoną topologię gałęzi (np. Łączysz się w gałęzi tematu i chcesz mieć pewność, że tak wygląda podczas czytania historii). W tym celu można przekazać --no-ffflagi i git mergebędzie zawsze skonstruować seryjnej zamiast szybkiego przewijania do przodu.

Podobnie, jeśli chcesz wykonać a git pulllub użyć git merge, aby jawnie przewinąć do przodu i chcesz ratować się, jeśli nie można szybko przewinąć do przodu, możesz użyć --ff-onlyflagi. W ten sposób możesz regularnie robić coś takiego, git pull --ff-onlybez zastanowienia, a następnie, jeśli wystąpi błąd, możesz wrócić i zdecydować, czy chcesz scalić, czy też dokonać zmiany bazy.

Lily Ballard
źródło
87
Bardziej bezpośrednio odpowiedzieć na pytanie OP: oni nie zawsze są różne, ale jeśli są, to jasne, ze gitkalbo git log --graphże do przodu seryjnej nie stworzyć seryjnej commit, podczas gdy jeden non-fast-forward zrobił.
Cascabel
11
Byłoby miło rozwinąć powód unikania ff: autor wspomniał o „specyficznej topologii gałęzi” oznacza, że ​​w przypadku --no-ff dodatkowe zatwierdzenie scalania służy jako marker scalenia. Zalety to wyraźny znacznik scalający z nazwiskami autora i fuzji. Wady to nieliniowa historia, która wygląda jak zestaw zbiegających się torów kolejowych. Możliwym psychologicznym efektem ubocznym scalenia jest jednak to, że uczestnicy tracą zainteresowanie z powodu dłuższego procesu recenzji: blog.spreedly.com/2014/06/24/…
Vlad
6
Czy uczciwie byłoby powiedzieć, że --no-ffz funkcji rozwijania lub rozwijania do opanowania jest podobne do scalania żądania pull?
merlinpatt 15.04.16
9
@merlinpatt Sure. Jeśli scalisz żądanie ściągnięcia w GitHub, robi to odpowiednik --no-ff.
Lily Ballard
1
To dobre wytłumaczenie. Człowieku, po prostu nie ma wystarczających dobrych wyjaśnień git, aby ludzie mogli zrozumieć, dlaczego czysta historia git jest naprawdę, bardzo ważna (łącznie ze mną!)
dudewad
1037

Graficzna odpowiedź na to pytanie

Oto strona z jasnym wyjaśnieniem i graficzną ilustracją użycia git merge --no-ff:

różnica między git merge --no-ff i git merge

Dopóki tego nie zobaczyłem, byłem całkowicie zagubiony w git. Używanie --no-ffpozwala komuś przeglądając historię, aby wyraźnie zobaczyć gałąź, w której wypisałeś się do pracy. (ten link wskazuje narzędzie do wizualizacji „sieci” github) A oto kolejne świetne odniesienie z ilustracjami. Odniesienie to ładnie uzupełnia pierwsze z większym naciskiem na osoby mniej zaznajomione z git.


Podstawowe informacje o newbs takich jak ja

Jeśli jesteś podobny do mnie, a nie do Git-guru, moja odpowiedź tutaj opisuje obsługę usuwania plików ze śledzenia gita bez usuwania ich z lokalnego systemu plików, co wydaje się słabo udokumentowane, ale często występuje. Kolejną nowością jest uzyskanie bieżącego kodu , który wciąż mi się wymyka.


Przykładowy przepływ pracy

Zaktualizowałem pakiet na mojej stronie internetowej i musiałem wrócić do notatek, aby zobaczyć przepływ pracy; Pomyślałem, że warto dodać przykład do tej odpowiedzi.

Mój obieg poleceń 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

Poniżej: faktyczne użycie wraz z objaśnieniami.
Uwaga: wynik poniżej jest wycinany; git jest dość gadatliwy.

$ 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

Zauważ 3 rzeczy z góry:
1) W danych wyjściowych można zobaczyć zmiany wynikające z aktualizacji pakietu ECC, w tym dodanie nowych plików.
2) Zauważ też, że istnieją dwa pliki (nie w /eccfolderze), które usunąłem niezależnie od tej zmiany. Zamiast mylić usuwanie plików, utworzę później eccinną cleanupgałąź, aby odzwierciedlić usunięcie tych plików.
3) Nie śledziłem mojego przepływu pracy! Zapomniałem o git, kiedy próbowałem ponownie uruchomić ecc.

Poniżej: zamiast robić wszystko, git commit -am "updated ecc package"co zwykle robiłem, chciałem tylko dodać pliki w /eccfolderze. Te usunięte pliki nie były specjalnie częścią mojego git add, ale ponieważ były już śledzone w git, muszę je usunąć z zatwierdzenia tej gałęzi:

$ 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 [email protected]:me/mywebsite.git
   8a0d9ec..333eff5  master -> master



Skrypt do automatyzacji powyższego

Korzystając z tego procesu ponad 10 razy dziennie, zacząłem pisać skrypty wsadowe w celu wykonania poleceń, więc stworzyłem prawie właściwy git_update.sh <branch> <"commit message">skrypt do wykonania powyższych kroków. Oto źródło Gist dla tego skryptu.

Zamiast git commit -amwybierać pliki z listy „zmodyfikowanej” utworzonej za pomocą, git statusa następnie wklejać je w tym skrypcie. Stało się tak, ponieważ wprowadziłem dziesiątki zmian, ale chciałem różnych nazw gałęzi, aby pomóc w grupowaniu zmian.

Chris K.
źródło
11
Czy nadal możesz bezpiecznie usunąć oddział po scaleniu z tą --no-ffopcją?
Andy Fleming,
17
@DesignerGuy tak, możesz bezpiecznie usunąć stary oddział. Pomyśl o gałęziach jako o wskaźnikach do konkretnego zatwierdzenia.
Zyphrax,
2
Uważam, że ten tekst ze strony, do której prowadzi link, jest pomocny: bez --no-ff "z historii Gita nie można zobaczyć, który z obiektów zatwierdzających zaimplementował jakąś cechę - musiałbyś ręcznie odczytać wszystkie komunikaty dziennika."
Lorne Laliberte
jedno zdjęcie zawsze najlepsze niż tysiąc słów!
rupps
1
Grafika nie pokazuje gałęzi funkcji na drugim obrazie, dlaczego nie? Nadal istnieje, prawda?
ADJenks
269

Scal strategie

Jawne scalanie : Tworzy nowe zatwierdzenie scalania. (Oto, co dostaniesz, jeśli użyjesz --no-ff.)

wprowadź opis zdjęcia tutaj

Szybkie przewijanie do przodu: Przewijaj szybko, bez tworzenia nowego zatwierdzenia:

wprowadź opis zdjęcia tutaj

Rebase : Ustanów nowy poziom podstawowy:

wprowadź opis zdjęcia tutaj

Squash: Zmiażdż lub ściśnij (coś) siłą, aby stał się płaski:

wprowadź opis zdjęcia tutaj

Premraj
źródło
11
Ciekawa grafika, ale tak naprawdę nie pokazuje przypadku - no-ff, a zatem nie do końca odpowiada na pytanie tutaj
Vib
22
Pierwszy, „jawne scalanie”, zatytułowany tutaj jako „scalanie”, to scalanie nieff.
bkribbs,
3
ta grafika naprawdę mi bardzo pomogła
exexzian
2
nie odpowiada na każde pytanie, ale ta grafika jest niesamowita, UPVOTE!
SovietFrontier
3
Jaka jest zatem różnica między Rebase a połączeniem szybkiego przewijania?
ThanosFisherman
217

Ta --no-ffopcja zapewnia, że ​​scalanie do przodu nie nastąpi, i że zawsze zostanie utworzony nowy obiekt zatwierdzenia . Może to być pożądane, jeśli chcesz, aby git utrzymywał historię gałęzi funkcji.             git merge --no-ff vs git merge Na powyższym obrazku lewa strona jest przykładem historii git po użyciu, git merge --no-ffa prawa strona jest przykładem użycia, git mergegdzie możliwe było scalenie ff.

EDYCJA : Poprzednia wersja tego obrazu wskazywała tylko jednego rodzica dla zatwierdzenia scalania. Scalenia mają wiele zatwierdzeń nadrzędnych, których używa git do przechowywania historii „gałęzi funkcji” i oryginalnej gałęzi. Wiele linków nadrzędnych jest podświetlonych na zielono.

Daniel Smith
źródło
3
Co wskazuje zielona strzałka w porównaniu z czarną strzałką?
rtconner,
11
zielona strzałka wskazuje połączenie między zatwierdzeniem a rodzicem, w którym zatwierdzenie ma więcej niż jednego rodzica. Normalne zatwierdzenia (czarne) mają tylko jednego rodzica.
Daniel Smith
9
@DanielSmith Jak narysować ten elegancki wykres?
chancyWu
4
@chancyWu z Adobe Illustrator
Daniel Smith
Wydaje się, że w mojej firmie wszystkie żądania ściągania są scalone z opcją --no-ff. Czy w tych okolicznościach nadal warto dokonywać zmian bazy, zanim utworzę żądanie ściągnięcia?
Nickpick
37

To stare pytanie, które zostało nieco subtelnie wspomniane w innych postach, ale wyjaśnienie, które sprawiło, że kliknąłem to dla mnie, jest to, że nie szybkie połączenia będą wymagały osobnego zatwierdzenia .

Parris Varney
źródło
czy mógłbyś przejrzeć przepływ pracy w mojej odpowiedzi powyżej: czy to oznacza, że ​​potrzebuję dodatkowych zatwierdzeń poza tym, co mam teraz? Dzięki.
Chris K
3
@ChrisK git merge --no-ff eccpo prostu będziesz mieć dodatkowe zatwierdzenie korespondencji seryjnej dla wzorca git loggałęzi. Nie jest to technicznie potrzebne, jeśli master wskazuje bezpośredni przodek zatwierdzenia ecc, ale poprzez określenie opcji --no-ff wymusza się utworzenie zatwierdzenia scalania. Będzie miał tytuł:Merge branch 'ecc'
Ankur Agarwal
Świetne podsumowanie! :)
Eduard
4

Flaga --no-ff powoduje, że scalanie zawsze tworzy nowy obiekt zatwierdzenia, nawet jeśli scalanie można wykonać za pomocą szybkiego przewijania do przodu. Pozwala to uniknąć utraty informacji o historycznym istnieniu gałęzi obiektów i grupuje wszystkie zatwierdzenia, które razem dodały obiekt

jsina
źródło