如何在Git中合并特定的提交


1034

我已经从GitHub的存储库中分支了一个分支,并提交了一些特定于我的东西。现在,我发现原始存储库具有的出色功能,该功能位于HEAD

我只想合并它而无需先前的提交。我该怎么办?我知道如何合并所有提交:

git branch -b a-good-feature
git pull repository master
git checkout master
git merge a-good-feature
git commit -a
git push

如果您要针对github进行此操作,本文将带您逐步完成。markosullivan.ca/how-to-handle-a-pull-request-from-github
johndpope 2011年

Answers:


1173

' git cherry-pick'应该是您的答案。

应用现有提交引入的更改。

不要忘记阅读bdonlan的有关樱桃选择结果的答案:
“将所有提交从分支中拉出,将指定的提交推入另一个中”,其中:

A-----B------C
 \
  \
   D

变成:

A-----B------C
 \
  \
   D-----C'

提交的问题是git认为提交包含所有历史记录

其中C'具有不同的SHA-1ID。
同样,樱桃从一个分支到另一个分支挑选一个提交基本上涉及生成补丁,然后应用它,从而也失去了历史记录。

提交ID的这种更改在其他方面破坏了git的合并功能(尽管如果谨慎使用,则会在此进行试探)。
不过,更重要的是,它忽略了功能依赖性-如果C实际上使用了B中定义的功能,您将永远不会知道


1
@ openid000:“更细粒度的分支”:实际上,这正是bdonlan在他的回答中所暗示的。
VonC

8
注意:“ git rebase”也会更改SHA-1。另请参阅“ git rebase与git merge(stackoverflow.com/questions/804115/git-rebase-vs-git-merge)”和“ git工作流”(stackoverflow.com/questions/457927/…),以了解“ git rebase”的情况“是合法的
。– VonC

1
在“细粒度分支”,“樱桃选择”和“变基”之间,您将拥有使用git在分支中管理代码的所有可能性。
VonC

@VonC“细粒度的分支”是的。我有两个分支,并且在一个分支中已升级到特定的可视化模块。此模块独立于所有其他代码,因此Cherry-pick非常方便将这些更改应用于其他分支
Cheeku 2015年

1
但是请注意,合并时,提交历史记录中将同时包含提交C'和C。
拉胡尔·沙阿

738

您可以使用git cherry-pick单独将单个提交应用于当前分支。

例: git cherry-pick d42c389f


68
+1您先前关于樱桃采摘的帖子(stackoverflow.com/questions/880957/…)。我可以自由地在上面自己的答案中复制其摘录。
VonC

4
大概git cherry-pick d42c还是git cherry-pick d42c3 会的。Git很聪明。;)
guneysus 2015年

2
致命:修订
不当

2
我刚刚执行了此命令,它似乎已经起作用了,但是当我执行git status时,它说“什么也没提交,工作树干净”。当我刷新我的bitbucket网页中的“提交”页面时,它没有显示。但是它在我执行git log时出现。我看到了修改后的代码。有人可以解释我是否必须执行其他步骤吗?

1
不幸的是,这并不能回答问题:它实际上并没有创建合并。没有血统指向d42c389f。也许OP本身并不关心创建合并,但是有时差异确实很重要。
LarsH

29

让我们尝试举一个例子来理解:

我有一个分支,例如master,指向X <commit-id>,还有一个新分支,指向Y <sha1>。

其中Y <commit-id> = <master>分支提交-很少提交

现在说,对于Y分支,我必须关闭master分支和新分支之间的提交。下面是我们可以遵循的过程:

步骤1:

git checkout -b local origin/new

其中本地是分支名称。可以给出任何名称。

第2步:

  git merge origin/master --no-ff --stat -v --log=300

将提交从master分支合并到新分支,还创建一个日志消息的合并提交,其中最多包含正在合并的<n>个实际提交中的单行描述。

有关Git合并的更多信息和参数,请参考:

git merge --help

另外,如果您需要合并特定的提交,则可以使用:

git cherry-pick <commit-id>

您是否更改了Y3D句子中的定义?“我有一个指向Y的新分支”与“现在说Y分支”,听起来像Y曾经是一个提交,然后它变成了一个分支
Purefan

我如何从要合并的分支的某个提交点做到这一点
Kulbhushan Singh,

3

在我的用例中,我们对CI CD也有类似的需求。我们在开发和主分支中使用了git flow。开发人员可以自由地直接合并那里的更改以进行开发,也可以通过功能分支的拉取请求进行合并。但是要掌握,我们只能通过Jenkins以自动化的方式合并来自developer分支的稳定提交。

在这种情况下,摘樱桃不是一个好选择。但是,我们从commit-id创建一个本地分支,然后将该本地分支合并到master并执行mvn clean验证(我们使用maven)。如果成功,则使用带有localCheckout = true选项和pushChanges = false的maven发布插件将生产版本工件发布到联系。最终,当一切都成功时,则将更改推送到源代码。

示例代码段:

如果您手动完成,则假设您处于主状态。但是在詹金斯上,当您签出仓库时,您将位于默认分支(如果已配置,则为master)。

git pull  // Just to pull any changes.
git branch local-<commitd-id> <commit-id>  // Create a branch from the given commit-id
git merge local-<commit-id>  // Merge that local branch to master.
mvn clean verify   // Verify if the code is build able
mvn <any args> release:clean release:prepare release:perform // Release artifacts
git push origin/master  // Push the local changes performed above to origin.
git push origin <tag>  // Push the tag to origin

这将为您提供完全控制,而无畏合并或冲突地狱。

如果有更好的选择,请随时提出建议。


3

前导答案描述了如何特定提交中的更改应用于当前分支。如果这就是“如何合并”的意思,那么请按照他们的建议使用“樱桃挑选”。

但是,如果您实际上想要合并,即您想要与两个父项进行新提交当前分支上的现有提交以及您要从中应用更改的提交),那么执行自动选择将无法实现。

例如,如果您的构建过程利用git ancestry来基于最新标记自动设置版本字符串(使用git describe),则可能需要具有真实的合并历史记录。

您可以执行实际操作git merge --no-commit,而不是手动选择,然后手动调整索引以删除不需要的任何更改。

假设您在分支上,A并且想要合并分支顶端的提交B

git checkout A
git merge --no-commit B

现在,您已设置创建带有两个父项的提交,当前提示提交为AB。但是,您可能应用了比您想要的更多的更改,包括来自B分支上较早提交的更改。您需要撤消这些不需要的更改,然后提交。

(可能有一种简单的方法可以将工作目录的状态和索引设置回合并之前的状态,这样一来,您就可以从一开始就选择想要的提交了。我不知道如何实现这一目标,git checkout HEAD并且git reset HEAD都将删除合并状态,从而违反了此方法的目的。

因此,手动撤消不需要的更改。例如,您可以

git revert --no-commit 012ea56

对于每个不需要的提交012ea56

完成调整后,创建您的提交:

git commit -m "Merge in commit 823749a from B which tweaked the timeout code"

现在,您只有想要的更改,而祖先树显示您已从B技术合并。

By using our site, you acknowledge that you have read and understand our Cookie Policy and Privacy Policy.
Licensed under cc by-sa 3.0 with attribution required.