问题
Look, I make a modification in a branch and then pick a commit from a similar branch, which does not have this modification. I wonder if modification must be rolled back or not.
Initially, both A and B branches have a full copy of the same file
begin
123
456
def f
789
klm
end
But they diverge. First, A moves def f
into the end of file, producing refactored A
begin
123
456
789
klm
end
def f
Now, if we cherry-pick B on top of this A, the original file is recovered (def f
is back in the middle of the file). This is fine because I stated to ask this question once I was informed that cherry-pick with -theirs produces an overriding alternative to cherry-pick. B is 'their' version of the file and it is what I expected because we see that B wins indeed: the only difference between A and B is in the place of A-refactoring and B version is preferred in this case.
I however started to ask this question because it is not always the case. If we add a bit of change to B, e.g rewrite the first line of the procedure for instance, 123 to 222
(I label this new version of B C
in the bash code below) what will be the result of picking this C into A do you think? The result of picking A <- C is puzzling
begin
222
456
789
klm
end
def f
You see, the first line is 222 from C but def f
is also in the end, which means that refactoring of A has preserved and C did not override it. That is a mystery of inconsistent behaviour IMO. You think that B is different from A by the whole file but it is not, once you further modify a little bit. The unrelated change stop the rollback or I just cannot figure out the git rules out. Which changes should I expect in the cherry-pick op?
I think that it is related situation where picking B tells that whole file has changed whereas if you pick modified C, diff proceeds per normal detecting only single line change.
You can reconstruct the situation using
mkdir preserving ; cd preserving
git init ; echo rrr > root
git add root ; git commit -m root
git checkout -b B ; git checkout -b A
function makeABC {
echo begin > abc
echo " 123" >> abc
echo " 456" >> abc
echo " def f" >> abc
echo " 789" >> abc
echo " klm" >> abc
echo end >> abc
}
echo commiting ABC into branch A
makeABC ; git add abc ; git commit -m abc
echo refactoring A, def f moved into the end of file
git checkout A
sed -i -e '/def f/d' abc
echo "def f" >> abc
git add abc ; git commit -m "refactoring 'def f'"
echo posting abc into B
git checkout B ; makeABC ; git add abc ; git commit -m "abc in B"
echo choosing which branch to pick
picking="B" ; case $picking in
"B") ;;
"C") git checkout -b C ; sed -i -e 's/123/CCC/g' abc
git add abc ; git commit -m CCC ;;
esac
git checkout A ; git cherry-pick $picking -Xtheirs
echo observe if refactoring def f is in place in A
gitk --all &
echo 'preserving' folder created
Set value of picking
variable to "B" or "C" to choose the branch that you want to pick upon A.
回答1:
This is because the -Xtheirs
part of git cherry-pick -Xtheirs
only says what to do with conflicting changes, and there are no conflicting changes between the two commits in your last example. The merge strategy is recursive
(the default in this case), and theirs
is an option to this strategy.
If there was a strategy called theirs
, then you would have gotten the result which you expected.
Now, there is no theirs
strategy, but there is an ours
strategy that can demonstrate the point. Instead of git cherry-pick -Xtheirs C
, try git cherry-pick --strategy=ours C
and see what happens. It will effectively ignore the changes from C
and say that there is nothing to commit. This strategy is of course useless when cherry-picking, but it may help to understand.
To achieve what you really wanted in the first place, here is one way to do it:
git checkout A
git read-tree -m -u C
git commit --no-edit -c C
More information about merge strategies can be found in man git-merge.
回答2:
From what I got on freenode#git, people say that in contrast to rebase, picking works with updates done by picked commit alone, it does not look at cumulative changes since the common root. This means that since B has introduced abc file, file will be re-introduced when picked on top of A and, thus overwriting the whole file. C however introduces only a small change in the file and therefore will leave A modification intact.
来源:https://stackoverflow.com/questions/34187857/why-doesnt-git-cherry-pick-override-modification-in-current-branch-if-their-is