Почему git выполняет ускоренное слияние по умолчанию?

641

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

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

Так что думать о будущем: я единственный, кто работает над этим проектом. Если я использую подход по умолчанию в git (ускоренное слияние), моя история приведет к созданию одной гигантской основной ветви. Никто не знает, что я использовал отдельную ветку для каждой функции, потому что в итоге у меня будет только эта гигантская основная ветка. Разве это не выглядит непрофессионально?

По этой причине я не хочу быстрого слияния и не могу понять, почему это по умолчанию. А что тут хорошего?

Флориан Пилз
источник
38
Примечание: см. Также sandofsky.com/blog/git-workflow.html и избегайте ' no-ff' с его " фиксацией контрольной точки", которая нарушает пополам или обвинение.
VonC
15
Вы сожалеете об использовании git в проекте с одним человеком?
HaveAGuess
27
Точно нет! В моей рабочей папке у меня есть 7 индивидуальных проектов, где я использую git. Позвольте мне перефразировать это: я начал много проектов, так как я задал этот вопрос, и все они версионированы через git. Насколько я знаю, только git и mercurial поддерживают локальное управление версиями, что очень важно для меня, так как я к этому привык. Это легко настроить, и у вас всегда есть вся история с вами. В групповых проектах это даже лучше, так как вы можете выполнять коммиты, не мешая никому работать с кодом. Кроме того, я использую github, чтобы поделиться некоторыми своими проектами (например, micro-optparse), где git является требованием.
Флориан Пилз
7
@Cawas true, -no-ffредко является хорошей идеей, но все же может помочь сохранить внутреннюю историю объекта при записи только одного коммита в основной ветке. Это имеет смысл для длинной истории объектов, когда вы время от времени сливаете ее прогрессию в основную ветку.
VonC
12
Кстати, на ваш вопрос «Разве это [история линейной ветви] не выглядит непрофессионально?». Нет ничего непрофессионального в использовании системы с исходным кодом с ее настройками по умолчанию. Это не профессионализм. Это касается определения того, на какую философию ветвления вы подписываетесь. Например, @VonC ссылается на статью Сандофски, в которой он выступает за использование быстрой перемотки в качестве превосходного подхода. Не правильно или неправильно, просто разные философии для разных контекстов.
Marplesoft

Ответы:

718

Быстрое слияние имеет смысл для кратковременных ветвей, но в более сложной истории слияние без быстрой перемотки может облегчить понимание истории и упростить возврат группы коммитов.

Предупреждение : не ускоренная пересылка также имеет потенциальные побочные эффекты. Пожалуйста, просмотрите https://sandofsky.com/blog/git-workflow.html , избегайте «no-ff» с его «фиксацией контрольной точки», которые нарушают пополам или обвиняют, и тщательно продумайте, должен ли это быть подход по умолчанию master.

альтернативный текст
(От nvie.com , Винсент Дриссен , пост " Успешная модель ветвления Git ")

Включение готовой функции по разработке

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

$ git checkout develop
Switched to branch 'develop'
$ git merge --no-ff myfeature
Updating ea1b82a..05e9557
(Summary of changes)
$ git branch -d myfeature
Deleted branch myfeature (was 05e9557).
$ git push origin develop

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

Якуб Narębski также упоминает о конфигурацииmerge.ff :

По умолчанию Git не создает дополнительный коммит слияния при слиянии коммита, который является потомком текущего коммита. Вместо этого верхушка текущей ветви быстро пересылается.
Когда falseэта переменная установлена ​​в , эта переменная указывает Git создать дополнительный коммит слияния в таком случае (эквивалентно предоставлению --no-ffопции из командной строки).
Если установлено значение ' only', разрешено только такое ускоренное слияние (эквивалентно предоставлению --ff-onlyопции из командной строки).


Быстрая перемотка вперед по умолчанию, потому что:

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

Но если вы ожидаете итеративный рабочий процесс в одной теме / ветви функций (т. Е. Я объединяю, затем я возвращаюсь к этой ветви функций и добавляю еще несколько коммитов), тогда полезно включать только объединение в основную ветку, а не все промежуточные коммиты ветви функций.

В этом случае вы можете в конечном итоге установить этот тип файла конфигурации :

[branch "master"]
# This is the list of cmdline options that should be added to git-merge 
# when I merge commits into the master branch.

# The option --no-commit instructs git not to commit the merge
# by default. This allows me to do some final adjustment to the commit log
# message before it gets commited. I often use this to add extra info to
# the merge message or rewrite my local branch names in the commit message
# to branch names that are more understandable to the casual reader of the git log.

# Option --no-ff instructs git to always record a merge commit, even if
# the branch being merged into can be fast-forwarded. This is often the
# case when you create a short-lived topic branch which tracks master, do
# some changes on the topic branch and then merge the changes into the
# master which remained unchanged while you were doing your work on the
# topic branch. In this case the master branch can be fast-forwarded (that
# is the tip of the master branch can be updated to point to the tip of
# the topic branch) and this is what git does by default. With --no-ff
# option set, git creates a real merge commit which records the fact that
# another branch was merged. I find this easier to understand and read in
# the log.

mergeoptions = --no-commit --no-ff

ОП добавляет в комментариях:

Я вижу некоторый смысл в перемотке вперед для [недолговечных] ветвей, но если сделать это действием по умолчанию, это означает, что git предполагает, что вы ... часто имеете [недолговечные] ветви. Разумно?

Джефроми отвечает:

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

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

В целом, я добавляю:

это действительно зависит от вашего рабочего процесса разработки :

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

Смотрите " Когда вы должны перейти? "

На самом деле, когда вы рассматриваете модель веток Mercurial, в ее основе лежит одна ветвь на репозиторий (даже если вы можете создавать анонимные заголовки, закладки и даже именованные ветки ).
См. «Git and Mercurial - Сравнение и контраст» .

Mercurial, по умолчанию, использует анонимные легкие кодовые строки, которые в его терминологии называются «заголовками».
Git использует легкие именованные ветви с инъективным отображением для отображения имен ветвей в удаленном репозитории на имена ветвей удаленного отслеживания.
Git «вынуждает» вас называть ветви (ну, за исключением одной неназванной ветви, которая называется « отсоединенный HEAD »), но я думаю, что это лучше работает с тяжелыми ветвями, такими как тема веток, то есть несколько ветвей в одной парадигме хранилища.

VonC
источник
Вау, это было быстро. ;) Я знаю о параметре --no-ff, но о быстрой перемотке узнаю только после того, как испортил свою функцию. Я вижу некоторый смысл в перемотке вперед для короткоживущих ветвей, но если сделать это действием по умолчанию, это означает, что git предполагает, что у вас чаще всего есть такие короткоживущие ветви. Разумно?
Флориан Пилз
@Florian: я считаю, что это разумный взгляд на процесс. Я добавил пример конфигурации, которая настроит способ управления слиянием с мастером.
VonC
Спасибо, файл конфигурации должен помочь избежать таких ошибок. :) Применяйте эту конфигурацию только локально с помощью "git config branch.master.mergeoptions '--no-ff'"
Флориан Пилз
2
@BehrangSaeedzadeh: перебазирование - это еще одна тема (которая не была упомянута на этой странице): если вы не выдвинули свою featureветку в публичное репо, вы можете перебазировать ее masterстолько раз, сколько захотите. См. Stackoverflow.com/questions/5250817/…
VonC
4
@BehrangSaeedzadeh: перебазирование само по себе не сделает историю линейной. Именно то, как вы интегрируете изменения своей featureветви обратно master, делает указанную историю линейной или нет. Простое быстрое слияние сделает его линейным. Это имеет смысл, если вы очистили историю этой featureветви перед слиянием в ускоренном режиме, оставив только значительные коммиты, как упомянуто в stackoverflow.com/questions/7425541/… .
VonC
42

Позвольте мне немного подробнее остановиться на очень всеобъемлющем ответе VonC :


Во-первых, если я правильно помню, тот факт, что Git по умолчанию не создает коммитов слияния в случае ускоренной перемотки вперед , возник из-за рассмотрения «одинаковых репозиториев» с одной ветвью, где взаимное извлечение используется для синхронизации этих двух репозиториев ( Рабочий процесс вы можете найти в качестве первого примера в документации большинства пользователей, включая «Руководство пользователя Git» и «Контроль версий на примере»). В этом случае вы не используете pull для объединения полностью реализованной ветви, вы используете ее, чтобы не отставать от другой работы. Вы не хотите иметь эфемерный и неважный факт, когда вы делаете синхронизацию, сохраненную и сохраненную в репозитории, сохраненную для будущего.

Обратите внимание, что полезность ветвей функций и наличия нескольких веток в одном репозитории появилась только позже, с более широким использованием VCS с хорошей поддержкой слияния и с попыткой различных рабочих процессов на основе слияния. Вот почему, например, Mercurial изначально поддерживал только одну ветвь для каждого хранилища (плюс анонимные советы для отслеживания удаленных веток), как это было видно в более старых версиях «Mercurial: Полное руководство».


Во-вторых, следуя рекомендациям по использованию ветвей функций , а именно, что все ветви функций должны начинаться со стабильной версии (обычно с последнего выпуска), чтобы можно было выбирать и какие функции включать, выбирая, какие ветви функций следует объединить, вы обычно не находятся в ситуации ускоренной перемотки ... что делает этот вопрос спорным. Вам нужно беспокоиться о создании истинного слияния, а не о перемотке вперед при слиянии самой первой ветви (при условии, что вы не вносите изменения с одним коммитом непосредственно в 'master'); все другие последующие слияния, конечно, находятся в ситуации, не связанной с перемоткой вперед.

НТН

Якуб Наребски
источник
Относительно ветвей функций из стабильных выпусков: что, если функция, которую я разрабатываю для следующего выпуска, зависит от изменений другой функции, которую я уже разработал и слил в мастер? Конечно, это означает, что я должен создать эту вторую ветвь функций из master?
dOxxx
1
@dOxxx: Да, есть исключения, например, когда одна ветка строится на другой (либо напрямую, либо после слияния предыдущего в мастер).
Якуб Наребски