Всегда ли отображается сообщение «Успешно перебазированы и обновлены ссылки / главы / мастера»? - PullRequest
0 голосов
/ 06 июля 2019

Я работал в основной ветке и попробовал это git rebase -i HEAD~3. Я действительно пытался удалить коммит из истории коммитов, но понял, что в этом нет необходимости.

После открытия редактора nano я не внес никаких изменений в файл и закрыл его без сохранения. Однако в командной строке появилось сообщение о том, что: Msgstr "Успешно перебазированы и обновлены ссылки / главы / мастера". Что это значит? Я не сделал (насколько я знаю) никаких изменений. Когда я посмотрел на git log и git status, я не увидел никаких изменений.

После этого я сделал пару коммитов и перенес их на пульт.

Мой вопрос: почему было отображено сообщение? Что-нибудь изменилось на самом деле?

Я спрашиваю об этом, потому что это на самом деле общий проект, и я собирался совершить серьезную ошибку, пытаясь что-то упрекнуть, и теперь я волнуюсь, что мог бы это сделать. Как вы, вероятно, можете сказать, я довольно n00b с git:)

Спасибо за вашу помощь!

1 Ответ

1 голос
/ 06 июля 2019

В вашем конкретном случае на самом деле ничего не произошло. Перебазирование прошло успешно - и проделал скромный объем работы в этом процессе - но все, что он сделал, привело к в точности к исходным коммитам . Следовательно, последняя часть, когда говорится «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 правильно использовать здесь оригинал.

...