Why doesn't git cherry-pick override modification in current branch if their is different?

☆樱花仙子☆ 提交于 2019-12-19 04:12:51

问题


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

易学教程内所有资源均来自网络或用户发布的内容,如有违反法律规定的内容欢迎反馈
该文章没有解决你所遇到的问题?点击提问,说说你的问题,让更多的人一起探讨吧!