14

I'd like to know how you handle a situation like this in Git:

  • create branch task001 from master
  • master: modify foo.c and rename it to bar.c
  • task001: modify foo.c and rename it to moo.c
  • merge task001 to master

What Git tells me is:

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.

How should I solve it? I mean, I still want to merge the two files once the name conflict is resolved.

Thanks.

pablo
  • 6,392
  • 4
  • 42
  • 62

2 Answers2

13

I initially understood your question to mean something different from what you have clarified in the comments, but the original answer may be useful, so I'm leaving that as the first part, but adding an alternative below:

1. Just resolving the conflict, so you only have the diverged moo.c and bar.c

Here I'm assuming that the result you want is to have bar.c and moo.c, with their changes from each branch, in the merged version. I would deal with this by resolving the conflict in the index and committing. The situation you describe looks like this:

$ 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

If I then merge task001 into master, it produces the same error that you see:

$ 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.

Then git status will show you a summary of problems:

$ 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")

Now you just need to use git add and git rm to resolve the conflicts:

$ 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
#

(I used --cached for removing foo.c because it's no longer in the working copy, and --cached means the command only looks at the index -- otherwise you get an error about foo.c not existing.)

Then you just commit the results:

$ git commit
[ save the prepopulated commit message ]

And you've resolved the conflicts and have bar.c and moo.c in the merged version.

(I note in passing that git merge -s resolve works for me in the simple example situation describe here, but I hope this is more generally useful.)

2. Merging bar.c and moo.c back into one file

It seems from your comments below that really what you would like is to merge these files back into one again, with the changes to the file in both branches merged. If you want git's merge strategies to help you with this, the files on each branch must have the same path before merging, so you'll need to rename at least one of them before merging. Let's suppose you want the merged file to be called quux.c, although you could just as well make it foo.c again, of course. Then you could do the following steps, which rename the file on each branch before merging:

# 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

If the changes didn't conflict, then at this point that merge should Just Work. In the example I tried they did conflict, so I needed to edit the file, git add the file and commit the result:

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
Mark Longair
  • 446,582
  • 72
  • 411
  • 327
  • Also likely helpful: `git checkout [--ours|--theirs] `. – Cascabel Jan 13 '11 at 17:57
  • Well, but what I expected to do was: be able to check that the renames diverged and then be able to merge them together under a single name... – pablo Jan 13 '11 at 19:15
  • @pablo: The error you get indicates that the file was renamed divergently. One the latter part, what is the state that you want to end up with? Is it a single file (say foo.c again) with the changes introduced in both branches merged? – Mark Longair Jan 13 '11 at 20:35
  • @Mark: no, what I see at the end is: two files, not merged... :( Thanks. – pablo Jan 13 '11 at 21:11
  • @pablo: I was asking about what you *want* to see :) But I think I understand now - I'll update my answer tomorrow... – Mark Longair Jan 13 '11 at 21:21
  • @pablo: I've now revised my answer to add steps to merge them back into one file again - hope this answer is worthy of being accepted now ;) – Mark Longair Jan 14 '11 at 09:50
  • @Mark: sorry Mark but I think I'm not making myself clear. Your second example moves renames two different files into a single one. No, my example is: you've foo.c, in one branch you change it and you move it to bar.c, in other branch you change it an you move it to moo.c, when you merge, the system should be able to notice the move diverged, let you solve it (at the end you should have one file, it is clear that the two developers made divergent changes, but you can't have, for instance, two mains on a project) and then help you merge together. – pablo Jan 14 '11 at 19:16
  • @pablo: In the second case, you will end up with a single file with the changes from both branches - you might need to resolve conflicts manually though. It's not clear to me what more help git could offer without understanding the meaning of the content of the files, e.g. there are plenty of cases where you might want to keep both edited files under their new names, such as if, rather than source code, these are two new text files people have produced from a common source. I'm afraid I don't think I can be any more help. – Mark Longair Jan 14 '11 at 19:27
  • I did `git add` to the file I wanted to keep. However, after `commit`, I only see the two untracked files (`some_file~HEAD` and `some_file~[commit]`) and not the file that I would like to keep. What's a safe way to get out of this? I'm fine with just using one of the files ending in a `~`, they're both the same. – theyuv Nov 08 '16 at 11:24
-2

Simple way to fix this First abort the current merge

git merge --abort

Now, decide which version of the file you want to use and use a merge strategy (with the flag -s)

You are on the branch master. If you want to keep the name you set in master then use this command

git merge -s ours task

If you want to use the version from task

git merge -s theirs task
Abizern
  • 146,289
  • 39
  • 203
  • 257
  • @Abizem, will that strategy only take the rename from one side, or will it also take the contents instead of merging? – Benjol Jan 13 '11 at 13:31
  • It will resolve conflicts by using the master branch version of the file. This means names and conflicts. – Abizern Jan 13 '11 at 15:09
  • 1
    What is this `git merge --abort` you speak of? Do you perhaps mean `git reset --merge`? In any case, you don't need to abort the whole merge to do this, nor do you have to keep the ours or theirs version for *all* files. Just, after you reach the conflicts, use `git checkout [--ours|--theirs] `. – Cascabel Jan 13 '11 at 17:56
  • `git merge --abort` backs out of a conflicted merge state. I find it useful as it lets me start again. And [I'm aware](http://365git.tumblr.com/post/488797694/fixing-merge-conflicts) of reset merge and the git checkout methods – Abizern Jan 13 '11 at 20:02
  • `git merge --abort` is only in v1.7.4-rc0 and later - it's described in 35d2fffdb857836 as a synonym for `git reset --merge`, which has been available since 1.6.2. – Mark Longair Jan 13 '11 at 21:19