git-шоу слияния

когда у меня есть слияние и запуск git show #commit, он показывает только журнал фиксации, а не разницу с реальным изменением, например

commit c0f50178901e09a1237f7b9d9173ec5d1c4936c
Merge: ed234b ded051
Author: abc
Date:   Mon Nov 21 15:56:33 2016 -0800

    Merge branch 'abc'

Я понимаю, что реальная фиксация находится в журнале слияния, но я хочу сохранить ввод текста, есть ли способ показать diff в одном?

Ответ 1

TL; DR: используйте git show -m c05f017 или git show --first-parent c05f017, или, возможно, git diff c05f017^ c05f017.


Там возникает фундаментальная ошибка в вашем вопросе: коммиты не являются различиями; коммиты - это снимки. Это может показаться разницей без разницы - и для некоторых коммитов это так. Но для слияния коммитов это не так.

Когда git show (или git log -p) показывает фиксацию как diff, она делает это, сравнивая моментальный снимок фиксации с чем-то другим. Команда git diff выполняет одно и то же: сравнивает одну фиксацию с другой фиксацией. (Или он может сравнить фиксацию с деревом, или с содержимым индекса, либо с несколькими другими комбинациями).

Для обычных коммитов тривиально очевидно, что сравнивать: сравните этот моментальный снимок фиксации с предыдущим (т.е. родительским) фиксацией моментального снимка. Вот что делает git showgit log -p тоже): он запускает git diff от родительского commit, к этой фиксации.

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


1 Слияние может иметь трех или более родителей. Они называются "сгустки осьминогов". Однако они не делают ничего особенного и в основном для хвастовства. :-) Вы можете игнорировать их здесь.


Когда есть два родителя, с которыми (-ам) следует git show сравнение?

Что git log -p выбирает по умолчанию, совсем не сравнивать. Вы можете заставить его показать что-то, добавив различные флаги (см. Ниже).

То, что git show предпочитает делать по умолчанию, сложнее. Поскольку есть два родителя, git show сначала сравнение с "первым родителем", 2 затем сравнивается со вторым родителем. Затем - эта часть очень важна - она сочетает в себе два отличия, создавая так называемый "комбинированный diff".

В следующем разделе позвольте мне отметить сложный, но очень полезный бит синтаксиса Git. Если у вас есть совершить ID как c05f017, вы можете добавить каретку или "шляпа" символ ^ после того, чтобы назвать родителем фиксации. Вы можете дополнительно добавить другой номер, чтобы выбрать родителя. Для регулярных (не -m erge) фиксирует только один, поэтому c05f017^ является родительским. Для c05f017^ слияния c05f017^ и c05f017^1 означают первый родительский c05f017^2, а c05f017^2 - второй родитель.


2 Я помещал это в кавычки, потому что первая родительская идея особенно важна в Git, как мы увидим в одно мгновение. Другими словами, Git больше всего заботится о том, какой родитель является первым, а остальные - просто "остальным".


Комбинированные различия

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

Обратите внимание, что объединенный diff содержит только файлы, которые были изменены от всех родителей.

То есть, предположим, что M является фиксацией слияния, а diff M ^ 1 против M говорит, что файл mainline.txt и common.txt оба изменены. Предположим далее, что разные M ^ 2 и M говорят, что файлы sidebranch.txt и common.txt были изменены. Объединенный diff покажет только common.txt, пропустив как mainline.txt и sidebranch.txt потому что эти два файла были изменены только от одного родителя (каждого). (Даже тогда Git может отображать только некоторые common.txt для common.txt.)


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


Разделение разностей

Параметр -m вероятно, означает слияние здесь, - говорит Git, по сути, "разделить" слияние. То есть вместо того, чтобы пытаться объединить различия с каждым родителем в один большой комбинированный diff, просто покажите diff для каждого родителя, по одному разу за раз.

Это иногда то, что вы хотите. Когда это не то, что вы хотите, вы можете запустить свой собственный явный git diff чтобы просто сравнить с одним из двух родителей (или см. Ниже).

С каким родителем вы должны столкнуться?

Обычно правильный ответ - "первый родитель".

Ключ к понятию "первый родитель" заключается в том, что когда Git делает комманду слияния, он всегда записывает ветвь, в которой вы находитесь, в то время, как первый родитель. Другая ветка становится вторым родителем.

То есть, если вы develop и объединяете topic:

$ git checkout develop
$ git merge topic

Git сделает новое обязательство, совершить слияние с двумя родителями-на текущей ветке, develop. Первым родителем фиксации слияния будет фиксация, которая была вершиной develop всего лишь минуту назад. Второй родитель будет коммитом, который (по-прежнему) является кончиком topic.

Поскольку вас обычно беспокоит то, что принесло слияние, сравнение с первым родителем даст вам это. Так обычно, что вы хотите. По этой причине git show позволяет запускать git show --first-parent. Это "расщепляет" фиксацию, а затем git show только отличается от первого родителя. (Это немного отличается от git show -m, который разделяет фиксацию дважды: первый split сравнивается с первым родителем, а второй split сравнивается со вторым родителем.)

Аналогично, вы можете запустить git log -p --first-parent... но вы все равно должны добавить -m чтобы увидеть изменение как патч, потому что по умолчанию git log просто пропускает отображение различий полностью для слияния, (Внутренне, операция пропуска - потому что операция -m erge переопределяет то, как раскол заставляет его действовать, как не--m erge.) Здесь, - --first-parent имеет еще более важный эффект: журнал операция не смотрит ни на одну из боковых ветвей вообще, только на основной (первая -p arent) линия.