Почему я получаю этот конфликт слияния с git rebase интерактивным? - PullRequest
0 голосов
/ 22 февраля 2019

Я все еще изучаю git.

У меня есть файл с именем names.txt.С этим текстом.

enter image description here

Это моя история коммитов

enter image description here

Первый коммит добавил файл.Второй коммит добавил первую строчку Мэри.Третий коммит добавил вторую строку Джон.

git show 7bdb5ef

enter image description here

git show 80384aa

enter image description here

Я хочу перебазировать это и отредактировать коммит Мэри, чтобы изменить текст на Mary Shelly.

I do git rebase -i 4a5244b

Затем я установлю коммит Мэри для редактирования и запуска перебазирования.

enter image description here

Здесь перебазирование останавливается.

enter image description here

Теперь name.txt имеет значение при коммите Mary.enter image description here

Я изменяю его на Mary Shelly и ставлю.

Я запускаю

git commit --amend 

, а затем

git rebase --continue

Теперь я получил этот конфликт слияния.

enter image description here

enter image description here

Не знаюЯ не понимаю, почему это происходит.Commit John только изменяет вторую строку в файле.Когда мы редактируем коммит Mary, мы меняем только первую строку файла.Как это вызывает конфликт?

Ответы [ 3 ]

0 голосов
/ 22 февраля 2019

Проблема в том, что изменение, которое вы внесли в исходную строку, на практике слишком вероятно, что также потребуется в добавленной смежной строке, чтобы слияние могло быть успешно выполнено без участия человеческого суждения.В качестве примера я использую

<<<<<<<<<<<<<
    if ( g->tag == mark 
      || g->tag == error ) {
||||||||||||||
    if ( tag == mark
      || tag == error ) {
==============
    if ( tag == mark 
      || tag == release
      || tag == error ) {
>>>>>>>>>>>>>>

, где одно изменение добавило g-> к паре линий, а другое изменение добавило линию release в середине.

0 голосов
/ 22 февраля 2019

Проблема в том, что означает конфликт слияния, а комментарий Чепнера является ключом к пониманию причины.Что ж, и граф коммитов , плюс тот факт, что git rebase состоит из повторяющихся git cherry-pick операций.Интерактивная перебазировка позволяет вам добавлять свои собственные команды между git cherry-pick или даже менять черри-пики на что-то еще.(Первоначальный лист команд начинается с всех команд pick, каждая из которых означает выбор вишни .)

Ваша история коммитов является сводкой вашего графа коммитов.по сути, результат посещения каждого коммита в графе коммитов, начиная с некоторой конкретной конечной точки (кончик вашей текущей ветви) и работая в обратном направлении.Если вы используете git log --graph, вы получите потенциально важную информацию, которая не указана без --graph, хотя в этом конкретном случае легко увидеть, что график является линейным.Итак, у вас есть всего три коммита:

A <-B <-C   <-- master (HEAD)

, где A на самом деле 4a5244b, B означает 7bdb5ef, а C означает 80384aa (если я переписализображения правильно).Каждый коммит имеет полную, полную копию файла names.txt.Копия, конечно, отличается в коммитах A, B и C, в том, что в A она пуста;в B это чтение одной строки Mary;и в C это две строки, читающие Mary, а затем John

Сам график возникает из-за того, что commit C, или 80384aa, содержит идентификатор хеша commit B или 7bdb5ef, внутри C самой .Вот почему я нарисовал стрелку из C, указывающую на B.Git вызывает этот C родительский коммит.Git записывает хэш-идентификатор C в имя master, а затем присоединяет специальное имя HEAD к имени master, чтобы он знал, что именно здесь git log следуетstart, и этот коммит C тот, который у вас есть, для работы прямо сейчас.

Когда вы запускаете git rebase -i 4a5244b - выбираете коммит A в качестве новой базы - Git выясняет, чтоэто означает copy commit B и C, поэтому он помещает их хэш-идентификаторы в список pick команд.Затем он открывает ваш редактор на листе команд.Вы изменяете pick на edit, что говорит Git: Сделайте выбор вишни, а затем выйдите из перебазирования, в середине операции.

Вы не заставить выполнить перебазирование, чтобы сделать истинную копию.(Чтобы сделать это, используйте -f или --no-ff или --force-rebase - все это означает одно и то же. Это не имеет значения здесь, ни в большинстве случаев.) Так что Git увидел, что была инструкция, Скопируйте B, чтобы он шел после A, и понял: Эй, подождите, B уже после A.Я просто оставлю это там. Git сделал это и остановился, оставив вас в этом состоянии:

A--B   <-- HEAD
    \
     C   <-- master

Обратите внимание, что HEAD больше не привязан к master: теперь он указывает напрямуюсовершить B.Коммит C остается, а master по-прежнему указывает на него, но Git остановился в режиме «detached HEAD», чтобы позволить вам выполнить редактирование.

Вы вносите изменения в файл, git addи git commit --amend.Это делает новый коммит - мы могли бы назвать его B' или D, и обычно я использую B', поскольку обычно он очень похож на B, но на этот раз он достаточно отличается, поэтомудавайте использовать D.Новый коммит имеет A в качестве родителя - это то, что делает --amend.Git обновляет HEAD, чтобы указать на новый коммит.Существующий коммит B остается без изменений.Итак, теперь у вас есть:

  D   <-- HEAD
 /
A--B
    \
     C   <-- master

Файл names.txt в D имеет новое однострочное чтение Mary Shelly.

Теперь вы запускаете git rebase --continue, поэтому Git продолжает счто осталось в инструкцииОн состоит из pick <hash-of-C>, что заставляет Git запускать git cherry-pick для копирования C.Эта копия должна идти после текущей фиксации, D.Существующий коммит C этого не делает, поэтому Git действительно должен выполнить эту работу на этот раз.

Вишня - это слияние - слияние как глагол, по крайней мере

To perform операция слияния - для слияния , действие - Git требуется три входа.Эти три входа: слияние, , текущий или --ours (также иногда называемый локальный , в частности, git mergetool) и другой или --theirs (иногда называется remote ).Для регулярных слияний база часто немного удалена: именно там две линии коммитов расходятся.Для вишни - и для возврата, в этом отношении - база находится прямо рядом с коммитом.Основой слияния этой операции является родительский коммит C B!

Фактическая операция слияния состоит в запуске двух команд git diff для всех коммитов:

  • git diff --find-renames <em>hash-of-base</em> <em>hash-of-ours</em>: что мы изменили?
  • git diff --find-renames <em>hash-of-base</em> <em>hash-of-theirs</em>: что они изменили?

Так что теперь Git делает diffs commit B, base, vs commit D, ваш текущий / наш коммит.Этот diff влияет на файл names.txt и говорит: измените одну строку, в которой написано Мэри, на две строки: одну, читающую Мэри Шелли, и одну, читающую Джона. Затем Git diff * B vs C, чтобы увидетьчто "они" (вы, ранее) сделали.Разница влияет на файл names.txt и говорит: добавьте строку, читающую John в конце файла, после строки, читающей Mary.

Вот что Git показывает вам в конфликте слиянияраздел: в одном файле написано замените Мэри на Мэри Шелли , в другом - оставьте Мэри и добавьте Джона .Если хотите, вы можете сказать Git оставить в разделе конфликта слияния больше информации.Для этого установите diff.conflictStyle на diff3.(По умолчанию, если он не установлен, merge.)

С настройкой diff3 вы увидите, что содержимое base - помечено ||||||| -одна строка Mary, и что два файла из конфликтующих коммитов заменили эту базу соответственно Mary Shelly или Mary + новой строкой John.Я нахожу этот тип конфликта слияния более понятным и более простым для слияния вручную.

В любом случае, ваша задача на данный момент состоит в том, чтобы найти правильный результат - что бы это ни было - и записать его и скопировать виндексный слот ноль.Обычно вы просто редактируете грязный names.txt, оставленный Git в вашем рабочем дереве, помещаете в него нужное содержимое и затем запускаете git add names.txt.

Возобновление

Исправивконфликта, запустите git <em>whatever</em> --continue, чтобы возобновить любую остановленную операцию - в этом случае, перебазировать, но это также происходит с cherry-pick и слиянием.Git будет использовать содержимое индекса, которое вы обновили с помощью git add, чтобы сделать новый коммит, который является копией C:

  D--C'   <-- HEAD
 /
A--B
    \
     C   <-- master

Достигнув конца листа команд, git rebase сейчасВ завершение вынимаете имя master off commit C и вставляете его в C', который является последней сделанной копией, а затем снова присоединяете HEAD:

  D--C'   <-- master (HEAD)
 /
A--B
    \
     C   [abandoned]
0 голосов
/ 22 февраля 2019

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

Здесь повторное применение фиксации John вызывает проблемы: добавлен исходный коммит John рядом со строкой Mary.Теперь Git пытается повторно применить коммит, но эта ссылочная строка с надписью Mary больше не существует - все, что есть строка с надписью Mary Shelly ... имейте в виду, что Git не понимает цели и / или значениявашего файла, поэтому в таком случае он не будет рисковать и представить это вам как конфликт, чтобы вы могли проверить его.

Попробуйте повторить то же самое с множеством других строк междуJohn и Mary, которые вы сохраните прежними - вы увидите, что у вас не возникнет конфликта.

...