TL; DR
Используйте --full-history
- но вы, вероятно, тоже хотите больше опций, так что читайте дальше.
Long
Во-первых, большое спасибо за сценарий воспроизведения! Это было очень полезно здесь.
Далее:
(git log --all
показывает все, но в реальной жизни он показывает все изменения во всех файлах, включая не относящиеся к делу изменения в y
и z
, которые было бы слишком сложно просмотреть.)
Да. Но это демонстрирует, что здесь нет проблем ни с одним из коммитов; , проблема полностью в git log
, здесь. Это связано с ужасным упрощением истории режимом, который:
git log master -- x
вызывает.
git log
без упрощения истории
Позвольте мне добавить вывод из:
git log --all --decorate --oneline --graph
(«Журнал git с помощью A DOG») , который, так как я делал репродукцию с использованием сценария, будет иметь хеш-идентификаторы, отличные от того, что у вас (или у кого-либо еще, кто делает другое воспроизведение), но имеет та же структура, что позволяет нам говорить о коммитах:
* cc7285d (HEAD -> master, branch) Merge branch 'master' into branch
|\
| * ad686b0 changed x to x2, z to z2
* | dcaa916 changed y to y2, z to z3
|/
* a222cef initial commit
Теперь обычный git log
, без -- x
для проверки файла x
, не не включает упрощение истории. Git начинается с указанного вами коммита - например:
git log dcaa916
начинается с dcaa916
- или с HEAD
, если вы ничего не указали.
В этом случае git log
начинается с коммита cc7285d
. Git показывает этот коммит, затем переходит к родителю (-ям) этого коммита. Здесь есть два родителя - dcaa916
и ad686b0
- поэтому Git мест оба фиксирует в очередь с приоритетами . Затем он вытягивает один из коммитов из головы очереди. Когда я пытаюсь это сделать, то получается dcaa916
. (В более реалистичных графиках по умолчанию будет использоваться график с более поздней меткой времени коммиттера, но, собрав этот репозиторий с помощью скрипта, оба коммита имеют одинаковую метку времени.) Git показывает, что коммит и помещает родителя dcaa916
a222cef
в очередь. Для топологического здравого смысла, учитывая этот конкретный график, коммит в начале очереди теперь всегда будет ad686b0
, поэтому Git показывает этот коммит и затем ....
Что ж, теперь родительский элемент ad686b0
равен a222cef
, но a222cef
уже находится в очереди! Вот тут-то и возникает «топологическая рассудительность». Не показывая a222cef
слишком рано, мы следим за тем, чтобы случайно не показать a222cef
дважды (среди прочих вопросов). Теперь в очереди есть a222cef
и ничего больше, поэтому git log
снимает a222cef
с очереди, показывает a222cef
и помещает в очередь родителей a222cef
. В этом примере с репродуктором нет родителей, поэтому очередь остается пустой, и git log
может закончиться, и это именно то, что мы видим с обычным git log
. С помощью A DOG мы также получаем график и вариант вывода в одну строку.
git log
с упрощением истории
Git не имеет файла истории. История в репозитории состоит из коммитов . Но git log
сделает все возможное, чтобы показать историю файлов. Чтобы сделать это, он должен синтезировать один и сделать , что , авторы Git решили просто пропустить некоторое подмножество коммитов. Документация пытается объяснить это с помощью одного предложения:
Иногда вас интересуют только части истории, например, коммиты, модифицирующие конкретный . Но есть две части Упрощение истории , одна часть - выбор коммитов, а другая - как это сделать, поскольку существуют различные стратегии для упрощения истории.
Я думаю, что это объяснение из одного параграфа просто не работает, но я еще не придумал, как мне кажется, объяснение правильное . :-) Вот что они пытаются выразить:
Git не собирается показывать вам все коммиты. Он покажет некоторые выбранные подмножества коммитов.
Эточасть имеет смысл.Мы уже видим, что даже без упрощения истории: Git начинается с last commit, который мы указываем с именем ветви или с HEAD
или чем-то другим, а затем работает в обратном направлении, по одному коммиту за раз, помещаяболее чем один коммит за раз в свою очередь приоритетов, если и когда это необходимо.
С Упрощением истории, мы по-прежнему обходим график фиксации, используя очередь приоритетов, но для многих коммитов мы 'просто не собираюсь показывать коммит.Хорошо, пока - но теперь Git добавляет поворот, который заставил их написать этот странный абзац.
Если Git не собирается показывать вам все коммиты, возможно, он может чит и даже не потрудившись следовать некоторым вилкам.
Это сложная часть для выражения.Когда мы работаем в обратном направлении от ветви tip к корню коммит-графа, каждый коммит merge , в котором объединяются два потока коммитов, становится форком, в котором расходятся два потока коммитов.В частности, commit cc7285d
- это слияние, и когда у нас не происходит упрощение истории, Git всегда помещает обоих родителей в очередь.Но когда у нас do происходит упрощение истории, Git иногда не не помещает эти коммиты в очередь.
Действительно сложная часть здесь решает, какие коммиты попадают в очередь, и вот тут-то и появляется понятие "более подробное объяснение" и TREESAME . Я призываю людей прочитать его, потому что у него многополезной информации, но она очень плотно упакована и не очень хороша для , определяющего TREESAME в первую очередь.В документации это выглядит так:
Предположим, вы указали foo
в качестве .Мы будем вызывать коммиты, которые изменяют foo
! TREESAME, а остальные TREESAME.(В diff, отфильтрованном для foo
, они выглядят по-разному и равными соответственно.)
Это определение зависит от коммита, являющегося коммитом без слияния!
Все коммиты являются снимками (или, точнее, содержат снимков).Таким образом, ни один коммит, взятый сам по себе, не изменяет любой файл.Просто имеет файл или не имеет файла.Если у него есть файл, у него есть определенное содержимое для файла.Чтобы рассматривать фиксацию как изменение - как набор модификаций - нам нужно выбрать другой коммит, извлечь оба коммитов, а затем сравнить их.Для коммитов без слияния есть очевидный коммит: родитель.Учитывая некоторую цепочку коммитов:
...--F--G--H--...
, мы увидим, что изменило в коммите H
, извлекая как G
и H
, так и сравнивая их.Мы увидим, что изменило в G
, извлекая F
и G
и сравнивая их.Вот о чем здесь абзац TREESAME: мы берем, скажем, F
и G
и удаляем все, кроме файлов, которые вы просили.Затем мы сравниваем остальные файлы.Они одинаковы в урезанных F
и G
?Если это так, F
и G
являются TREESAME.Если нет, то это не так.
Но коммиты слияния, по определению, имеют по крайней мере двух родителей:
...--K
\
M
/
...--L
Если мы на коммите слияния M
, какого родителя мы выбираем, чтобы определить, что такое TREESAME, а что нет?
Ответ Git - сравнить коммит с всеми родителями, по одному за раз.Некоторые сравнения могут привести к «is TREESAME», а другие могут привести к «is TREESAME».Например, файл foo
в M
может совпадать с файлом foo
в K
и / или файлом foo
в L
.
То, какие коммиты использует Git, зависит от дополнительных опций, которые вы предоставляетедо git log
:
Режим по умолчанию
Коммиты включаются, если они не TREESAME для любого из родителей (хотя это можно изменить, см. --sparse
ниже).Если фиксация была слиянием и TREESAME для одного из родителей, следует только за этим родителем.(Даже если есть несколько родителей TREESAME, следуйте только за одним из них.) В противном случае, следуйте за всеми родителями.
Итак, давайте рассмотрим слияние cc7285d
и сравним его с каждым из его (двух) родителей:
$ git diff --name-status cc7285d^1 cc7285d
M z
$ git diff --name-status cc7285d^2 cc7285d
M x
M y
M z
Это означает, что git log
будет ходить только по первому родителю, фиксировать cc7285d^1
(что является dcaa916
) - это тот, который не делает't change x
:
... Если фиксация была слиянием и TREESAME для одного из родителей, следуйте только за этим родителем....
Итак, этот git log
проходит фиксацию cc7285d
, затем фиксацию dcaa916
, затем фиксацию a222cef
и затем останавливается.Он вообще никогда не смотрит на commit cc7285d^2
(то есть ad686b0
).
В остальной части этого раздела документации git log
описываются параметры --full-history
, --dense
, --sparse
,и --simplify-merges
(и даже я не понимаю истинной цели последнего варианта :-)).Из всех них --full-history
является наиболее очевидным и будет делать то, что вы хотите.(--ancestry-path
и --simplify-by-decoration
также являются этим разделом, но они не влияют на пути при слияниях.)
Предостережения
Хотя --full-history
будет следить за тем, чтобы Git прошел все "ноги "каждого слияния, git log -p
сам по умолчанию показывает нет diff для коммитов слияния.Вы должны добавить одну из трех опций - -c
, --cc
или -m
- чтобы git log -p
показывал любые различия вообще для любого слияния.
Если ваша цель специально найти bad слияние с двумя родителями, которое отбрасывает какое-то конкретное изменение, которое должно было быть сохранено, вы, вероятно, захотите показать разницу между этим слиянием по крайней мере с одним, а возможно и с двумя из его двух родителей.Команда git show
сделает это, но по умолчанию используется стиль --cc
.Команда git log
не сделает этого вообще.Если вы добавите --cc
к вашему git log
, вы получите тот же самый дифференциал, который git show
будет показывать по умолчанию - и это тоже не сработает.
--cc
или -c
Опции указывают Git, что, просматривая коммит слияния, Git должен анализировать коммит для всех родителей, а затем производить summary diff, а не детальный.Содержание резюме исключает части, которые соответствуют одному или всем родителям.Вы ищете слияние, которое случайно отбросило важное изменение - слияние, которое такое же , как и по крайней мере у одного из его родителей, когда оно должно отличаться от этого родителя.Этот комбинированный дифференциал собирается скрыть место, где изменение не является, но должно быть.Таким образом, вы не хотите -c
или --cc
.
Это оставляет параметр -m
.Когда git show
или git log
будет показывать diff, а коммит будет коммитом слияния, Git покажет один diff на каждого родителя .То есть для коммита слияния типа M
, git show -m
сначала сравнит K
с M
и покажет эту разницу.Затем он сравнит L
с M
и покажет другой дифференциал. Это вариант, который вы хотите здесь, для этого конкретного случая.
Обратите внимание, что -m
прекрасно сочетается с --first-parent
, чтобы показать только полный diff для первого родителя каждого слияния.Часто это в точности , что вы хотите.