git cherry-pick是这个意思吗? [英] Does git cherry-pick mean this?

查看:85
本文介绍了git cherry-pick是这个意思吗?的处理方法,对大家解决问题具有一定的参考价值,需要的朋友们下面随着小编来一起学习吧!

问题描述

commit A^={toBeRemoved.txt, toBeModified.txt,old.txt} 
commit A={file1.txt(new added),added.txt(new added),toBeModified.txt(modified since A^),old.txt}
commit originally pointed by Branch B={file1.txt,toBeModified.txt,old.txt} 

然后

git checkout B
git cherry-pick A

将在分支B的顶端产生一个新的提交A',使得

A'={file1.txt(diff and merged,conflict possible),toBeModified.txt(diff and merged,conflict possible),added.txt,old.txt(from branch B)}

我不确定手册中" 应用由提交引入的更改 "的含义.我认为A对A ^引入的更改仅指+ added.txt 和- toBeRemoved.txt .至于文件 toBeModified.txt ,无论自A ^以来是否真的被修改,总是有可能引起冲突,就像 file1.txt ,可能需要手动合并.换句话说,最终A'的外观不仅取决于A和A ^,而且取决于A'将降落的分支B . 我要确定我的理解是否正确,因为这部分使我困惑了很长时间.

解决方案

您是正确的.从技术上讲,从技术上讲,樱桃挑选是一种合并操作-正如我喜欢的那样,作为动词合并.

大多数Git用户很快就会熟悉git merge命令. merge命令可以运行此合并操作,即合并为动词的想法.它还可以产生一个 merge commit ,它使用单词 merge 作为形容词来修饰名词 commit .短语 merge commit 通常缩写为 agerm ,该词使用 merge 作为名词.重要的是不要将此 merge作为名词(类型为 merge 的提交)与产生它的过程混淆:合并,动词合并,甚至尽管git merge会同时执行这两种操作(在某些情况下).将这些想法分开很重要的原因是,其他Git命令执行了动作,将合并为动词部分,而没有进行 merge commit . git cherry-pick命令就是这样的命令之一.

了解常规合并

仍然,要了解进行合并(作为动词合并操作)的含义,我认为从git merge的操作开始是有帮助的.与git checkout branch1; git merge branch2中一样,进行合并的过程包括首先找到合并基础提交,以便每个合并都有三个输入.

让我们假设有两个不同的程序员在从事一个项目.按照传统,我们将程序员A命名为Alice,将程序员B命名为Bob.爱丽丝(Alice)和鲍勃(Bob)从相同的存储库和分支开始,并最终彼此共享他们的新提交.原始分支(也许是master)是一个简单的线性提交系列,右侧有更新的提交:

...--F--G--H   <-- master

这里的每个大写字母代表提交的实际哈希ID.

Alice现在克隆存储库,以便她具有相同的提交,并创建她的分支alice.在该分支上,她进行了两次新提交:

             I--J   <-- alice
            /
...--F--G--H   <-- origin/master

Bob还会克隆存储库,以便他通过H进行提交,并创建其分支bob.在此分支上,他进行了两次提交:

...--F--G--H   <-- origin/master
            \
             K--L   <-- bob

请记住,每个提交都具有唯一的哈希ID ,但是宇宙中每个地方的每个Git 都同意爱丽丝的提交哈希ID对于爱丽丝的两次提交以及鲍勃的提交都是正确的哈希ID对于Bob来说是正确的.因此,对于Alice使用的替代字母I-J与对于Bob使用的替代字母K-L不同.当我们将这两个分支放入任何Git存储库中时,它们会保留其提交哈希ID,因此我们最终将它们组合如下:

             I--J   <-- alice
            /
...--F--G--H   <-- master
            \
             K--L   <-- bob

由谁控制存储库可以是git checkout alice; git merge bob还是git checkout -b merged alice; git merge bob.只是为了好玩,让我们去做后者.我们不会费心画master(但是名称仍然存在,仍然指向提交H).

             I--J   <-- alice, merged (HEAD)
            /
...--F--G--H
            \
             K--L   <-- bob

由于merged current 分支(已签出),因此在其中附加了名称HEAD.当我们运行git merge bob时,名称alicemerged都标识提交J.

通过选择要合并的提交JL,我们告诉Git它应该自动找到最佳的 shared 提交.这就是提交H:爱丽丝和鲍勃都从那开始.从技术上讲,合并基础是由提交形成的有向无环图(DAG)的最低公共祖先(LCA),找到DAG的LCA使用的是我之前已经描述过多次的算法,但是在视觉上它很明显最好的共享提交就是提交H.

因此,在找到了正确的合并基础提交之后,Git现在将与提交H关联的快照与与提交JL关联的两个快照进行比较.我们可以让Git以人类可以阅读的方式做到这一点.合并是通过内部方式完成的,这种方式对于程序的读取更方便,但是效果是相同的.要自己查看,我们可以使用:

  • git diff --find-renames hash-of-H hash-of-J:这告诉我们爱丽丝做了什么更改.这包括对特定文件的逐行更改,还包括对所有新创建的文件,完全删除的文件以及任何检测到的重命名文件的更改. 1 对于git merge,Git只是将更改保存到Git方便的位置.

    请注意,我们根本不看任何中间提交.我们只是将HJ进行比较,以观察整体效果.

  • git diff --find-renames hash-of-H hash-of-L:这告诉我们Bob的更改.此过程与Alice的提交过程相同:我们不查看两者之间的任何内容,仅查看开始的共享合并库H和结束的提交L,看看鲍勃做了什么.

git merge接下来要做的是合并为动词流程的核心. Git 组合.从合并基础中的快照开始,Git应用了Alice的所有更改 Bob的所有更改.当他们在何时何地发生冲突时,Git会做几件事:

  • 它将所有三个输入文件放入Git的 index 中,该索引也称为暂存区.这三个文件位于编号为 staging slot 的插槽中:插槽号1用于从合并基础H复制文件,插槽号2用于从当前提交,并且插槽号3是来自另一个提交L的文件的副本.

    在某些情况下(例如,如果Alice删除了Bob更改了同一文件的文件),则仅将两个文件放入索引中.对于添加/添加"冲突,也会发生这种情况:H中没有文件,并且Alice和Bob都创建了一个具有相同名称的文件. (对于复杂的重命名情况,Git中存在一些缺陷/错误,其中索引副本具有多个名称,并且所有操作都变得过分困难.幸运的是,这种情况很少见.)但是总的来说,冲突将所有三个文件都放入了索引.

  • 然后,对于冲突情况,Git尽最大努力进行合并,并将部分合并的结果以及由冲突标记包围的输入文件的未合并部分保留在 work-tree ,您可以在该文件中查看和编辑.请记住,索引中的文件是不可见的:您必须先在索引的 out 中复制它们,然后才能使用它们.

    如果您是git mergetool的粉丝,这就是git mergetool的作用:它将来自索引的三个输入文件复制到工作树中,您可以在其中查看和使用它们.然后它将运行您喜欢的任何实际合并工具,以便您可以看到所有这三个文件,当然还有Git尽最大努力将它们组合到工作树文件中. (对于我自己,我通常更喜欢将merge.conflictStyle设置为diff3并仅使用生成的工作树副本.)

    请注意,您可以使用--ours从当前或HEAD提交中引用插槽2中的文件:

    git checkout --ours path/to/file.ext
    

    您可以使用--theirs从其他提交中引用插槽3中的文件.插槽1中没有文件的简写(尽管可能应该是:--base).

对于所有没有冲突的文件,Git成功地合并了Alice的更改和Bob的更改,或者仅合并了Alice的文件(其中Bob没有进行更改),或者仅合并了Bob的文件(爱丽丝没有做任何更改).或者,通常对于大多数文件来说,每个文件的所有三个副本(合并基,爱丽丝的和鲍勃的)都匹配,因为没有人更改任何东西,在这种情况下,任何副本都可以.这些成功合并的文件,加上Alice和Bob所做的更改,并结合在基础文件中,适合于新的合并提交,并且它们由Git自动合并到工作树和索引/登台区域中. /p>

(请注意,Alice和Bob也可能进行了相同更改,例如,修复了注释中的错字.在这种情况下,Git只需要复制个副本,否则这些重复的更改将不会被视为冲突.)

这将完成该过程的作为原语合并"部分.然后,git merge命令由于存在冲突而停止,或者继续进行 merge-形容词合并提交.如果Git因冲突而停止,则可以修复Git在工作树和索引中留下的混乱,然后通过执行合并提交,运行来完成该过程. git merge --continuegit commit(请注意它们正在完成冲突的合并,并进行最终的合并提交).我们可以在这里画出来:

             I--J   <-- alice
            /    \
...--F--G--H      M   <-- merged (HEAD)
            \    /
             K--L   <-- bob

新提交M与其他任何提交一样,都具有快照(来自Alice和Bob的合并更改,应用于合并基础H,构成该快照)和一些元数据: 2 2 M的特别之处在于它不仅有一个父母,而且有两个父母.按顺序,两个父级依次是J-因为merged在运行git merge之前指向了J-然后是L,因为那是我们合并的提交.


1 git diff的输出可以(并且应该)视为一组指令,用于将左侧提交变为右侧提交.也就是说,git diff的输出可能会说:在第41行,删除2行.然后在第75行,插入这一行.对于新文件,差异将显示使用这些内容创建这个新文件,对于已删除文件,差异将显示希望旧文件具有这些内容;删除该文件.如果您从提取了左侧提交的工作树开始,然后忠实地遵循所有这些说明,那么您最终将得到一个与右侧提交相匹配的工作树.

由于diff输出具有人类可读(且可编辑)的文本,因此,您当然可以仅应用其中的 part ,也可以应用其中的所有内容以及更多内容.您还可以尝试将其应用于左侧的提交以外的其他提交,这就是git format-patchgit am的含义.

2 请注意,git merge提供了一条默认消息,指出您进行合并的原因是为了进行合并,并且通常还会提供一个分支的名称(合并)或两个分支(您合并的那个,然后是into,然后是您所在的那个).第二点信息有时可能稍微有用,而且通常没有太多要说的.但是,合并分支功能/F"可能不如合并功能F",后接该功能的实际描述.


现在我们可以完全理解樱桃采摘了

当我们使用git cherry-pick时,我们指示Git 复制一些提交.和以前一样,我们从提交图开始.如图所示,图形的确切形状并不重要:

...--o--o--...--A'--A--o--o--o   <-- somebranch
            \
             o--o--B   <-- branch

请注意,我在这里将 branch 称为branch,将 commit 称为B,因为我喜欢将这些单字母替代词用于提交哈希.正如您在问题中所做的那样,我已经将A的父母称为A'.

运行git checkout branch时,会将HEAD附加到名称branch,然后将提交B提取到索引和工作树中.现在,像往常一样,我们可以在branch的尖端查看并处理来自提交B的文件.

然后我们运行git cherry-pick A时-直接给出A的哈希值,或使用找到提交A的名称或相对表达式-Git定位提交A和提交A'.提交A'只是A的父级.它必须是唯一的父级:如果commit A是合并,则git cherry-pick拒绝选择其多个父级中的任何一个,并向我们显示一条错误消息,指出 we 必须自己选择该父对象(使用-m选项).如果我们确实手动选择了该父对象(例如git cherry-pick -m 1 A),那么Cherry-pick将使用我们选择的父对象,但通常我们会选择非合并的Cherry-pick.

Git现在运行常规的 merge-as-a-verb 动作,但不是 finding 合并基础,Git只是使用我们选择的提交A'隐含地.这是两个git diff操作的合并基础.必要时,来自提交A'的文件将进入索引的插槽1.

进入索引插槽2的内容与往常相同:我们现在已签出的提交,即提交B.最后一个插槽用于保存提交A中的文件,这是我们说过的选择.因此,--ours引用来自提交B的文件,而--theirs引用来自提交A的文件.大部分不可见的合并库是指来自提交A'(--theirs提交的父级)的文件.

如果存在合并冲突,则如同git merge停止一样,cherry-pick操作将停止,将冲突保留在工作树中,并将每个文件的三个(或有时两个)副本保留在索引中.由您自己来解决问题,然后运行git cherry-pick --continue进行樱桃挑选.

如果没有 合并冲突,或者在解决问题并运行git cherry-pick --continue之后,Git继续进行新的提交.与往常一样,新提交使用索引/登台区域中的任何内容.在所有全自动情况下,Git都会尽最大努力将这两个变更集组合在一起,并将这些合并的变更应用于A'(合并基础)中的文件.新提交也从原始提交中复制提交 message .然后,您将有机会编辑该消息,这取决于您是否要求编辑(--edit)(--no-edit).

在非常简单但又很常见的情况下,从A'B的差异很小,并且/或者该差异与A'的差异之间没有冲突到A.在这种情况下,从A'A的更改与从A'B的更改合并的结果是相同的结果只需直接修补提交B即可.实际上,在某些非常古老的Git版本中,git cherry-pick实际上是这样做的-它没有以commit A'作为合并基础来运行整个合并系统.但是在复杂的情况下,两者可能会产生不同的结果.因此,现代Git使用A'作为合并基础,B作为当前提交,以及A作为其他提交来进行完全合并.那是您在问题中所说的是您的理解,这是正确的.

我们现在也可以完全理解git revert

如果您运行git revert A而不是git cherry-pick A(在提交B时),则Git再次进行三向合并.这次的变化是,提交A是合并基础,提交B--ours提交,而提交A'(A的父级)是另一个提交.当从AB的差异(即我们更改了什么")与从AA'的差异(即它们更改了什么")发生冲突时,就会发生冲突.通过反转diff(产生一组用于修改commit A的指令,从而获得commit A'),两个diff的组合具有撤消从A'A所做的任何更改的效果.

let's say

commit A^={toBeRemoved.txt, toBeModified.txt,old.txt} 
commit A={file1.txt(new added),added.txt(new added),toBeModified.txt(modified since A^),old.txt}
commit originally pointed by Branch B={file1.txt,toBeModified.txt,old.txt} 

then

git checkout B
git cherry-pick A

will produce a new commit A' at the tip of branch B such that

A'={file1.txt(diff and merged,conflict possible),toBeModified.txt(diff and merged,conflict possible),added.txt,old.txt(from branch B)}

I'm not sure about the meaning of "Apply the change introduced by the commit" in the manual. I think the changes introduced by A with regard to A^ here only refer to +added.txt and -toBeRemoved.txt. As for the file toBeModified.txt, no matter if it is really modified since A^, it's always possible to cause conflicts just like file1.txt , which might need to be merged manually. In other words, what the final A' looks like depends not only on A and A^, but also on branch B where A' will land. I'm asking to make sure if my understanding is right because this part confused me for a long time.

解决方案

You are correct. A cherry pick is technically a merge operation—merge as a verb, as I like to put it.

Most Git users soon become familiar with the git merge command. The merge command can run this merging operation, this merge as a verb idea. It can also produce a merge commit, which uses the word merge as an adjective modifying the noun commit. The phrase merge commit is often shortened to a merge, which uses the word merge as a noun. It's important not to confuse this merge as a noun—the commit that has type merge—with the process that produces it: the action of merging, merge as a verb, even though git merge does both (in some cases). The reason it's important to keep these ideas separate is that other Git commands perform the action, the merge as a verb part, without ever making a merge commit. The git cherry-pick command is one such command.

Understanding regular merges

Still, to understand what it means to do a merge—the merge as a verb operation—it's helpful, I think, to start with what git merge does. The process of doing a merge, as in git checkout branch1; git merge branch2, involves first finding a merge base commit, so that there are three inputs to every merge.

Let's suppose there are two different programmers working on a project. As is traditional, we'll assume programmer A is named Alice, and programmer B is named Bob. Alice and Bob start with the same repository and branch, and eventually share their new commits with each other. The original branch, perhaps master, is a straightforward linear series of commits, with newer commits at the right:

...--F--G--H   <-- master

Each uppercase letter here stands in for the actual hash ID of the commit.

Alice now clones the repository, so that she has these same commits, and creates her branch alice. On this branch, she makes two new commits:

             I--J   <-- alice
            /
...--F--G--H   <-- origin/master

Bob also clones the repository, so that he has the commits through H, and creates his branch bob. On this branch, he makes two commits:

...--F--G--H   <-- origin/master
            \
             K--L   <-- bob

Remember that every commit has a unique hash ID, but every Git everywhere in the universe agrees that Alice's commit hash IDs are correct for Alice's two commits, and Bob's commit hash IDs are correct for Bob's. So we use different stand-in letters I-J for Alice's than the K-L for Bob's. When we put these two branches into any Git repository, they retain their commit hash IDs, so we eventually have them combined like this:

             I--J   <-- alice
            /
...--F--G--H   <-- master
            \
             K--L   <-- bob

Whoever controls this repository can git checkout alice; git merge bob, or git checkout -b merged alice; git merge bob. Just for fun, let's do the latter. We won't bother to draw in master (but the name still exists, still pointing to commit H).

             I--J   <-- alice, merged (HEAD)
            /
...--F--G--H
            \
             K--L   <-- bob

Since merged is the current branch (checked-out), that's where the name HEAD is attached. Both names alice and merged identify commit J when we run git merge bob.

By selecting commits J and L to be merged, we tell Git that it should automatically locate the best shared commit. That's commit H: the one that both Alice and Bob started with. Technically the merge base is the Lowest Common Ancestor (LCA) of the Directed Acyclic Graph (DAG) formed by the commits, and finding the LCA of a DAG uses an algorithm that I've described many times before, but here it's pretty obvious visually that the best shared commit is just commit H.

So, having located the proper merge base commit, Git now compares the snapshot associated with commit H to the two snapshots associated with commits J and L. We can have Git do this in a way that we, as humans, can read. The merge does it in an internal way that is more convenient for programs to read, but the effect is the same. To see this for ourselves, we would use:

  • git diff --find-renames hash-of-H hash-of-J: this tells us what Alice changed. That includes line-by-line changes to specific files, but also any newly-created files, entirely-deleted files, and any detected renamed files. With git diff, each of these is printed out so that we can see them.1 For git merge, Git just saves the changes somewhere convenient for Git.

    Note that we don't look at any of the intermediate commits at all. We just compare H to J, to observe the overall effect.

  • git diff --find-renames hash-of-H hash-of-L: this tells us what Bob changed. The process is identical to that with Alice's commits: we don't look at anything in between, just the starting shared merge base H and the ending commit L, to see what Bob did.

What git merge does next is the heart of the merge as a verb process. Git combines the changes. Starting from the snapshot in the merge base, Git applies all of Alice's changes and all of Bob's changes. When and where they conflict, Git does several things:

  • It puts all three input files into Git's index, which is also called the staging area. These three files go in numbered staging slots: slot number 1 is for the copy of the file from the merge base H, slot number 2 is for the copy of the file from the current commit J, and slot number 3 is for the copy of the file from the other commit L.

    In some cases—e.g., if Alice deleted a file where Bob changed the same file—it puts just two files into the index. That also occurs for an "add/add" conflict: there was no file in H, and Alice and Bob both created a file with the same name. (For complex rename cases, there's a bit of a flaw / bug in Git, in which the index copies have multiple names and everything gets overly difficult. Fortunately this case is pretty rare.) But in general, conflicts put all three files into the index.

  • Then, again for the conflict case, Git does the best it can with merging, and leaves the partially-merged result, plus the un-merged sections of the input files surrounded by conflict markers, in the work-tree, in the file you can see and edit. Remember, the files that are in the index are invisible: you have to copy them out of the index before you can use them at all.

    If you are a fan of git mergetool, this is what git mergetool does: it copies the three input files from the index, into the work-tree where you can see and work on / with them. Then it runs whatever actual merge-tool you prefer, so that you can see all three of these files, plus of course Git's best-effort at combining them into the work-tree file. (For myself, I usually prefer to set merge.conflictStyle to diff3 and just work with the resulting work-tree copy.)

    Note that you can refer to files in slot 2, from the current or HEAD commit, using --ours:

    git checkout --ours path/to/file.ext
    

    You can refer to files in slot 3, from the other commit, using --theirs. There is no shorthand for files in slot 1 (though there probably should be: --base).

For all the files where there are no conflicts, though, Git successfully merges Alice's changes and Bob's changes, or just takes Alice's file (where Bob made no changes), or just takes Bob's file (where Alice made no changes). Or, often the case for most files, all three copies of each file—merge base, Alice's, and Bob's—all match because nobody changed anything, in which case any copy of the file will do. These successfully-merged files, with Alice's and Bob's changes combined atop whatever was in the base, are suitable for the new merge commit, and they go into the work-tree and the index/staging-area as automatically-combined by Git.

(Note that it's also possible for Alice and Bob to have made the same change, e.g., to fix a typo in a comment. In this case, Git takes just one copy of the otherwise duplicated changes. This take-one-copy action is not considered a conflict.)

This completes the merge-as-a-verb portion of the process. The git merge command then either stops because there was a conflict, or goes on to make the merge-as-an-adjective merge commit. If Git stops with a conflict, it's up to you to fix up the mess that Git left behind in both the work-tree and the index, and then finish the process by making the merge commit, by running either git merge --continue or git commit (both notice that they're finishing up the conflicted merge, and make the final merge commit). We can draw that here:

             I--J   <-- alice
            /    \
...--F--G--H      M   <-- merged (HEAD)
            \    /
             K--L   <-- bob

New commit M is just like any other commit, in that it has a snapshot (the combined changes from Alice and Bob, applied to the merge base H, make up that snapshot) and some metadata: who made the commit (you), when (now), and why (the log message you enter).2 What's special about M is that it has not just one parent, but two parents. The two parents are, in order, J first—because merged pointed to J before we ran git merge—and then L, because that was the commit we merged.


1The output from git diff can (and should) be viewed as a set of instructions for turning the left-side commit into the right-side commit. That is, git diff's output may say: At line 41, delete 2 lines. Then at what was line 75, insert this one new line. For new files, the diff will say create this new file with these contents and for deleted files, the diff will say expect the old file to have these contents; delete that file. If you start with a work-tree that has the left-side commit extracted, and follow all these instructions faithfully, you will end up with a work-tree that matches the right-side commit.

Since the diff output has human-readable (and editable) text, you can, of course, apply only part of it, or apply all of it plus more. You can also attempt to apply it to a commit other than the left-side one, and that's what git format-patch and git am are about.

2Note that git merge supplies a default message that says that the reason you made the merge was in order to make a merge, and typically also gives the names of one branch (the branch you merged) or both branches (the one you merged, then into, then the one you were on). That second bit of information may occasionally be slightly useful, and often there isn't really a lot else to say. But "Merge branch feature/F" is probably not as good as "Incorporate feature F" followed by an actual description of the feature, for instance.


Now we can fully understand cherry-pick

When we use git cherry-pick, we instruct Git to copy some commit. We start with a commit graph, just as before. The exact shape of the graph is not that important, as we'll see:

...--o--o--...--A'--A--o--o--o   <-- somebranch
            \
             o--o--B   <-- branch

Note that I'm calling the branch here branch, and the commit B, since I like to use these one-letter stand-ins for commit hashes. I've called A's parent A', as you did in your question.

When we run git checkout branch, that attaches our HEAD to the name branch, and extracts commit B into our index and work-tree. We can now see and work with the files from commit B, at the tip of branch, as usual.

When we then run git cherry-pick A—giving the hash of A directly, or using a name or relative expression that finds commit A—Git locates both commit A and commit A'. Commit A' is simply the parent of A. It must be the only parent: if commit A is a merge, git cherry-pick refuses to pick any of its multiple parents, and gives us an error message saying that we must pick that parent ourselves (using the -m option). If we do pick that parent manually—e.g., git cherry-pick -m 1 A—then the cherry-pick uses the parent we chose, but usually we cherry-pick non-merges.

Git now runs the usual merge-as-a-verb action, but instead of finding a merge base, Git just uses the commit A' that we chose implicitly. That's the merge base for the two git diff operations. Files from commit A' will go into slot 1 of the index, if / when necessary.

What goes into slot 2 of the index is the same as always: the commit we have checked out right now, i.e., commit B. The last slot is for files from commit A, the one we said to cherry-pick. Hence --ours refers to files from commit B while --theirs refers to files from commit A. The mostly-invisible merge base refers to files from commit A', the parent of the --theirs commit.

If there are merge conflicts, the cherry-pick operation stops, just like git merge stops, leaving the conflicts in the work-tree and the three (or sometimes two) copies of each file in the index. It's up to you to fix up the mess, then run git cherry-pick --continue to make the cherry-pick finish.

If there aren't merge conflicts, or after you've fixed things up and run git cherry-pick --continue, Git goes on to make a new commit. As always, the new commit uses whatever is in the index / staging-area. In all the fully-automatic cases, that's Git's best effort at combining both sets of changes, and applying those combined changes to the files from A' (the merge base). The new commit copies the commit message from the original commit, too. You then get a chance to edit that message or not, depending on whether you asked to edit (--edit) or not (--no-edit).

In very simple, but also very common, cases, the diff from A' to B is small and/or there are no conflicts between that diff and the one from A' to A. In such cases, the result of combining changes made from A' to A with those made from A' to B is the same as the result of simply patching commit B directly. And in fact, in some very ancient versions of Git, git cherry-pick actually did that—it did not run the whole merge system with commit A' as a merge base. But in complicated situations, the two can produce different results. So modern Git does a full merge, using A' as the merge base, B as the current commit, and A as the other commit. That's what you said in your question was your understanding, and that's correct.

We can also completely understand git revert now

If, instead of git cherry-pick A, you run git revert A (while on commit B), Git once again does a three-way merge. What changes is that this time, commit A is the merge base, commit B is the --ours commit, and commit A'—the parent of A—is the other commit. Conflicts occur when the diff from A to B, i.e., "what we changed", conflicts with the diff from A to A', i.e., "what they changed". By reversing the diff—producing a set of instructions for modifying commit A so as to obtain commit A'—the combining of the two diffs has the effect of backing out whatever changes were made from A' to A.

这篇关于git cherry-pick是这个意思吗?的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!

查看全文
登录 关闭
扫码关注1秒登录
发送“验证码”获取 | 15天全站免登陆