问题
I encountered a strange situation with git in which I had a feature branch that modified some files and deleted one file "foo.js". When I rebased onto master via "git rebase master feature", I encountered the following type of conflict:
CONFLICT (rename/delete): src/main/resources/com/blah/bar.js deleted in Change some js and renamed in HEAD. Version HEAD of src/main/resources/com/blah/bar.js left in tree.
The odd thing is that neither neither the master branch nor the feature branch had even modified bar.js, let alone deleted it. What's more, executing "git rebase -i master feature" results in no conflict (when choosing all "pick").
One possible important clue is that foo.js and bar.js are similar but not identical files, and the slightly modified files are also similar to foo.js and bar.js. The only wild speculation that I can make is that some of the changes made to related js files somehow confused git into thinking that the offending commit in branch feature consisted of a delete of bar.js followed by a rename of foo.js (which was deleted) to bar.js. Is this possible/expected? Does anyone have any idea why this could happen? FYI, I'm using git version 1.8.3.2 on linux mint 16.
回答1:
That is a bit unusual, but your diagnosis is correct.
An interactive rebase uses a series of git cherry-pick
commands. A non-interactive rebase uses git-merge-recursive
(by default at least; see the git-rebase--merge
script in the git-core
directory, wherever that is on your system, or simply note the -s <strategy>
argument to git rebase
) to make each new commit from an old one. As the documentation for -m
/ --merge
says (although it fails to mention that this is the default):
Use merging strategies to rebase. When the recursive (default) merge strategy is used, this allows rebase to be aware of renames on the upstream side. Note that a rebase merge works by replaying each commit from the working branch on top of the <upstream> branch. Because of this, when a merge conflict happens, the side reported as ours is the so-far rebased series, starting with <upstream>, and theirs is the working branch. In other words, the sides are swapped.
You can tell for sure by using git diff -M --name-status
1 to compare the suspect commit pair. If this shows bar.js
as D
eleted and foo.js
as R
enamed to bar.js
, that's the culprit.
Annoyingly, there is no way to set the rename threshold for merge. Fortunately the workaround is simply to run an interactive rebase and make no changes in the command series, which you can short-cut by doing:
GIT_EDITOR=: git rebase -i ...
or by using the -p
option to preserve merges (the latter is different if you have merges, but has the side effect of doing an "implied interactive" rebase, which simply sets GIT_EDITOR=:
and also turns off autosquash).
1The -M
option is not required if you have configured diff.renames
to true
(see git config documentation). Merge-recursive internally sets the rename but not copy detection, and sets the "rename limit" the first of whichever of these applies: your configured merge.renamelimit
, if you have one; your diff.renamelimit
if you have one; or the constant 1000
.
来源:https://stackoverflow.com/questions/22951108/git-rebase-master-feature-gives-rename-delete-conflict-git-rebase-i-master-f