git merge:将更改应用于移至其他文件的代码


132

我现在正在尝试一个非常强大的git merge动作。我遇到的一个问题是我对分支中的某些代码进行了一些更改,但是我的同事将该代码移到了他的分支中的新文件中。因此,当我这样做时git merge my_branch his_branch,git并未注意到新文件中的代码与旧文件中的代码相同,因此没有任何更改。

将更改再次应用于新文件中的代码的最简单方法是什么。我不会发现太多需要重新应用的提交的问题(我可以使用git log --stat)。但是据我所知,没有办法让git将更改重新应用到新文件中。我现在看到的最简单的事情是手动重新应用更改,这似乎不是一个好主意。

我知道git可以识别blob,而不是文件,因此,肯定有一种方法可以告诉它,“从此提交中应用此确切的代码更改,除非不是在新文件中,而是在现在的位置”。


2
不完全一样,但这里是有良好的anwsers类似的问题可能适用的:stackoverflow.com/questions/2701790/...
马里亚诺Desanze

4
没有一个答案能说明为什么 git无法自动执行这样的合并。我认为它应该足够聪明以检测重命名并自动执行适当的合并?
约翰

提出问题时,可能并非如此,但是现代版本的git(我使用的是1.9.5)可以将更改合并到重命名和移动的文件中。甚至有一个--rename-threshold选项可以调整所需的相似度。
托德·欧文

1
@ToddOwen如果在上游分支基础上进行基础变迁会起作用,但是如果您选择或向后移植一系列可能不包括重命名文件的提交的更改,仍然会遇到麻烦。
GuyPaddock

如果您首先重新设置基准,这是/根本不是问题吗?
hbogert

Answers:


132

我遇到了类似的问题,我通过根据工作重新调整以匹配目标文件组织来解决了该问题。

假设您original.txt在分支(local分支)上进行了修改,但在master分支上original.txt已将其复制到另一个分支,例如copy.txt。此副本已在我们称为commit的提交中完成CP

您想将所做的所有本地更改,提交AB以下更改应用于original.txt新文件copy.txt

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

使用move进行更改的起点创建一个一次性分支git branch move X。也就是说,将move分支置于commit X,这是您要合并的commit之前的分支;这很可能是您从中提交以实施更改的提交。如用户@digory doo在下面所述,您可以执行git merge-base master localfind X

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

在此分支上,发出以下重命名命令:

git mv original.txt copy.txt

这将重命名文件。请注意,copy.txt此时您的树中尚不存在该文件。
提交您的更改(我们将其命名为commit 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

...哪里CPcopy.txt在另一个分支中引入的提交。这将所有更改基于提交copy.txt之上CP。现在,您的local分支完全就像您始终进行修改copy.txt而不是一样original.txt,并且您可以继续与他人合并。

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

重要的是要对更改应用CP或否则copy.txt将不存在,而对更改应重新应用original.txt

希望这很清楚。这个答案来晚了,但这可能对其他人有用。


2
这项工作很多,但我认为原则上应该可行。我认为与合并相比,合并会带来更好的运气。
asmeurer

7
该解决方案仅涉及基本的git命令,与编辑补丁或使用patch(这可能涉及很多工作)不同,这就是为什么我认为展示它可能很有趣。另外,请注意,就我而言,与实际应用更改相比,我花了更多时间来编写答案。您会在哪一步使用合并命令?为什么呢?我看到的唯一区别是,通过重新基准化,我做出了一个临时提交,该提交后来被丢弃(commit MV),这仅对于合并是不可能的。
coredump

1
使用rebase,您有更高的机会处理合并冲突,因为您要处理每个提交,而使用合并时,您可以一次处理所有内容,这意味着使用rebase可能不会发生的某些更改。我要做的是合并,然后手动移动合并的文件。不过,也许我误解了您的答案。
asmeurer 2013年

4
我添加了一些ASCII树来阐明该方法。在这种情况下,要进行合并与重新设置:我想做的是在分支中的“ original.txt”上进行所有更改,然后将其应用到master分支中的“ copy.txt”,因为某些原因,“原始”。 txt”已复制(但未移动)到“ copy.txt”。该副本之后,“ original.txt”也可能在master分支上演变了。如果直接合并,则对original.txt进行的本地更改将应用​​于主分支中修改后的original.txt,这将很难合并。问候。
coredump

1
无论哪种方式,我都相信该解决方案会奏效(尽管幸运的是我暂时没有尝试这种情况),所以现在我将其标记为答案。
asmeurer 2013年

31

您可以随时使用git diff(或git format-patch)生成补丁,然后再在补丁手动编辑的文件名,并与应用它git apply(或git am)。

缺少这一点,它将自动工作的唯一方法是,如果git的重命名检测可以弄清楚旧文件和新文件是同一件事-听起来好像不是您的情况,只是其中的一部分。的确git使用的是Blob,而不是文件,但是Blob只是整个文件的内容,没有附加文件名和元数据。因此,如果您在两个文件之间移动了一段代码,那么它们并不是真正相同的blob-其余blob的内容是不同的,只是该块相同。


1
好吧,这是迄今为止最好的答案。我不知道如何进行git format-patch提交工作。如果我这样做git format-patch SHA1,它将为整个历史记录生成一堆补丁文件。但是我想git show SHA1 > diff.patch也会一样。
asmeurer 2010年

1
@asmeurer:使用该-1选项。格式补丁程序的正常操作模式是修订范围,例如origin/master..master,因此您可以轻松准备补丁程序系列。
卡斯卡贝尔

1
其实,另外一个音符。 git apply而且git am太挑剔了,因为他们想要相同的行号。但是我目前使用UNIX patch命令成功。
asmeurer 2010年

23

这是一个合并解决方案,遇到重命名和编辑合并冲突,并使用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.

创建重命名并在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

有趣。下次遇到此问题时,我将不得不尝试此操作。
asmeurer 2013年

1
在该解决方案中,在我看来,第一步(中止的合并)仅对找出哪些文件进行了重命名和远程编辑很有用。如果您提前了解他们。您可以跳过该步骤,基本上,解决方案是简单地在本地手动重命名文件,然后像往常一样合并并解决冲突。

1
谢谢。我的情况是,我感动B.txt -> C.txtA.txt -> B.txt使用git mv和git的不能自动匹配正确的合并冲突(是旧之间越来越合并冲突B.txt和新的B.txt)。通过使用此方法,合并冲突现在位于正确的文件之间。
cib

这仅在整个文件移动时有效,但是git通常应自动检测到这种情况。棘手的情况是仅移动文件的一部分。
罗宾·格林
By using our site, you acknowledge that you have read and understand our Cookie Policy and Privacy Policy.
Licensed under cc by-sa 3.0 with attribution required.