Welcome to OGeek Q&A Community for programmer and developer-Open, Learning and Share
Welcome To Ask or Share your Answers For Others

Categories

0 votes
348 views
in Technique[技术] by (71.8m points)

patch - How do I apply changes from a specific Git commit somewhere else in the commit tree?

Say I have two branches in a git repo 'master' and 'feature'. The two branches have diverged from some point. Now I want to take the exact changes to 'script.py' from commit '0123456789abcdef' on the 'feature' branch and apply those changes to the corresponding lines of 'script.py' at the head of the 'master' branch.
git cherry-pick seems like the obvious choice to me here, but it does not do exactly what I want. The trouble is that git cherry-pick brings the 'master' branch up to date with all the changes on 'feature' up to commit '0123456789abcdef'.
I do not want all of those changes applied. I only want exactly the changes that occurred in commit '0123456789abcdef' - nothing more, nothing less. I thought it would be smart if I could somehow exploit common history to figure out where the corresponding changed lines might have moved in 'master' relative to the commit from 'feature', yet without applying all those preceding changes on 'master'.
How can I do this? Surely, I can copy-and-paste it from the output of a git diff, but knowing Git somewhat well it seems to me it must have a better way to do it that I just do not know yet.

Edit: it seems the above behaviour must have been a result of a mistake I can no longer reproduce. Based on @torek's answer, I have repeated the correct procedure of a cherry-pick and it produces the result I wanted.

question from:https://stackoverflow.com/questions/65907063/how-do-i-apply-changes-from-a-specific-git-commit-somewhere-else-in-the-commit-t

与恶龙缠斗过久,自身亦成为恶龙;凝视深渊过久,深渊将回以凝视…
Welcome To Ask or Share your Answers For Others

1 Reply

0 votes
by (71.8m points)

In this claim:

git cherry-pick seems like the obvious choice to me here, but it does not do exactly what I want. The trouble is that git cherry-pick brings the 'master' branch up to date with all the changes on 'feature' up to commit '0123456789abcdef'.

you have conflated cherry-pick with merge. If you were to run:

git checkout master
git merge 0123456789abcdef

that would locate the merge base of the current tip commit of master, and commit 0123456789abcdef, and do what you say. But git cherry-pick 0123456789abcdef locates instead the parent commit of 0123456789abcdef, then invokes Git's merge engine on the three commits involved here.

The effect is that git cherry-pick will find the changes from 0123456789abcdef^—the parent of 0123456789abcdef—to 0123456789abcdef, and apply them to the current or HEAD commit. This will affect more than one file, if more than one file has any difference from 0123456789abcdef^ to 0123456789abcdef (use git diff 0123456789abcdef^ 0123456789abcdef, or more simply, git show 0123456789abcdef, to see; note that 0123456789abcdef must not be a merge commit).

If you only want the 0123456789abcdef^-to-0123456789abcdef changes for one particular file applied, use git cherry-pick -n so that git cherry-pick does the initial part of the work, but then stops before committing the result. This gives you the opportunity to back out changes to other files, using git reset or git restore. (If using git reset, be sure to supply file names; git restore is new in Git 2.23, and is a more limited but more flexible variant of git reset in this particular regard.)

Note that git cherry-pick does not do the equivalent of:

git diff master 0123456789abcdef

That is, it's not looking to make the master version of any file match the version of any file in 0123456789abcdef. What Git will end up doing is a three-way merge, combining the diffs produced by:

git diff --find-renames 0123456789abcdef^ HEAD

(i.e., what you changed with respect to the parent of 0123456789abcdef) with those produced by:

git diff --find-renames 0123456789abcdef^ 0123456789abcdef

(i.e., what they changed with respect to their own parent). The combined changes are then applied to the snapshot from 0123456789abcdef^: this adds your changes to their parent, plus their changes to their parent. The end result—if there are no conflicts—is that you've added their changes to your current commit, where "their changes" are with respect to their commit's parent's snapshot; but this also accounts for any line motion, or even file-name-changes, between their commit's parent and your commit.

Hence, based on your question, it seems that cherry-pick is the answer. If it's not, you're going to need to provide more information about why that is the case.


与恶龙缠斗过久,自身亦成为恶龙;凝视深渊过久,深渊将回以凝视…
OGeek|极客中国-欢迎来到极客的世界,一个免费开放的程序员编程交流平台!开放,进步,分享!让技术改变生活,让极客改变未来! Welcome to OGeek Q&A Community for programmer and developer-Open, Learning and Share
Click Here to Ask a Question

...