В чем разница между `git merge` и` git merge --no-ff`?

977

Используя gitk log, я не смог заметить разницу между ними. Как я могу наблюдать разницу (с помощью команды git или какого-либо инструмента)?

user1162226
источник
3
Возможно дублирование Git fast forward VS без слияния fast forward .
Возможная
копия

Ответы:

1080

В --no-ffфлаг предотвращает git mergeот выполнения «голодание вперед» , если он обнаружит , что ваш текущий HEADявляется предком коммита вы пытаетесь объединить. Быстрая перемотка вперед - это когда вместо создания коммита слияния git просто перемещает указатель ветки, чтобы указать на входящий коммит. Это обычно происходит при выполнении git pullбез каких-либо локальных изменений.

Однако иногда вы хотите предотвратить такое поведение, как правило, потому что вы хотите поддерживать топологию конкретной ветви (например, вы объединяетесь в ветку темы и хотите, чтобы она выглядела так при чтении истории). Чтобы сделать это, вы можете передать --no-ffфлаг и всегдаgit merge создаст слияние вместо быстрой пересылки.

Точно так же, если вы хотите выполнить a git pullили использовать git mergeдля явной перемотки вперед и хотите выручить, если она не может перемотать вперед, тогда вы можете использовать --ff-onlyфлаг. Таким образом, вы можете регулярно делать что-то вроде, git pull --ff-onlyне думая, а затем, если это не так, вы можете вернуться и решить, хотите ли вы объединить или перебазировать.

Лили Баллард
источник
87
Для более прямого ответа на вопрос OP еще: они не всегда отличаются, но если они есть, это ясно из gitkили git log --graphчто быстро вперед слияние не создать слияния совершить, а не быстрой перемотки вперед один сделал.
Каскабель
11
Было бы неплохо остановиться на причине отказа от ff: автор упомянул «конкретную топологию ветвления», которая означает, что в случае --no-ff дополнительная фиксация слияния служит маркером слияния. В плюсе есть явный маркер слияния с именами автора и слияния. Недостатки нелинейной истории, которая выглядит как набор сходящихся железнодорожных путей. Возможный психологический побочный эффект от слияния заключается в том, что участники теряют интерес из-за более длительного процесса рецензирования: blog.spreedly.com/2014/06/24/…
Влад
6
Справедливо ли будет сказать, что --no-ffот возможности разработки или разработки до освоения похоже на объединение запроса на извлечение?
Мерлинпатт
9
@merlinpatt Конечно. Если вы объединяете запрос на удаление в GitHub, он делает эквивалент --no-ff.
Лили Баллард
1
Это хорошее объяснение. Чувак, просто не хватает хороших объяснений git для людей, чтобы понять, почему чистая история git действительно, действительно важна (включая меня!)
dudewad
1037

Графический ответ на этот вопрос

Вот сайт с понятным объяснением и графической иллюстрацией использования git merge --no-ff:

разница между git merge --no-ff и git merge

Пока я не увидел это, я был полностью потерян с мерзавцем. Использование --no-ffпозволяет кому-то, просматривая историю, ясно видеть ветку, с которой вы работали. (эта ссылка указывает на «сетевой» инструмент визуализации github). А вот еще одна отличная ссылка с иллюстрациями. Этот справочник хорошо дополняет первый, с большим акцентом на тех, кто менее знаком с git.


Основная информация для таких новичков, как я

Если вы похожи на меня, а не на Git-гуру, мой ответ здесь описывает обработку удаления файлов из отслеживания git без удаления их из локальной файловой системы, что кажется плохо документированным, но часто встречающимся. Еще одна новая ситуация - получение текущего кода , который все еще удается ускользнуть от меня.


Пример рабочего процесса

Я обновил пакет на своем сайте и должен был вернуться к своим заметкам, чтобы увидеть мой рабочий процесс; Я подумал, что полезно добавить пример к этому ответу.

Мой рабочий процесс команд 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

Ниже: фактическое использование, включая пояснения.
Примечание: вывод ниже обрезан; мерзавец довольно многословен.

$ 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

Обратите внимание на 3 вещи сверху:
1) В выходных данных вы видите изменения в обновлении пакета ECC, включая добавление новых файлов.
2) Также обратите внимание, что есть два файла (не в /eccпапке), которые я удалил независимо от этого изменения. Вместо того, чтобы путать удаления этих файлов с eccдругими, cleanupпозже я сделаю другую ветку, чтобы отразить удаление этих файлов.
3) Я не следил за своим рабочим процессом! Я забыл про мерзавца, когда пытался заставить ecc снова работать.

Ниже: вместо того, чтобы делать все включено, git commit -am "updated ecc package"как обычно, я хотел только добавить файлы в /eccпапку. Эти удаленные файлы не были частью моей git add, но поскольку они уже отслеживались в git, мне нужно удалить их из коммита этой ветки:

$ 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



Скрипт для автоматизации вышеперечисленного

Используя этот процесс более 10 раз в день, я приступил к написанию пакетных сценариев для выполнения команд, поэтому я создал почти правильный git_update.sh <branch> <"commit message">сценарий для выполнения вышеуказанных шагов. Вот источник Gist для этого сценария.

Вместо этого git commit -amя выбираю файлы из «модифицированного» списка, созданного с помощью, git statusа затем вставляю их в этот скрипт. Это произошло потому, что я сделал десятки правок, но хотел, чтобы различные имена ветвей помогли сгруппировать изменения.

Крис К
источник
11
Можете ли вы безопасно удалить ветку после слияния с --no-ffопцией?
Энди Флеминг,
17
@DesignerGuy да, вы можете безопасно удалить старую ветку. Думайте о ветвях как об указателях на конкретный коммит.
Zyphrax
2
Я нашел этот текст со связанной страницы полезным: без --no-ff «из истории Git невозможно увидеть, какие из объектов коммитов вместе реализовали функцию - вам придется вручную читать все сообщения журнала».
Лорн Лалиберте
одно изображение всегда лучше тысячи слов!
rupps
1
На рисунке не изображена ветвь функций на втором изображении, почему бы и нет? Это все еще существует, не так ли?
ADJenks
270

Стратегии слияния

Явное слияние : Создает новый коммит слияния. (Это то, что вы получите, если использовали --no-ff.)

введите описание изображения здесь

Fast Forward Merge: Быстрая перемотка вперед без создания нового коммита:

введите описание изображения здесь

Rebase : установить новый базовый уровень:

введите описание изображения здесь

Сквош: раздавить или сжать (что-то) с силой, чтобы он стал плоским:

введите описание изображения здесь

Premraj
источник
11
Интересная графика, но она на самом деле не показывает случай --no-ff и, следовательно, не совсем отвечает на вопрос здесь
Vib
22
Первый, "явное слияние", называемый здесь как "слияние", является слиянием без ff.
bkribbs
3
эта графика мне очень помогла
exexzian
2
не отвечает на вопрос, скажем так, но этот рисунок удивителен, UPVOTE!
Советская граница
3
Так в чем же разница между Rebase и ускоренным слиянием?
ThanosFisherman
217

В --no-ffопции гарантирует , что быстрая перемотка вперед слияние не будет происходить, и что нового коммита объект всегда будет создан . Это может быть желательно, если вы хотите, чтобы git поддерживал историю ветвей функций.             git merge --no-ff vs git merge На изображении выше, левая сторона - пример истории git после использования, git merge --no-ffа правая сторона - пример использования, git mergeгде возможно слияние ff.

РЕДАКТИРОВАТЬ : предыдущая версия этого изображения указала только один родительский элемент для фиксации слияния. Коммиты слияния имеют несколько родительских коммитов, которые git использует для сохранения истории «ветви функций» и исходной ветви. Несколько родительских ссылок выделены зеленым цветом.

Даниэль Смит
источник
3
Что означает зеленая стрелка против черной?
rtconner
11
зеленая стрелка указывает на связь между коммитом и родителем, где коммит имеет более одного родителя. Обычные коммиты (черные) имеют только одного родителя.
Даниэль Смит
9
@DanielSmith Как вы рисуете этот элегантный график?
chancyWu
4
@chancyWu с Adobe Illustrator
Даниэль Смит
Кажется, в моей компании все запросы на включение объединяются с параметром --no-ff. В этих условиях имеет ли смысл еще раз выполнить перебазирование, прежде чем я создаю запрос на извлечение?
Nickpick
37

Это старый вопрос, и он несколько тонко упоминается в других постах, но объяснение, которое сделало этот щелчок для меня, заключается в том, что для слияния без ускоренной перемотки потребуется отдельная фиксация .

Пэррис Варни
источник
не могли бы вы рассмотреть рабочий процесс в моем ответе выше: означает ли это, что мне нужны дополнительные коммиты сверх того, что я получил сейчас? Спасибо.
Крис К
3
@ChrisK у git merge --no-ff eccвас просто будет дополнительный коммит слияния в git logветке for. Это технически не требуется в том случае, если master указывает на прямого предка коммита ecc, но указав опцию --no-ff, вы принудительно создаете этот коммит слияния. Это будет иметь название:Merge branch 'ecc'
Анкур Агарвал
Отличное резюме! :)
Эдуард
4

Флаг --no-ff заставляет слияние всегда создавать новый объект фиксации, даже если слияние может быть выполнено с ускоренной перемоткой вперед. Это позволяет избежать потери информации об историческом существовании ветви компонента и объединяет все коммиты, которые вместе добавляли функцию

jsina
источник