Git исправить конфликт слияния, удалив файлы в master, это возможно? - PullRequest
0 голосов
/ 10 февраля 2020

У меня конфликт с мастером с одной из моих веток, есть один файл, который конфликтует. Могу ли я удалить файл в master и объединить с разветвлением, чтобы получить его обратно? Это исправит конфликт? Спасибо

1 Ответ

0 голосов
/ 10 февраля 2020

(Заметьте, но это важно: не думайте так много о ветвях , во-первых. Файлы находятся в коммитах . Git хранилищах коммитах . Git объединяет коммитов . Все сводится к коммитов , а имена веток, такие как master, просто служат для find коммитов.)

Если бы вы удалили файл, вы бы сделали это в коммите. (Этот коммит может содержаться или не содержаться в наборе коммитов, доступном по имени master; в том методе, о котором вы думаете, он будет в этом наборе комитов.) Но это не поможет: вы просто получил бы другой конфликт, в частности конфликт модификации / удаления. Ответ на конфликты никогда не бывает сложным «удалить файл здесь или там». Это всегда разрешить конфликт.

Вы должны начать с самого начала, а именно: Объединение - это объединение изменений . Это включает в себя поиск базы слияния коммит. Вы указываете Git объединить фиксацию - обычно путем присвоения имени ветви, как в git merge otherbranch, но это все еще указывает один конкретный c коммит. Git находит этот коммит базы слияния сам по себе как коммит # 1, использует ваш текущий коммит - конец вашей текущей ветви - как коммит # 2, и использует коммит, который вы назвали коммитом # 3.

Каждый коммит хранит полный и полный снимок всех ваших файлов, поэтому прежде чем Git сможет объединить изменений, Git сначала должен найти изменений. Это включает запуск git diff, который может сравнивать две фиксации. Он может сравнивать только два коммита! 1 Таким образом, база слияния, которую выбирает git merge, должна быть на обеих ветвях:

          I--J   <-- branch1 (HEAD)
         /
...--G--H
         \
          K--L   <-- branch2

Если вы используете branch1 и запускаете git merge branch2, то три коммита, которые Git будет использовать на этом этапе, будут H, J и L: J - ваш текущий коммит, на кончике branch1; L - это коммит в конце branch2, который вы объединяете; H - это лучший общий коммит , который находится на обеих ветвях, поэтому H является базой слияния.

Git теперь, по сути, выполняется :

git diff --find-renames <hash-of-H> <hash-of-J>   # what we changed on branch1
git diff --find-renames <hash-of-H> <hash-of-L>   # what they changed on branch2

Процесс слияния теперь прост: я sh собираю для каждого файла, который мы оба изменили, набор изменений, которые мы оба сделали, и объединяем эти изменения. Эти объединенные изменения затем применяются ко всему, что было в файле в коммите H - не к нашему коммиту J, ни к их коммиту L, а скорее к base commit! Таким образом, мы получаем оба набора изменений.

Конфликт возникает потому, что вы и они изменили одинаковые строки одного и того же файла. Возможно, вы и они также изменили разных строк этого же файла; Git сможет комбинировать их самостоятельно. Только когда ваши изменения и их изменения «соприкасаются» (перекрываются или примыкают), и вы и они не сделали точно такое же изменение , вы получаете конфликт.

Пока конфликт продолжается, Git дает вам доступ к всем трем исходным входным файлам. То, что вы видите в своем файле рабочего дерева, является его лучшей попыткой объединить изменения с маркерами конфликтов; но вы можете получить доступ к базовой копии файла слияния, вашей branch1 commit- J копии файла и их branch2 commit- L копии файла, если хотите. (Команда git mergetool делает такие вещи за вас, извлекая три копии файла и запуская выбранный вами инструмент слияния. Однако я не использую это: я обычно просто редактирую копию рабочего дерева напрямую.)

Если вы абсолютно уверены, что хотите отбросить все ваших изменений - даже тех, которые не вступают в конфликт с изменениями другой стороны - вы можете сделать это довольно легко на этом этапе, используя git checkout --theirs. Например, если конфликты есть в файле lib.py:

git checkout --theirs lib.py

, будет извлечена копия файла № 3 (из commit L) в копию рабочего дерева. Затем вы можете проверить его, убедиться, что он хорош, а затем использовать git add lib.py, чтобы отметить разрешенный конфликт.


1 Git имеет git diff, который может сравнивать три или более коммитов, производя то, что он называет комбинированным diff , но это не так здесь действительно важно: это не работает для слияний.


Что произойдет, если вы делаете удалите файл перед слиянием?

Предположим, вы продолжили план. Вы сделаете новый коммит в ветви branch1, в котором этот файл - давайте продолжим называть его lib.py для конкретности - не существует:

          I--J--N   <-- branch1 (HEAD)
         /
...--G--H
         \
          K--L   <-- branch2

Git теперь будет git diff commit H vs commit N чтобы увидеть, что вы изменили. Среди всех других изменений, которые он обнаружит, вы обнаружите, что вы полностью удалили lib.py. Тогда Git будет отличаться коммитом H против коммита L. Здесь он должен найти какое-то изменение в lib.py, потому что когда вы попытались выполнить слияние с коммитом J, у него возник конфликт, и он может возникнуть только в том случае, если вы и они внесли изменения в одни и те же строки этого файла. Таким образом, будут некоторые изменения, которые Git захочет применить с H против L.

. Вы получите конфликт modify / delete . Git может временно go с удалением или может оставить вас со своей версией lib.py от коммита L. Если Git удалил файл, теперь вам нужно git checkout --theirs lib.py, чтобы получить их. Если бы Git оставил их в вашем рабочем дереве, вам бы не пришлось ничего делать здесь - сэкономив вам здесь один шаг, но за счет дополнительного шага создания коммита N. Тогда вам нужно будет git add lib.py разрешить конфликт.

Вы только что проделали почти столько же работы! Почему бы просто не сделать это правильно, не делая дополнительного коммита?

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