A3.4 Приложение C: Команды Git — Ветвление и слияния
За создание новых веток и слияние их воедино отвечает несколько Git команд.
git branch
Команда git branch — это своего рода «менеджер веток». Она умеет перечислять ваши ветки, создавать новые, удалять и переименовывать их.
Большая часть главы Ветвление в Git посвящена этой команде, она используется повсеместно в этой главе. Впервые команда branch была представлена в разделе Создание новой ветки главы 3, а большинство таких её возможностей как перечисление и удаление веток были разобраны в разделе Управление ветками главы 3.
В разделе Отслеживание веток главы 3 мы показали как использовать сочетание git branch -u для отслеживания веток.
Наконец, мы разобрались что происходит за кулисами этой команды в разделе Ссылки в Git главы 10.
git checkout
Команда git checkout используется для переключения веток и выгрузки их содержимого в рабочий каталог.
Мы познакомились с этой командой в разделе Переключение веток главы 3 вместе с git branch .
В разделе Отслеживание веток главы 3 мы узнали как использовать флаг —track для отслеживания веток.
В разделе Использование команды checkout в конфликтах главы 7 мы использовали эту команду с опцией —conflict=diff3 для разрешения конфликтов заново, в случае если предыдущее решение не подходило по некоторым причинам.
Мы рассмотрели детали взаимосвязи этой команды и git reset в разделе Раскрытие тайн reset главы 7.
Мы исследовали внутренние механизмы этой команды в разделе HEAD главы 10.
git merge
Команда git merge используется для слияния одной или нескольких веток в текущую. Затем она устанавливает указатель текущей ветки на результирующий коммит.
Мы познакомили вас с этой командой в разделе Основы ветвления главы 3. И хотя git merge встречается в этой книге повсеместно, практически все использования имеют вид git merge с указанием единственной ветки для слияния.
Мы узнали как делать «сплющенные» слияния (когда Git делает слияние в виде нового коммита, без сохранения всей истории работы) в конце раздела Форк публичного проекта.
В разделе Продвинутое слияние главы 7 мы глубже разобрались с процессом слияния и этой командой, включая флаги -Xignore-all-whitespace и —abort , используемый для отмены слияния в случае возникновения проблем.
Мы научились проверять криптографические подписи перед слияниями если ваш проект использует GPG в разделе Подпись коммитов главы 7.
Ну и наконец в разделе Слияние поддеревьев главы 7 мы познакомились со слиянием поддеревьев.
git mergetool
Команда git mergetool просто вызывает внешнюю программу слияний, в случае если у вас возникли проблемы слияния.
Мы вкратце упомянули о ней в разделе Основные конфликты слияния главы 3 и рассказали как настроить свою программу слияния в разделе Внешние программы слияния и сравнения главы 8.
git log
Команда git log используется для просмотра истории коммитов, начиная с самого свежего и уходя к истокам проекта. По умолчанию, она показывает лишь историю текущей ветки, но может быть настроена на вывод истории других, даже нескольких сразу, веток. Также её можно использовать для просмотра различий между ветками на уровне коммитов.
Практически во всех главах книги эта команда используется для демонстрации истории проекта.
Мы познакомились c git log и некоторыми её деталями в разделе Просмотр истории коммитов главы 2. Там мы видели использование опций -p и —stat для получения представления об изменениях в каждом коммите, а также —pretty and —oneline для настройки формата вывода этой команды — более полным и подробным или кратким.
В разделе Создание новой ветки главы 3 мы использовали опцию —decorate чтобы отобразить указатели веток на истории коммитов, а также —graph чтобы просматривать историю в виде дерева.
В разделах Небольшая команда главы 5 и Диапазоны коммитов главы 7 мы познакомили вас с синтаксисом branchA..branchB , позволяющем команде git log показывать только коммиты, присутствующие в одной ветке, но отсутствующие в другой. Мы довольно подробно рассматриваем этот вопрос в разделе Диапазоны коммитов.
В разделах История при слиянии и Три точки главы 7 мы рассмотрели синтаксис branchA…branchB и опцию —left-right позволяющие увидеть, что находится в одной или в другой ветке, но не в них обеих сразу. Также в разделе История при слиянии мы рассмотрели опцию —merge , которая может быть полезной при разрешении конфликтов, а также —cc для просмотра конфликтов слияния в истории проекта.
В разделе RefLog-сокращения главы 7 мы использовали опцию -g для вывода git reflog , используя git log .
В разделе Поиск главы 7 мы рассмотрели использование опций -S и -L для поиска событий в истории проекта, например, истории развития какой-либо фичи.
В разделе Подпись коммитов главы 7 мы показали, как использовать опцию —show-signature для отображения строки валидации подписи для каждого коммита в git log .
git stash
Команда git stash используется для временного сохранения всех незафиксированных изменений с целью очистки рабочего каталога без необходимости фиксировать незавершённую работу в текущей ветке.
Эта команда практически целиком раскрыта в разделе Припрятывание и очистка главы 7.
git tag
Команда git tag используется для задания постоянной метки на какой-либо момент в истории проекта. Обычно она используется для релизов.
Мы познакомились и разобрались с ней в разделе Работа с тегами главы 2 и использовали на практике в разделе Помечайте свои релизы главы 5.
Мы научились создавать подписанные с помощью GPG метки, используя флаг -s , и проверять их, используя флаг -v , в разделе Подпись главы 7.
Чем отличается merge от commit?
Чем отличается merge от commit? Когда комитим — заливаем, скажем, в главную ветку свои изменения. Когда мержим — объединяем свою копию с изменениями в главной ветке. Когда нужно выложить проект в продакшн, переключаемся на trunk, мержим, а потом еще и комитим, зачем второе действие?
Отслеживать
задан 16 июл 2012 в 9:56
1,936 7 7 золотых знаков 28 28 серебряных знаков 47 47 бронзовых знаков
@nMike, все очень просто. «Meржим» всегда свою копию, база не меняется. «Коммитим» — это заливаем свое в базу. Насколько помню, если при commit возникает конфликт, то commit не проходит. Надо сделать svn merge в локальную копию, разрешить конфликты, а потом закинуть в базу svn ci .
16 июл 2012 в 11:00
тогда чем отличается merge от update?
1 авг 2012 в 3:25
@nMike А чем отличается зеленый от сладкого?
1 авг 2012 в 11:14
Мержим из транка, апдейтим из ветки. Если работаем в транке, то операции эквивалентны.
29 авг 2012 в 3:44
@nMike вы ошибаетесь, merge от update отличаются не только этим.
22 дек 2016 в 6:24
3 ответа 3
Сортировка: Сброс на вариант по умолчанию
Если бы merge был бы эквивалентен merge + commit , то пользователь потерял бы возможность выполнять следующие действия:
- Целиком review’ить изменения в feature branch , которые появляются после merge —reintegrate .
- Решать conflict’ы и tree conflict’ы , которые возникли в результате операции merge .
Отслеживать
ответ дан 16 июл 2012 в 10:06
M. Williams M. Williams
23.5k 1 1 золотой знак 39 39 серебряных знаков 58 58 бронзовых знаков
Дело в том, что commit — это не просто заливка изменений. Сущность этой операции заключается в том, что изменения, сделанные локально, преобразуются в изменения, которые могут быть успешно добавлены в репозиторий и зафиксированы в нём в виде очередной ревизии. При этом локальные изменения всегда выражены в виде различий относительно последней ревизии, о которой известно локально (т.е. ревизии, которая была во время последнего update), а в репозиторий могут быть добавлены лишь такие изменения, которые выражены в виде различий относительно последней ревизии самого репозитория.
В простейшем случае, когда в репозиторий не было добавлено ничего со стороны, преобразование изменений сводится к тому, чтобы не делать ничего, поскольку изменения и так уже выражены в виде различий относительно последней ревизии в репозитории. Поэтому операция commit становится тривиальной и выглядит, как простая заливка изменений. В случае же, когда с момента последнего update в репозитории уже появилось что-то новое, попытка сделать тривиальный commit проваливается и приходится проходить всю процедуру полностью, т.е. сначала приспосабливать свои локальные изменения к тем другим изменениям, которые уже были зафиксированы в репозитории кем-то другим, и только потом заливать их и фиксировать.
Когда мы заливаем в репозиторий изменения, сделанные в рабочей копии, необходимость делать commit в самом конце очевидна. Ведь в репозитории ещё нет наших локальных изменений, поскольку предыдущая попытка сделать тривиальный commit провалилась. Не так очевидна эта необходимость в ситуации, когда мы объединяем одну ветку с другой прямо в репозитории с помощью merge. Дело тут вот в чём. На самом деле, операция commit играет ещё и роль подтверждения. Заливка в репозиторий новых изменений как в тривиальном случае, так и после адаптации всегда выполняется после того, как пользователь увидел окончательный вариант, поэтому его подтверждение, необходимое для фиксации очередной ревизии, как бы подразумевается и даётся тем же самым commit’ом. А вот про объединение веток в репозитории такого сказать нельзя. Результат объединения веток обычно отличается от того, что было в последних ревизиях как одной ветки, так и другой. Он представляет собой совершенно новую ревизию. И даже в том случае, когда участие пользователя в самом объединении веток не требуется, от него всё равно требуется получить подтверждение после того, как будет получен окончательный вариант новой ревизии. Именно для этого и нужен commit после merge. Если объединение прошло на автомате, то commit играет лишь роль подтверждения, которое необходимо для фиксации любой ревизии в репозитории.
Введение в Git Merge и Git Rebase: зачем и когда их использовать
Часто у разработчиков возникает выбор между Merge (слияние) и Rebase (перемещение). В Гугле вы увидите разное мнение, многие советуют не использовать Rebase, так как это может вызвать серьезные проблемы. В статье я объясню, что такое слияние и перемещение, почему вы должны (или не должны) использовать их и как это сделать.

Git Merge и Git Rebase преследуют одну и ту же цель. Они предназначены для интеграции изменений из одной ветки в другую. Хотя конечная цель одинаковая, принципы работы разные.
Некоторые считают, что вы всегда должны использовать Rebase, другие предпочитают Merge. В этом есть свои плюсы и минусы.
Git Merge

Слияние — обычная практика для разработчиков, использующих системы контроля версий. Независимо от того, созданы ли ветки для тестирования, исправления ошибок или по другим причинам, слияние фиксирует изменения в другом месте. Слияние принимает содержимое ветки источника и объединяет их с целевой веткой. В этом процессе изменяется только целевая ветка. История исходных веток остается неизменной.
Плюсы:
- простота;
- сохраняет полную историю и хронологический порядок;
- поддерживает контекст ветки.
- история коммитов может быть заполнена (загрязнена) множеством коммитов;
- отладка с использованием git bisect может стать сложнее.
Слейте ветку master в ветку feature, используя команды checkout и merge.
$ git checkout feature $ git merge master (or) $ git merge master feature
Это создаст новый «Merge commit» в ветке feature, который содержит историю обеих веток.
Git Rebase
Rebase — еще один способ перенести изменения из одной ветки в другую. Rebase сжимает все изменения в один «патч». Затем он интегрирует патч в целевую ветку.
В отличие от слияния, перемещение перезаписывает историю, потому что она передает завершенную работу из одной ветки в другую. В процессе устраняется нежелательная история.

- Упрощает потенциально сложную историю
- Упрощение манипуляций с единственным коммитом
- Избежание слияния коммитов в занятых репозиториях и ветках
- Очищает промежуточные коммиты, делая их одним коммитом, что полезно для DevOps команд
- Сжатие фич до нескольких коммитов может скрыть контекст
- Перемещение публичных репозиториев может быть опасным при работе в команде
- Появляется больше работы
- Для восстановления с удаленными ветками требуется принудительный пуш. Это приводит к обновлению всех веток, имеющих одно и то же имя, как локально, так и удаленно, и это ужасно.
Как это сделать
Переместите ветку feature на главной ветке, используя следующие команды.
$ git checkout feature $ git rebase master
Это перемещает всю ветку функции в главную ветку. История проекта изменяется, создаются новые коммиты для каждого коммита в основной ветке.
Интерактивное перемещение
Это позволяет изменять коммиты при их перемещении в новую ветку. Это лучше, чем автоматическое перемещение, поскольку обеспечивает полный контроль над историей коммитов. Как правило, используется для очистки истории до слияния ветки feature в master.
$ git checkout feature $ git rebase -i master
Это откроет редактор, перечислив все коммиты, которые будут перемещены.
pick 22d6d7c Commit message#1 pick 44e8a9b Commit message#2 pick 79f1d2h Commit message#3
Это точно определяет, как будет выглядеть ветка после выполнения перемещения. Упорядочивая объекты, вы можете сделать историю такой, как захотите. Вы можете использовать команды fixup, squash, edit, и так далее.
Какой из них использовать?
Так что же лучше? Что рекомендуют эксперты?
Трудно принять единственно правильное решение о том, что лучше использовать, поскольку все команды разные. Всё зависит от потребностей и традиций внутри команды.
Принимайте решения на основании компетенции команды в Git. Для вас важна простота или перезаписывание истории, а может быть что-то другое?
Что рекомендую я?
По мере роста команды становится сложно управлять или отслеживать изменения в разработке, применяя слияние. Чтобы иметь чистую и понятную историю коммитов, разумно использовать Rebase.
Преимущества Rebase:
- Вы разрабатываете локально: если вы не делились своей работой с кем-либо еще. На данный момент вы должны предпочесть перемещение слиянию, чтобы сохранить свою историю в порядке. Если у вас есть личная вилка репозитория, которая не используется совместно с другими разработчиками, вы можете делать rebase даже после того, как переместились в свою ветку.
- Ваш код готов к ревью: вы создали пулл реквест. Другие анализируют вашу работу и потенциально стягивают ее к своей вилке для локального ревью. На данный момент вы не должны перемещать свою работу. Вы должны создать коммит «переделать» и обновить ветку. Это помогает отслеживать запросы на пулл реквест и предотвращает случайную поломку истории.
- Ревью сделано и готово к интеграции в целевую ветку. Поздравляем! Вы собираетесь удалить свою ветку feature. Учитывая, что с этого момента другие разработчики не будут fetch-merging эти изменения, это ваш шанс изменить вашу историю. На этом этапе вы можете переписать историю и сбросить оригинальные коммиты, и эти надоедливые «переделки» и «слияние» сливаются в небольшой набор целенаправленных коммитов. Создание явного слияния для этих коммитов является необязательным, но имеет значение. Он записывает, когда функция достигла master.
Не забывайте:
code = coffee + developer
Команда Git Merge
В какой-то момент история репозитория ветвится. Например, в проекте требуется пофиксить баг — обычно это выполняют в отдельной ветке, а потом сливают в основную ветку master. Команда git merge как раз и служит для слияния веток.
Пример использования
Допустим, есть основная ветка master. И есть ветка hotfix, в которой мы фиксим баг. Мы его пофиксили и хотим слить изменения в ветку master. Для этого надо перейти на ветку master — ту ветку, в которую сливают изменения:
git checkout master
И залить изменения из ветки hotfix в текущую ветку master:
git merge hotfix
Это типичный пример использования команды.
Но не всегда слияние происходит гладко. Пока мы делали hotfix, кто-то мог отредактировать тот же файл в том же месте, что и мы. Тогда слияние не удается, возникает конфликт.
Конфликт слияния
Допустим, файл index.html отредактировал кто-то еще в том же месте. Тогда при попытке выполнить команду merge мы получим сообщение:
$ git merge hotfix Auto-merging index.html CONFLICT (content): Merge conflict in index.html Automatic merge failed; fix conflicts and then commit the result.
Что же делать? Если открыть index.html, мы увидим, что Git его пометил примерно так:
contact : email.support@github.com