В вашем конкретном случае на самом деле ничего не произошло. Перебазирование прошло успешно - и проделал скромный объем работы в этом процессе - но все, что он сделал, привело к в точности к исходным коммитам . Следовательно, последняя часть, когда говорится «1003 *», означает: Я изменил имя master
с идентификации коммита X на идентификацию коммита X (для некоторого хеш-идентификатора X ). По сути, оно стерло старую запись и заменило ее абсолютно, полностью на 100% идентичной записью.
Что здесь происходит (если вам интересно)
Причина для всего этого немного сложна. git rebase
делает, по сути, копирование коммитов. Проблема здесь заключается в том, что сделанный коммит вовремя замораживается: никакая часть существующего коммита не может быть изменена. Если мы сделали коммит, который плохой, или даже просто «не такой хороший», мы можем захотеть заменить его новым и улучшенным: нам может потребоваться извлечь оригинал в рабочую область, сделать внесите некоторые изменения в рабочую область и сделайте новый коммит, который в основном такой же, но немного другой.
Если мы сделаем это - если мы сделаем копию коммита с измененной частью (-ями) - мы получим новый и другой коммит с новым и другим хеш-идентификатором. И это приводит к загадке, поскольку имена веток Git запоминают только один идентификатор хеша. В частности, имя ветви запоминает хеш-идентификатор последнего коммита, который мы хотим назвать частью ветви.
Между тем каждый коммит также запоминает хэш-идентификатор. Точнее, каждый коммит запоминает ноль или более хеш-идентификаторов, но обычно ровно один. Обычный хэш-идентификатор, который запоминает коммит, - это хэш-идентификатор коммита, который приходит до этого конкретного коммита. Git называет это родительским коммита.
Обратите внимание, что когда дочерний коммит "рожден" (создан), Git знает хэш-идентификатор родителя ребенка (или, для коммитов слияния, родители, множественное число). Таким образом, Git может добавить хеш-идентификатор в потомка во время создания. Но как только ребенок выписан, он замерз на все времена. Поэтому, когда этот потомок сам становится родителем позже, к нему не могут быть добавлены его потомки. Он может помнить только своих родителей.
Но этого достаточно! Если мы нарисуем эту ситуацию, мы обнаружим, что коммиты образуют хорошую простую цепочку в обратном направлении:
... <-F <-G <-H
Здесь H
обозначает фактический хэш-идентификатор последнего коммита. Commit H
- самый ребенок; его родитель G
; и H
запоминает идентификатор G
. Коммит G
запоминает идентификатор F
, а F
запоминает другого родителя и т. Д. Вплоть до начала хранилища.
A имя ветки подобно master
, поэтому необходимо только запомнить идентификатор последнего коммита, в данном случае H
:
...--F--G--H <-- master
Чтобы добавить новый коммит, у нас есть Git, извлекающий последний master
один, то есть H
, в рабочую область. Затем мы работаем над этим и готовим новый коммит. Как только все будет готово (с git add
и так далее), мы запускаем git commit
. Теперь Git замораживает все, что мы сказали, чтобы сохранить для нового коммита, добавляет фактический хеш-идентификатор H
и записывает новый коммит, который получает новый, уникальный, большой уродливый хеш-код, который мы просто назовем I
...--F--G--H <-- master
\
I
последний шаг заключается в том, что git commit
записывает этот новый хэш-идентификатор в имя master
, так что master
теперь запоминает фиксацию I
вместо фиксации H
. Это нормально, потому что commit I
сам запоминает commit H
:
...--F--G--H
\
I <-- master
Если мы решим, что мы добавили коммит I
, мы на самом деле не можем изменить I
вообще, но мы можем скопировать в новую и улучшенную замену, возможно, один называется J
:
J
/
...--F--G--H
\
I <-- master
Если шТеперь мы заставляем Git сделать имя master
запомнить J
хеш-идентификатор, он выглядит как мы каким-то образом волшебным образом изменили I
, пока мы не обращаем внимания на хеш идентификаторы. (Git, напротив, уделяет очень строгое внимание на хеш-идентификаторы. Хеш-идентификаторы являются своего рода жизненной силой: именно так работает почти все внутри Git.)
При некоторых видах ребаз мы хотим скопировать серию коммитов, чтобы они попали в другое место в цепочке:
...--F--G--H <-- master
\
I--J <-- feature
Здесь, если мы хотим, чтобы наша функция строилась на коммите H
вместо коммита F
, нам придется повторно скопировать I
и J
, чтобы заставить их следовать после H
, и, вероятно, используйте немного другой исходный код, и тогда мы заставим Git скопировать имя feature
из J
и указать его на новые копии:
I'-J' <-- feature
/
...--F--G--H <-- master
\
I--J [abandoned]
В других случаях мы просто хотим сделать небольшое исправление. Например, мы начинаем с:
...--F--G--H <-- master
\
I--J <-- feature
но решите, что мы хотим изменить сообщение журнала (reword
в git rebase -i
). Это сделает новый и улучшенный I'
, как и раньше. Если все, что мы делаем, это reword
, ну, J
было хорошо, но родитель J
- I
, поэтому нам нужен новый и улучшенный J'
, чей родитель - копия I'
:
I'-J' <-- feature
/
...--F--G--H <-- master
\
I--J [abandoned]
Умная часть git rebase
заключается в том, что, если вы не скажете этого не делать - например, с помощью --force
, он заметит случай, когда на самом деле вообще нет изменений для принятия I
и просто используйте исходный коммит I
. Это происходит, если:
- нет изменений ни в одном источнике;
- нет изменений имени автора, сообщения журнала и т. Д .; и
- нет изменений в родительском хэш-идентификаторе.
Так было и с вашей перебазировкой: вы сказали не вносите никаких изменений и родительские хеш-идентификаторы также были одинаковыми, поэтому Git просто оставил все три коммита на месте, тщательно определив что это нормально делать. 1
Затем, как Git всегда делает после завершения операции rebase, Git вставил идентификатор хэша последнего скопированного коммита в имя ветви, так что имя записывает последний коммит в ветви. Это была updated
часть.
1 Из-за опции --force
, git rebase
действительно проверяет это. Если вы сказали rebase, что он абсолютно должен заменить коммиты, он внесет тривиальное изменение - например, обновит дату автора - так, чтобы новый коммит имел новый и другой хэш-идентификатор.
Команда git filter-branch
, которая в остальном похожа на git rebase
на стероидах - она копирует основные полосы коммитов при внесении в них произвольных изменений на основе аргументов фильтра - не не выполняет никаких таких проверок. Он полагается вместо того факта, что если вы делаете абсолютно, полностью, 100% -ный битовый идентичный коммит, который соответствует некоторому предыдущему существующему коммиту, вы фактически получаете оригинальный хэш-идентификатор обратно и не сохраняете новый объект в базе данных. Если бы у git rebase
не было --force
, он, вероятно, просто сделал бы это, а не проверял. С git filter-branch
, если вы хотите принудительно скопировать копию, вы должны организовать это в одном из ваших фильтров.
Обратите внимание, что требование соответствия 100% означает, что новый коммит должен иметь:
- то же дерево исходных текстов
- одни и те же имена авторов и коммиттеров и даты / время
- одно и то же сообщение журнала, включая точное написание каждого символа (включая пробелы) и ту же кодировку (UTF-8 или что-то еще)
- те же самые родительские хеш-идентификаторы, то есть та же самая история, приведшая к этому коммиту
и если фиксация так близко совпадает, то является оригинальной фиксацией, а не измененной копией. Так что для Git правильно использовать здесь оригинал.