git merge: применить изменения к коду, который перемещен в другой файл - PullRequest
108 голосов
/ 16 августа 2010

Я сейчас пытаюсь сделать довольно хитрый маневр слияния. Одна проблема, с которой я сталкиваюсь, заключается в том, что я внес некоторые изменения в некоторый код в своей ветке, но мой коллега переместил этот код в новый файл в своей ветке. Поэтому, когда я сделал git merge my_branch his_branch, git не заметил, что код в новом файле был таким же, как старый, и поэтому никаких моих изменений не было.

Какой самый простой способ применить мои изменения снова к коду в новых файлах. У меня не будет слишком много проблем, чтобы выяснить, какие коммиты нужно применить повторно (я могу просто использовать git log --stat). Но, насколько я могу судить, нет способа заставить git повторно применить изменения в новых файлах. Самая простая вещь, которую я вижу сейчас, - это вручную применить изменения, что не выглядит хорошей идеей.

Я знаю, что git распознает BLOB-объекты, а не файлы, поэтому, безусловно, должен быть способ сказать ему: «применить это точное изменение кода из этого коммита, кроме того, где оно было, но где оно сейчас находится в этом новом файле».

Ответы [ 3 ]

109 голосов
/ 28 февраля 2013

У меня была похожая проблема, и я решил ее, изменив работу, чтобы она соответствовала целевой файловой организации.

Скажем, что вы изменили original.txt в своей ветви (ветка local), но в основной ветви original.txt была скопирована в другую, скажем, copy.txt.Эта копия была сделана в коммите, который мы называем коммитом CP.

. Вы хотите применить все свои локальные изменения, коммиты A и B ниже, сделанные на original.txt, дляновый файл copy.txt.

 ---- X -----CP------ (master)
       \ 
        \--A---B--- (local)

Создайте одноразовую ветвь move в начальной точке ваших изменений с помощью git branch move X.То есть, поместите ветвь move в коммит X, тот, что раньше коммитов, которые вы хотите объединить;скорее всего, это коммит, из которого вы разветвились для реализации ваших изменений.Как писал пользователь @ digory doo , вы можете git merge-base master local найти X.

 ---- X (move)-----CP----- (master)
       \ 
        \--A---B--- (local)

В этой ветке введите следующую команду переименования:

git mv original.txt copy.txt

Переименовывает файл.Обратите внимание, что copy.txt еще не существовало в вашем дереве на данный момент.
Зафиксируйте ваше изменение (мы называем это коммитом MV).

        /--MV (move)
       /
 ---- X -----CP----- (master)
       \ 
        \--A---B--- (local)

Теперь вы можете перебазировать свою работу поверхmove:

git rebase move local

Это должно работать без проблем, и ваши изменения будут применены к copy.txt в вашем местном филиале.

        /--MV (move)---A'---B'--- (local)
       /
 ---- X -----CP----- (master)

Теперь вы не обязательно хотите илинеобходимо иметь коммит MV в истории вашей основной ветки, потому что операция перемещения может привести к конфликту с операцией копирования при коммите CP в основной ветке.

Вам нужно только перебазировать свою работу снова, отбрасывая операцию перемещения, следующим образом:

git rebase move local --onto CP

... где CP - коммит, где copy.txt было введено в другой ветке.Это отменяет все изменения в copy.txt поверх фиксации CP.Теперь ваша ветвь local точно такая же, как если бы вы всегда изменяли copy.txt, а не original.txt, и вы можете продолжать слияние с другими.

                /--A''---B''-- (local)
               /
 -----X-------CP----- (master)

Важно, чтобы изменения были применены к CP или иначе copy.txt не будет существовать, и изменения будут применены обратно к original.txt.

Надеюсь, это понятно.Этот ответ приходит поздно, но это может быть полезно для кого-то еще.

27 голосов
/ 16 августа 2010

Вы всегда можете использовать git diff (или git format-patch) для генерации патча, затем вручную отредактируйте имена файлов в патче и примените его с помощью git apply (или git am).

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

17 голосов
/ 27 августа 2013

Вот слияние решение проблемы конфликта слияния с переименованием и редактированием и разрешения его с помощью mergetool, распознающего правильные 3 исходных файла слияния.

  • Послесбой слияния из-за «удаленного файла», который, как вы понимаете, был переименован и отредактирован:

    1. Вы прервали слияние.
    2. Зафиксируйте переименованные файлы в своей ветви.
    3. Иснова объединить.

Краткое руководство:

Создать файл .txt:

$ git init
Initialized empty Git repository in /tmp/git-rename-and-modify-test/.git/

$ echo "A file." > file.txt
$ git add file.txt
$ git commit -am "file.txt added."
[master (root-commit) 401b10d] file.txt added.
 1 file changed, 1 insertion(+)
 create mode 100644 file.txt

Создать ветку, в которой вы будете редактироватьпозже:

$ git branch branch-with-edits
Branch branch-with-edits set up to track local branch master.

Создайте переименование и отредактируйте на мастере:

$ git mv file.txt renamed-and-edited.txt
$ echo "edits on master" >> renamed-and-edited.txt 
$ git commit -am "file.txt + edits -> renamed-and-edited.txt."
[master def790f] file.txt + edits -> renamed-and-edited.txt.
 2 files changed, 2 insertions(+), 1 deletion(-)
 delete mode 100644 file.txt
 create mode 100644 renamed-and-edited.txt

Поменяйте местами на ветку и отредактируйте там тоже:

$ git checkout branch-with-edits 
Switched to branch 'branch-with-edits'
Your branch is behind 'master' by 1 commit, and can be fast-forwarded.
  (use "git pull" to update your local branch)
$ 
$ echo "edits on branch" >> file.txt 
$ git commit -am "file.txt edited on branch."
[branch-with-edits 2c4760e] file.txt edited on branch.
 1 file changed, 1 insertion(+)

Попытка слияния мастера:

$ git merge master
CONFLICT (modify/delete): file.txt deleted in master and modified in HEAD. Version HEAD of file.txt left in tree.
Automatic merge failed; fix conflicts and then commit the result.

Обратите внимание, что конфликт трудно разрешить - и файлы были переименованы.Прервать, имитировать переименование:

$ git merge --abort
$ git mv file.txt renamed-and-edited.txt
$ git commit -am "Preparing for merge; Human noticed renames files were edited."
[branch-with-edits ca506da] Preparing for merge; Human noticed renames files were edited.
 1 file changed, 0 insertions(+), 0 deletions(-)
 rename file.txt => renamed-and-edited.txt (100%)

Попробуйте объединить снова:

$ git merge master
Auto-merging renamed-and-edited.txt
CONFLICT (add/add): Merge conflict in renamed-and-edited.txt
Recorded preimage for 'renamed-and-edited.txt'
Automatic merge failed; fix conflicts and then commit the result.

Отлично!В результате слияния возникает «нормальный» конфликт, который можно разрешить с помощью mergetool:

$ git mergetool
Merging:
renamed-and-edited.txt

Normal merge conflict for 'renamed-and-edited.txt':
  {local}: created file
  {remote}: created file
$ git commit 
Recorded resolution for 'renamed-and-edited.txt'.
[branch-with-edits 2264483] Merge branch 'master' into branch-with-edits
Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...