Git分歧重命名

14

我想了解在Git中如何处理这样的情况:

  • 从主分支创建任务001分支
  • 在主分支上修改foo.c并将其重命名为bar.c
  • 在任务001分支上修改foo.c并将其重命名为moo.c
  • 将任务001分支合并到主分支

Git告诉我的是:

CONFLICT (rename/rename): Rename "foo.c"->"bar.c" in branch "HEAD" rename "foo.c"->"moo.c" in "task001"
Automatic merge failed; fix conflicts and then commit the result.

我该怎么解决这个问题?我的意思是,在名字冲突得到解决后,我仍然想合并这两个文件。

谢谢。


冲突出现后,你的暂存区里有什么? - Gauthier
2个回答

13

我最初误解了你的问题,以为你的意思与评论中澄清的不同,但原始答案可能仍有用,因此我将其作为第一部分保留,并在下面添加一个替代方案:

1. 只解决冲突,让你只剩下 diverged 的 moo.c 和 bar.c

在这里,我假设你想要的结果是在合并后拥有 bar.cmoo.c,以及它们各自分支的更改内容。我会通过解决索引中的冲突并提交来处理这个问题。你描述的情况如下:

$ git log --pretty=oneline --graph --decorate master task001
* fce127471ab5d1ef55b1d16412a75a7129782517 (task001) Rename to a different file on task001
* 8edbc1357a3c0484dc077f6f7ce43de91d21e794 A small change on the task001 branch
| * d10e9020d147a4bbd3688744823380322bf37718 (HEAD, master) Rename on master to bar.c
| * f952617645456a551df07f219bfdc95e00c8ac9b Added a small change in master
|/  
* e8602eef46af744defd4fb4073ecdb6a7afbfd28 Initial version of foo.c

如果我将task001合并到master中,就会产生与您看到的相同的错误:
$ git merge task001 
CONFLICT (rename/rename): Rename "foo.c"->"bar.c" in branch "HEAD" rename "foo.c"->"moo.c" in "task001"
Automatic merge failed; fix conflicts and then commit the result.

然后运行git status命令,它将向您显示问题的摘要:

$ git status
# On branch master
# Unmerged paths:
#   (use "git add/rm <file>..." as appropriate to mark resolution)
#
#   added by us:        bar.c
#   both deleted:       foo.c
#   added by them:      moo.c
#
no changes added to commit (use "git add" and/or "git commit -a")

现在,您只需要使用git addgit rm来解决冲突:
$ git add bar.c
$ git rm --cached foo.c
foo.c: needs merge
moo.c: needs merge
rm 'foo.c'
$ git add moo.c
$ git status
# On branch master
# Changes to be committed:
#
#   new file:   moo.c
#

我使用--cached删除foo.c,因为它已不在工作副本中,而--cached表示该命令仅查看索引 - 否则会出现foo.c不存在的错误。

然后您只需提交结果即可:

$ git commit
[ save the prepopulated commit message ]

您已经解决了冲突,合并版本中包含bar.cmoo.c

(顺便提一下,在这里描述的简单情况下,git merge -s resolve对我很有用,但我希望这对大家更普遍有用。)

2. 将bar.c和moo.c合并回一个文件

从您下面的评论中看来,您真正想要的是将这些文件再次合并为一个文件,并将两个分支中的文件更改合并在一起。如果您想让git的合并策略帮助您完成此操作,则每个分支上的文件在合并之前必须具有相同的路径,因此您需要在合并之前重命名它们中的至少一个。假设您希望合并后的文件名为quux.c,当然您也可以将其重新命名为foo.c。然后您可以执行以下步骤,在合并之前重命名每个分支上的文件:

# Rename the file on master:

$ git checkout master
Already on 'master'
$ git mv bar.c quux.c
$ git commit -m "Rename bar.c to quux.c on branch master"
[master f9b3f49] Rename bar.c to quux.c on branch master
 1 files changed, 0 insertions(+), 0 deletions(-)
 rename bar.c => quux.c (100%)

# Rename the file on task001:

$ git checkout task001 
Switched to branch 'task001'
$ git mv moo.c quux.c
$ git commit -m "Rename moo.c to quux.c as well on branch task001"
[task001 c71ad89] Rename moo.c to quux.c as well on branch task001
 1 files changed, 0 insertions(+), 0 deletions(-)
 rename moo.c => quux.c (100%)

# Switch back to master, and merge in task001:

$ git checkout master
Switched to branch 'master'
$ git merge task001

如果更改没有冲突,那么此时合并应该可以正常进行。在我尝试的示例中,它们发生了冲突,因此我需要编辑文件,git add文件并提交结果:

Renaming foo.c->quux.c
Auto-merging quux.c
CONFLICT (content): merge conflict in quux.c
Automatic merge failed; fix conflicts and then commit the result.
$ emacs -nw quux.c 
$ git add quux.c
$ git commit
[master 8baa7cb] Merge branch 'task001'
$ ls
quux.c

好的,但我期望做的是:能够检查重命名是否分歧,然后能够将它们合并到一个单一的名称下... - pablo
@pablo:你得到的错误表明该文件已被不同地重命名。在后面部分中,你想要达到什么状态?是一个单一的文件(比如说foo.c),包含两个分支所引入的更改吗? - Mark Longair
@pablo:我在问你想看什么 :) 但我现在明白了 - 我明天会更新我的答案... - Mark Longair
@pablo:在第二种情况下,您将得到一个包含两个分支更改的单个文件 - 但您可能需要手动解决冲突。如果不理解文件内容的含义,例如,有很多情况下您可能希望保留两个编辑后的文件以其新名称存储,比如说这是两个人从共同源创建的新文本文件而不是源代码。恐怕我无法提供更多帮助了。 - Mark Longair
我对我想要保留的文件执行了 git add。但是,在 commit 之后,我只看到了两个未跟踪的文件(some_file~HEADsome_file~[commit]),而不是我想要保留的文件。有什么安全的方法可以解决这个问题吗?我只想使用以 ~ 结尾的其中一个文件,它们都是相同的。 - theyuv
显示剩余4条评论

-2

解决此问题的简单方法是先终止当前的合并

git merge --abort

现在,决定您要使用哪个文件版本,并使用合并策略(带有 -s 标志)

您正在 master 分支上。 如果要保留在 master 中设置的名称,则使用此命令

git merge -s ours task

如果您想使用任务中的版本
git merge -s theirs task

@Abizem,这个策略只会从一侧重命名,还是它也会将内容合并在一起? - Benjol
它将通过使用主分支版本的文件来解决冲突。这意味着名称和冲突。 - Abizern
1
你所说的 git merge --abort 是什么?也许你是指 git reset --merge 吗?无论如何,你不需要中止整个合并来执行此操作,也不必为 所有 文件保留我们或他们的版本。只需在冲突出现后使用 git checkout [--ours|--theirs] <path> 即可。 - Cascabel
git merge --abort 可以退出冲突合并状态。我觉得这很有用,因为它让我可以重新开始。而且,我知道重置合并和使用 git checkout 方法的相关内容。 - Abizern
git merge --abort 只在 v1.7.4-rc0 及以后版本中可用 - 它被描述为 git reset --merge 的同义词,而 git reset --merge 自 1.6.2 版本以来就已经可用。 - Mark Longair

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接