Git расходящееся переименование

Я хотел бы узнать, как вы справляетесь с такой ситуацией в Git:

  • создать задачу branch2001 из мастера
  • master: изменить foo.c и переименовать его в bar.c
  • task001: изменить foo.c и переименовать его в moo.c
  • merge task001 для управления

Что 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.

Как мне его решить? Я имею в виду, я все еще хочу объединить два файла после разрешения конфликта имен.

Спасибо.

Ответ 1

Я изначально понял, что ваш вопрос означает нечто иное, чем вы уточнили в комментариях, но исходный ответ может быть полезным, поэтому я оставляю это как первую часть, но добавляю альтернативу ниже:

1. Просто разрешив конфликт, у вас есть только расходящиеся moo.c и bar.c

Здесь я предполагаю, что результат, который вы хотите, должен иметь bar.c и moo.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 add и git 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.c и moo.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

Если изменения не конфликтуют, то в этот момент слияние должно выполняться Just Work. В примере, который я попробовал, они столкнулись с конфликтом, поэтому мне нужно было отредактировать файл, 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

Ответ 2

Простой способ исправить это Сначала прервите текущее слияние

git merge --abort

Теперь определите, какую версию файла вы хотите использовать, и используйте стратегию слияния (с флагом -s)

Вы находитесь на главном сервере. Если вы хотите сохранить имя, заданное мастером, используйте эту команду

git merge -s ours task

Если вы хотите использовать версию из задачи

git merge -s theirs task