Как я могу просмотреть слияние в Git?

397

У меня есть ветка git (например, mainline), и я хочу объединиться в другую ветку разработки. Или я?

Чтобы решить, действительно ли я хочу объединить эту ветку, я хотел бы увидеть какой-то предварительный просмотр того, что сделает слияние. Желательно с возможностью видеть список коммитов, которые применяются.

Пока что лучшее, что я могу придумать - это merge --no-ff --no-commitи тогда diff HEAD.

Glenjamin
источник
17
Я бы просто, git mergeи git reset --keep HEAD@{1}если мне не понравится результат.
Ян Худек
3
Обратите внимание, что просмотр списка коммитов с их diff не обязательно рассказывает всю историю - если слияние нетривиально, и особенно если есть конфликты, фактический результат слияния может быть немного интересным.
Каскабель
2
Ваш оригинальный метод делает именно это. Суть моего комментария заключается в том, что, хотя просмотр отдельных различий все хорошо и хорошо, если у вас сложное слияние, вы можете получить удивительные результаты, даже если все объединенные коммиты независимо хороши.
Каскабель
2
@Jan: по каким-то причинам git reset --keep HEAD@{1}вернулась fatal: Cannot do a keep reset in the middle of a merge.справка?
Мой
3
Почему нет --previewопции в git-merge?
Гауи

Ответы:

285

Я обнаружил, что лучшим решением для меня является просто выполнить слияние и прервать его, если возникнут конфликты . Этот конкретный синтаксис кажется мне чистым и простым. Это стратегия 2 ниже.

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

Стратегия 1: Безопасный путь - объединить временную ветку:

git checkout mybranch
git checkout -b mynew-temporary-branch
git merge some-other-branch

Таким образом, вы можете просто выбросить временную ветвь, если вы просто хотите увидеть, что это за конфликты. Вам не нужно беспокоиться об «отмене» слияния, и вы можете вернуться к своей работе - просто зарегестрируйте mybranch снова, и у вас не будет никакого кода слияния или конфликтов слияния в вашей ветке.

Это в основном всухую.

Стратегия 2: когда вы определенно хотите объединиться, но только если нет конфликтов

git checkout mybranch
git merge some-other-branch

Если git сообщает о конфликтах (и ТОЛЬКО ЕСЛИ ЕСТЬ конфликты), вы можете сделать:

git merge --abort

Если слияние прошло успешно, вы не можете прервать его (только сбросить).

Если вы не готовы к слиянию, используйте безопасный способ выше.

[EDIT: 2016-ноябрь - я поменял стратегию 1 на 2, потому что, похоже, большинство людей ищут «безопасный путь». Стратегия 2 теперь больше напоминает, что вы можете просто прервать слияние, если слияние имеет конфликты, с которыми вы не готовы иметь дело. Имейте в виду, если читаете комментарии!]

Kasapo
источник
2
+1 за стратегию 2. Временная ветка, которая показывает, что именно будет происходить при слиянии. Стратегия 1 - это то, чего я пытаюсь избежать в данном конкретном случае.
Гордолио
2
Я предлагаю изменить стратегии так, чтобы сначала была более безопасная (мой психологический тренинг проходит - большинство людей предположили бы, что лучшим вариантом будет первый, несмотря на четкое использование слова «более безопасный»), но, кроме этого, превосходный работа.
paxdiablo
6
Вы можете сделать это, git merge --no-ff --no-commitесли не хотите фиксировать изменения напрямую. Это устраняет «необходимость» в другой ветке и упрощает просмотр изменений, imo.
Джерард ван Хельден
и если вы решите, что вообще не хотите слияния, а на самом деле предпочли бы написать еще немного кода, а затем зафиксировать в своей ветке, чтобы вы могли развернуть JUST свою ветку на тестовом сервере, разве вам не придется возвращать git merge --no-ff --no-commit? Я думаю, что вы все еще можете сделать git merge --abortпосле этого, если вам не нравится то, что вы видите? Даже если слияние не вызывает конфликтов?
Kasapo
Большинство людей любят копировать и вставлять и не слишком много думать. Следовательно, для Стратегии 1, возможно, также добавьте, как прервать слияние во временной локальной ветви, git reset --hard HEADа затем извлечь другую ветку git checkout <different branch name>и удалить временную ветку git delete -b <name of temporary branch>.
user3613932
402
  • git log ..otherbranch
    • список изменений, которые будут объединены в текущую ветку.
  • git diff ...otherbranch
    • diff от общего предка (базы слияния) до главы того, что будет объединено. Обратите внимание на три точки , которые имеют особое значение по сравнению с двумя точками (см. Ниже).
  • gitk ...otherbranch
    • графическое представление ветвей, так как они были объединены в последний раз.

Пустая строка подразумевает HEAD, поэтому просто ..otherbranchвместо HEAD..otherbranch.

Две точки против трех имеют немного другое значение для diff, чем для команд, которые перечисляют ревизии (log, gitk и т. Д.). Для журнала и других две точки ( a..b) означают все, что есть, bно не aтри, а три точки ( a...b) означают все, что есть только в одном aили b. Но diff работает с двумя ревизиями, и там более простой случай, представленный двумя точками ( a..b), представляет собой простое отличие от ato, bа три точки ( a...b) означают разницу между общим предком и b( git diff $(git merge-base a b)..b).

Ян Худек
источник
7
Третья точка была частью, которую мне не хватало, спасибо! Подход журнала также работает хорошо, log -p --reverse ..otherbranch кажется хорошим способом увидеть, что будет объединено.
Glenjamin
1
Я скучал git checkout masterдо того, как попробовал это. Мне было интересно, почему он говорил, что все мои изменения будут переписаны ...
Дрооганс
5
git show ..otherbranchпокажет список изменений и diff, которые будут объединены в текущую ветку.
Гауи
4
Это не совсем точно, особенно для вишневых кирок.
void.pointer
2
@ void.pointer, git не обрабатывает вишневые пики специально при обычном слиянии, поэтому здесь их тоже нет. Можно было бы написать стратегию слияния, но, насколько я знаю, так и не было.
Ян Худек
19

Если вы похожи на меня, вы ищете эквивалент svn update -n. Следующее, кажется, делает трюк. Обратите внимание, что git fetchсначала нужно сделать так, чтобы в вашем локальном репо были соответствующие обновления для сравнения.

$ git fetch origin
$ git diff --name-status origin/master
D       TableAudit/Step0_DeleteOldFiles.sh
D       TableAudit/Step1_PopulateRawTableList.sh
A       manbuild/staff_companies.sql
M       update-all-slave-dbs.sh

или если вы хотите получить разность от вашей головы к пульту:

$ git fetch origin
$ git diff origin/master

IMO это решение намного проще и менее подвержено ошибкам (и, следовательно, намного менее рискованно), чем топовое решение, которое предлагает «объединить, а затем прервать».

djschny
источник
1
должно быть $ git checkout target-branchи тогда $ git diff --name-status ...branch-to-be-merged(три точки необходимы, поэтому введите их как есть)
Lu55
Единственное, чего здесь не хватает: он не показывает, какие файлы будут иметь конфликт - они имеют тот же маркер «M», что и файлы, которые были изменены в ветви, но могут быть объединены без каких-либо конфликтов.
peterflynn
Возможно, в 2012 году все было по-другому, но в настоящее время прерывание слияния кажется довольно надежным, поэтому я не думаю, что сейчас можно охарактеризовать это как «гораздо более простую и менее подверженную ошибкам».
Дэвид З
8

Большинство ответов здесь либо требуют чистого рабочего каталога и нескольких интерактивных шагов (плохо для сценариев), либо не работают для всех случаев, например, прошлые слияния, которые уже вносят некоторые из ожидающих изменений в целевую ветку, или cherry-picks, выполняющие одни и те же.

Чтобы по-настоящему увидеть, что изменится в masterветке, если вы объединитесь developс ней прямо сейчас:

git merge-tree $(git merge-base master develop) master develop

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

git merge-tree $(git merge-base master develop) master develop | colordiff | less -R

- https://git.seveas.net/previewing-a-merge-result.html

(спасибо Дэвиду Нормингтону за ссылку)

PS:

Если вы получите конфликты слияния, они будут отображаться с обычными маркерами конфликта в выходных данных, например:

$ git merge-tree $(git merge-base a b ) a b 
added in both
  our    100644 78981922613b2afb6025042ff6bd878ac1994e85 a
  their  100644 61780798228d17af2d34fce4cfbdf35556832472 a
@@ -1 +1,5 @@
+<<<<<<< .our
 a
+=======
+b
+>>>>>>> .their

Пользователь @dreftymac делает хорошее замечание: это делает его непригодным для сценариев, потому что вы не можете легко это уловить из кода состояния. Маркеры конфликта могут сильно различаться в зависимости от обстоятельств (удаленные или измененные и т. Д.), Что также затрудняет поиск. Осторожно.

hraban
источник
1
@ Hraban Это похоже на правильный ответ, однако, по-видимому, все еще есть один отсутствующий элемент. Этот подход требует, чтобы пользователь «просматривал» вывод, чтобы увидеть, присутствуют ли маркеры конфликта. Есть ли у вас подход, который просто возвращает, trueесли есть конфликты и falseесли нет конфликтов (например, логическое значение, которое не требует теста «глазного яблока» или какого-либо вмешательства человека).
dreftymac
1
@dreftymac не может найти что-либо на этот счет. Вы могли бы использовать что-то вроде, git merge-tree ... | grep -q '^[a-z].*in both$' && echo conflict || echo safe to mergeно это привередливый; Я, наверное, забыл случай. может быть, вы хотите проверить маркеры конфликта? например, это, вероятно, не ловит конфликты стилей "их удалили, наши изменили". (Я только что проверил, и это даже не показывает маркеры конфликта, поэтому вам нужно тщательное регулярное выражение, чтобы быть в безопасности здесь)
Храбан
1
Используйте less -Rдля обработки цветного вывода без изменения вашей конфигурации.
Джакомо Альзетта
Спасибо @GiacomoAlzetta, я обновил его.
hraban
6

Если вы уже получили изменения, мой любимый:

git log ...@{u}

Это нужно мерзавец 1.7.x Я верю, хотя. @{u}Обозначения являются «сокращенным» для восходящего потока ветви , так что это немного более универсально , чемgit log ...origin/master .

Примечание. Если вы используете zsh и функцию glog, вам, вероятно, придется сделать что-то вроде:

git log ...@\{u\}
Пабло Олмос де Агилера С.
источник
6

В дополнение к существующим ответам можно создать псевдоним, чтобы показать различия и / или журнал до слияния. Во многих ответах пропускается, что fetchнужно сделать перед «предварительным просмотром» слияния; это псевдоним , который сочетает в себе эти два шага в один (эмуляция нечто похожее на ртутных х hg incoming/outgoing )

Итак, опираясь на " git log ..otherbranch", вы можете добавить следующее к ~/.gitconfig:

...
[alias]
    # fetch and show what would be merged (use option "-p" to see patch)
    incoming = "!git remote update -p; git log ..@{u}"

Для симметрии можно использовать следующий псевдоним, чтобы показать, что зафиксировано и будет передано до нажатия:

    # what would be pushed (currently committed)
    outgoing = log @{u}..

И затем вы можете запустить « git incoming», чтобы показать множество изменений, или « git incoming -p», чтобы показать патч (т. Е. «Diff»), « git incoming --pretty=oneline», для краткого изложения и т. Д. Затем вы можете (необязательно) запустить « git pull» для на самом деле слить. (Хотя, так как вы уже получили, слияние может быть сделано напрямую.)

Точно так же " git outgoing" показывает, что будет толкаться, если вы будете бежать " git push".

Майкл
источник
3

git log currentbranch..otherbranchвыдаст вам список коммитов, которые войдут в текущую ветку, если вы сделаете слияние. Обычные аргументы в журнале, которые дают подробности о коммитах, дадут вам больше информации.

git diff currentbranch otherbranchдаст вам разницу между двумя коммитами, которые станут одним. Это будет diff, который даст вам все, что будет объединено.

Это поможет?

Нуфал Ибрагим
источник
2
На самом деле, неправильно. git log otherbranch..currentbranchдает список коммитов на текущей ветке . Это git diff otherbranch currentbranchдает вам различие от версии, которая будет объединена, до текущей подсказки, которая настолько бесполезна, насколько это возможно, потому что вам нужно различие от базы слияния до головки слияния.
Ян Худек
Спасибо. Я поменял названия деревьев.
Нуфал Ибрагим
3

Запрос на извлечение - я использовал большинство уже представленных идей, но я также часто использую (особенно если он от другого разработчика) выполняет запрос на извлечение, который дает удобный способ просмотреть все изменения в слиянии, прежде чем оно произойдет место. Я знаю, что это GitHub, а не Git, но это, безусловно, удобно.

G-Man
источник
2

Если не считать фактического выполнения слияния одноразовым способом (см. Ответ Касапо), то, похоже, нет надежного способа увидеть это.

Сказав это, вот метод, который немного приближается:

git log TARGET_BRANCH...SOURCE_BRANCH --cherry

Это дает четкое представление о том, какие коммиты попадут в слияние. Чтобы увидеть различия, добавьте -p. Чтобы увидеть имена файлов, добавить любой из --raw, --stat, --name-only,--name-status .

Проблема с git diff TARGET_BRANCH...SOURCE_BRANCHподходом (см. Ответ Яна Худека) состоит в том, что вы увидите различия в изменениях уже в вашей целевой ветви, если ваша исходная ветвь содержит перекрестные слияния.

antak
источник
1

Может быть, это может помочь вам? git-diff-tree - сравнивает содержимое и режим BLOB-объектов, найденных с помощью двух объектов дерева.

Chugaister
источник
1

Я не хочу использовать команду git merge в качестве предвестника для проверки конфликтующих файлов. Я не хочу делать слияние, я хочу определить потенциальные проблемы до слияния - проблемы, которые автоматическое слияние может скрыть от меня. Решение, которое я искал, состоит в том, как заставить git выложить список файлов, которые были изменены в обеих ветвях, которые будут объединены вместе в будущем, по отношению к некоторому общему предку. Как только у меня будет этот список, я смогу использовать другие инструменты сравнения файлов для дальнейшего изучения. Я искал несколько раз, и я до сих пор не нашел то, что я хочу в нативной команде git.

Вот мой обходной путь, на случай, если он поможет кому-то еще:

В этом сценарии у меня есть ветка под названием QA, в которой есть много изменений с момента последнего производственного выпуска. Наш последний производственный выпуск помечен как «15.20.1». У меня есть другая ветка разработки под названием new_stuff, которую я хочу объединить с веткой QA. И QA, и new_stuff указывают на коммиты, которые «следуют» (как сообщает gitk) за тегом 15.20.1.

git checkout QA
git pull
git diff 15.20.1 --name-only > QA_files
git checkout new_stuff
git pull
git diff 15.20.1 --name-only > new_stuff_files
comm -12 QA_files new_stuff_files

Вот некоторые обсуждения, в которых рассказывается, почему я заинтересован в нацеливании на эти конкретные файлы:

Как я могу доверять Git Merge?

/software/199780/how-far-do-you-trust-automerge

Лаура
источник