Почему git видит мой коммит, но не применяет изменения к базе кода? - PullRequest
2 голосов
/ 27 марта 2019

У меня есть репозиторий GitHub с коммитами, которые появляются в журнале и отображаются в приложении GitHub Desktop, но изменения в коммитах не применяются к коду (или не видны в Visual Studio с обвинениями).

Указанные коммиты являются частью слияния веток, которое произошло месяц назад. Мы слили мастер в ветвь, затем слили ветвь обратно в мастер. Фиксация слияния "master" в "branch" "показывает все изменения слияния. Некоторые из этих изменений не применяются к коду, и Visual Studio заявляет, что затронутые страницы не обновлялись с начала прошлого года. Может ли это быть как-то связано с удалением ветки после слияния с мастером?

Мы просмотрели все коммиты после слияния, и ни один из них не изменил код на страницах, затронутых этой проблемой. Страницы не были переименованы, удалены или перемещены.

Мы попытались отменить слияние, чтобы увидеть, что произойдет, и большинство коммитов исчезло в GitHub Desktop (от 58 измененных файлов до 8 измененных файлов), применяя только несколько изменений после слияния. Мы также попытались выбрать коммиты из этого слияния, и они были применены правильно. Поскольку мы можем сделать это, чтобы решить проблему, нас больше интересует, почему коммиты не применяются к связанным файлам кода.

Обновление: Мы попытались выполнить git checkout <sha1> для коммитов после коммита слияния и обнаружили, что код отличается (3 коммита позже - также слияние), но в этих коммитах нет ссылок на файлы кода.

1 Ответ

1 голос
/ 28 марта 2019

Стоит отметить, что коммиты не содержат и не содержат изменений .Коммиты содержат полных снимков .Зафиксировать зрителей намеренно лгать вам (а вы обычно хотите сделать это), чтобы показать фиксацию как набор изменений.

Механизм, стоящий за этим, важен.Фиксация - это моментальный снимок - например, отчет о погоде, в котором говорится, что в настоящее время температура составляет 20 ° C, - и вы хотите узнать, насколько отличается сейчас.Вы должны выбрать другой коммит - например, «вчера» - для , когда вы хотите сравнение.Тогда, если вчера было 19 ° C, а сегодня 20 ° C, разница заключается в том, что на 1 ° C теплее.Но чтобы получить это, вы должны были выбрать предыдущий день для сравнения.

Каждый коммит в Git уникально идентифицируется по его хэш-идентификатору.Идентификатор хэша - это то, как Git может получить все файлы, которые находятся в моментальном снимке, и метаданные о коммите, например, кто его сделал, когда и почему (сообщение журнала).Одним из элементов, сохраняемых в каждом коммите, является хэш-идентификатор предыдущего коммита.Это то, что средства просмотра коммитов Git используют для построения различий.

Средство просмотра коммитов не покажет вам каждую строку каждого файла в коммите с хешем H .Вместо этого он найдет предшественника коммита - его parent , в терминах Git.У этого родителя есть какой-то другой хэш G .Зритель извлекает и фиксирует, сравнивает их и сообщает вам: Между G и H эти файлы разные, с этими изменениями в этих строках. Обычно это намного короче - и намного большеполезнее - чем , вот полный снимок в H .

Но это разбивается при слияниях.Если мы нарисуем хороший линейный набор коммитов:

... <-F <-G <-H   <-- you-are-here

(стрелки указывают назад, потому что каждый коммит записывает своего родителя; родители не помнят своих детей), легко сравнить G против H,Но в итоге вы объединяете две линии развития:

       o--...--o--K
      /            \
...--*              M   <-- mainline
      \            /
       o--o--...--L   <-- branch

Основная линия в какой-то момент разделяется, и развиваются два разных человека или группы.Затем мы - или кто-то, в любом случае - использовали git checkout mainline; git merge branch и прошли через весь страшный 1 и магический 2 процесс слияния , который привел к этому коммит слияния M.

Фиксация M, как и любой другой коммит, состоит из моментального снимка и некоторых метаданных.Снимок похож на любой другой снимок.Единственная особенность M в том, что в своих метаданных он не просто указывает коммит K в качестве родителя.Вместо этого он перечисляет и коммитов - K и L - как его двух родителей.


1 Это на самом деле не страшно.

2 Это тоже не волшебство;см. ниже.


Как работает автоматическое слияние Git

Давайте кратко рассмотрим git merge и конфликты слияния .Если нет конфликтов, Git сам выполняет слияние.Обычно такие случаи не приводят к такого рода затруднениям, поэтому давайте посмотрим, что происходит, когда является конфликтом.

Чтобы начать слияние, Git просто сравнивает * против K- так же, как Git всегда сравнивает любую простую пару коммитов - чтобы выяснить, что же отличается.Затем Git сравнивает * -vs- L, чтобы выяснить, в чем дело.Затем Git объединяет два набора изменений .Это для слияния , или то, что я люблю называть слияние как глагол , часть процесса слияния.Объединенные изменения должны применяться к моментальному снимку в коммите *.

Помните, что каждый коммт держит снимок.В коммите * все файлы находятся в том состоянии, в котором они находились во время создания *.В коммите K все файлы находятся в каком-то другом состоянии, а в коммите L все файлы находятся в третьем состоянии.Могут даже быть файлы в K, которых нет в *, и / или в L, которых нет в * и т. Д., Но обычно большинство файлов находятся в основном во всех трех входах.

Предположим, «мы» означает людей, которые работали по линии K, а «они» означает людей, которые работали по линии L.Мы изменили файлы A, B и C. Измененные файлы B, C и D. Затем Git просто берет все наши изменения в A и все их изменения в D. Эта часть проста, потому что мы не касались D иони не касались A. Эта часть слияния выполнена.

Теперь Git выясняет, какие строки мы изменили в файле B, а какие строки они изменены в одном файле.Если наши строки вообще не перекрывают свои строки - обратите внимание, что Git рассматривает «просто касание» как иногда перекрывающееся - тогда Git может просто применить оба изменения к файлу B из коммита *.Эта часть слияния уже выполнена.

Git выясняет, какие строки мы изменили в C, и какие строки они изменили.Ох, на этот раз мы оба изменили одинаковые строки.Git записывает в рабочее дерево комбинацию изменений с маркерами конфликта и объявляет объединение конфликтным .

с момента объединения конфликтует, Git останавливается и получает помощь от человека, который выполняет слияние.Это их работа, чтобы исправить это.Есть много способов исправить это, но все они заканчиваются одинаково: тот, кто делает исправление, записывает правильную версию файла C в рабочее дерево и запускает git add C, чтобы сообщитьGit: это правильный результат.

Git не проверяет, что они написали, он просто берет все, что они положили в окончательный файл.Если они полностью все испортили, например, полностью выбросив ваш код, Git с этим согласен!Git предполагает, что они знают, что делают.

Теперь они запускают git commit или git merge --continue, а Git использует завершенный снимок слияния для создания коммита слияния M, который выглядит следующим образоммы его нарисовали.

Вернемся к вашей проблеме

Итак, давайте вернемся к нашему средству просмотра коммитов.Вы просите его просмотреть коммит M.Он показывает вам метаданные как обычно - имя того, кто сделал коммит, и так далее.Он может показывать или не отображать оба родительских хеш-идентификатора, в зависимости от программы просмотра.Вероятно, он показывает сообщение в журнале, которое человек, который запустил git merge, использовал для записи , почему они выполнили слияние и сохранили все важные заметки.Если бы этот человек был очень усердным, сообщение в журнале могло бы быть даже полезным ... но, увы, большинство людей используют автоматически сгенерированное, в основном бесполезное сообщение журнала: "объединить ветвь ...".

Теперь вашзритель должен показать, что вы хотите изменить в этом коммите.Но теперь есть проблема.Чтобы показать, что изменило , зритель должен посмотреть на родительский коммит и сравнить.Нет одного родителя.Есть два родителей.Какой из них будет использовать зритель?

Фактический ответ здесь зависит от зрителя.Некоторые зрители просто сдаются и показывают вам ничего .Например, git log -p делает именно это.Похоже, вы можете использовать этот вид зрителя.Другой зритель, который запускает git show, пытается быть полезным: он фактически сравнивает слияние M с обоими родителями, K и L.Но, увы, этот зритель пытается быть слишком полезным.Он касается мест, где слияние могло иметь конфликты слияния , так что он не отображает любые файлы, где файл в M точно соответствует или один в K или один в L.

Если человек, который произвел слияние , сделал это неправильно путем выброса некоторых изменений файла, которые должны были в M, этот вид зрителя также будет выбросить эти изменения с дисплея. В этом случае файл C точно соответствует их копии из commit L. Так что git show, как средство просмотра слияния, не покажет вам файл C .

(Использование git log в качестве средства просмотра коммитов, конечно, еще хуже: оно не показывает ни A, B, C, ни D, даже если это четыре файла, в которых произошли некоторые изменения.)

Вы можете поручить git loggit show) разбить коммит слияния на два виртуальных коммита. То есть дано:

...--K
      \
       M
      /
...--L

вы можете получить их притвориться , что они имеют:

...--K--M1

...--L--M2

и покажет вам сначала K -vs- M1, затем L -vs- M2. Это часто несколько полезно для этих случаев. Для этого добавьте -m к git log или git show. (Обратите внимание, что M1 и M2 никогда не заходят в репозиторий, они просто делают предварительные коммиты во время части "покажи мне разницу" при просмотре коммит-слияния.)

Суть как бы

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

Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...