如何将一个分支的提交复制到另一个分支?

1184

我从我的主分支获得了两个分支:

  • v2.1:(第二版)我已经工作了几个月
  • wss:我昨天创建的,为了给我的主分支(在生产中)添加一个特定的功能

有没有办法将昨天在 wss 上提交的更改复制到 v2.1 中?


将一个分支(或一系列提交)简单地复制到另一个分支,这个答案对我帮助最大:https://dev59.com/DHI-5IYBdhLWcg3wJE4K#1994491 - caramba
从另一个分支挑选特定提交记录 - KyleMit
11个回答

1435
使用
git cherry-pick <commit>

应用<commit>到您的当前分支。我自己会在gitk中交叉检查我选择的提交,并使用右键单击其中的提交条目来挑选它们。
如果你想更自动化(带有所有危险),并假设昨天以来的所有提交都发生在wss上,那么你可以使用git log生成提交列表(使用Jefromi建议的--pretty)。
git log --reverse --since=yesterday --pretty=%H

假设您使用bash,则所有内容都可以放在一起。

for commit in $(git log --reverse --since=yesterday --pretty=%H);
do
    git cherry-pick $commit
done

如果这里出现了问题(有很多潜在的问题),你就会陷入麻烦,因为这是在实时结账时运行的,所以要么手动挑选要提交的代码,要么像Jefromi建议的那样使用rebase。


1
所有 --pretty 选项的占位符都在 git-log 手册中。您可以获得任何格式 - 特别适用于以易于解析的形式获取脚本所需的字段。 - Cascabel
2
同意。这就是为什么我从不使用它,而是手动完成。但是挑选最好的还是答案,至少对于问题标题来说是这样。我修改了回复。 - Benjamin Bannier
1
有人提交到了一个旧的/不正确的分支,而 cherry-pick 让我把那个提交放到了正确的分支中(同时仍然保持他们作为提交者)。完美。 - Patrick
1
@qqqqq 我没有使用过,但你可以使用 git cherry-pick A^..B 从 A 到 B 进行操作。A 应该比 B 更旧。 - bitsand
34
一篇罕见的 git 答案,直接简洁地给出解决方案,而不是在 git 的复杂性中绕来绕去以证明回答者对其了解有多深。 - Przemek D
显示剩余7条评论

724

你应该确实有一个工作流程,让你通过合并完成所有这些操作:

- x - x - x (v2) - x - x - x (v2.1)
           \
            x - x - x (wss)

所以您需要做的就是 git checkout v2.1git merge wss。如果出于某种原因您无法这样做,并且无法使用git rebase将wss分支移到正确的位置,那么从其他地方获取并在其他地方应用单个提交的命令为git cherry-pick。只需检出要应用它的分支,然后运行 git cherry-pick <SHA of commit to cherry-pick>

以下是一些rebase可能会帮助您的方式:

如果您的历史记录看起来像这样:

- x - x - x (v2) - x - x - x (v2.1)
           \
            x - x - x (v2-only) - x - x - x (wss)

你可以使用 git rebase --onto v2 v2-only wss 将 wss 直接移动到 v2:

- x - x - x (v2) - x - x - x (v2.1)
          |\
          |  x - x - x (v2-only)
           \
             x - x - x (wss)

那么你就可以合并!如果你真的,真的,真的无法达到可以合并的地步,你仍然可以使用rebase来有效地一次性进行多个cherry-pick:

# wss-starting-point is the SHA1/branch immediately before the first commit to rebase
git branch wss-to-rebase wss
git rebase --onto v2.1 wss-starting-point wss-to-rebase
git checkout v2.1
git merge wss-to-rebase
注意:之所以需要额外的工作才能做到这一点,是因为这将在您的存储库中创建重复提交。这并不是一个好事情——轻松分支和合并的整个重点是通过在一个地方进行提交并将它们合并到需要它们的任何位置来完成所有操作。重复提交意味着从未打算将这两个分支合并(如果您决定以后想要合并它们,您会遇到冲突)。

1
完全同意这个答案。+1。另请参阅我的旧答案,以说明挑选的后果:https://dev59.com/WXNA5IYBdhLWcg3wpfmu#881112 - VonC
1
@VonC:感谢您的支持,以及有关为什么不要挑选的额外信息 - 我知道我在那里有点吝啬。@gotgenes:谢谢!我认为这完全值得努力 - 只需查看git-rebase手册即可。没有比这更好的解释方式了。 - Cascabel
谢谢jefromi,这些信息帮助我理解并编写了这个问题/答案 - http://stackoverflow.com/questions/32600066/git-share-pull-request-only-some-commits-with-upstream-repository/32600067#32600067。 - HankCa
1
这是我的使用案例:关键错误修复已提交到功能分支。我现在需要它进入主分支以进行生产。这将拯救我的生命。 - Captain Hypertext
如果我们有两个特性分支,每个分支有4个提交,然后我们想将这8个提交cherry-pick到主分支上,会发生什么? - ali Falahati
显示剩余2条评论

126

git cherry-pick:应用现有提交引入的更改。

假设我们有一个分支A,其中有(X, Y, Z)个提交。我们需要将这些提交添加到分支B中。我们将使用cherry-pick操作。

当我们使用cherry-pick时,我们应该按照提交在分支A中出现的相同时间顺序将提交添加到分支B中。

cherry-pick支持一系列提交,但如果该范围内有合并提交,则变得非常复杂。

git checkout B
git cherry-pick SHA-COMMIT-X
git cherry-pick SHA-COMMIT-Y
git cherry-pick SHA-COMMIT-Z

工作流程示例:

enter image description here

我们可以使用 cherry-pick 命令,并结合选项

-e 或者 --edit:使用此选项,git cherry-pick 命令将允许您在提交之前编辑提交信息。

-n 或者 --no-commit:通常该命令会自动创建一系列的提交。这个标志将应用必要的更改来将每个命名提交挑选到您的工作树和索引中,而不进行任何提交。此外,当使用此选项时,您的索引不必与 HEAD 提交匹配。挑选是针对您的索引初始状态执行的。

这里有一篇有趣的文章,关于使用 cherry-pick 命令。


83
假设我已经提交了更改到主分支。 现在我将获取提交的ID(xyz)。 然后我必须进入需要推送我的提交的分支。
单个提交ID xyz
git checkout branch-name
git cherry-pick xyz
git push origin branch-name

多个提交ID:xyz abc qwe

git checkout branch-name
git cherry-pick xyz abc qwe
git push origin branch-name

38

之前提到的回答已经覆盖了大部分内容,但是似乎缺少的一件事是cherry-pick--no-commit特性。

假设您在功能分支上有多个提交,并且希望将它们全部合并为一个提交并放入主分支中。在这种情况下,您需要执行以下操作:

git checkout <branch-on-which-to-add-features>
git cherry-pick --no-commit <commit-hash>
git cherry-pick --no-commit <commit-hash>
.
.
.

最后,一旦你cherry-pick了所有需要的功能,你可以进行最终提交:

git commit -m "Some message for the merge commit"
理想情况下,正如@Cascabel所提到的,你应该使用mergerebase。但如果你感觉没有其他选择,你可以使用cherry-pick

25
你可以从需要复制的提交中创建一个补丁,然后将补丁应用到目标分支中。具体请参考git-diff文档git-apply文档

17
即使出于某种原因你真的想使用补丁(patch)而不是挑选(cherry-pick)/变基(rebase),直接的方法是使用 git format-patch <修订范围>git am *.patch - Cascabel
需要使用 checkout 命令切换到另一个分支。 - CoolMind

14

如果你不太喜欢"布道者"的方式,你可以使用一个比较丑陋的方法。在deploy_template中有一些我想要复制到我的主分支deploy的提交。

或者,如果您对布道者的说法不太认同,您可以采取我正在使用的另一种有些不太好看的方式。在deploy_template中,有一些提交,我想将它们复制到我的主分支deploy中。

git branch deploy deploy_template
git checkout deploy
git rebase master

这将在deploy_template上创建一个新的分支deploy(我使用-f覆盖现有的deploy分支),然后将该新分支rebase到master,保持deploy_template不变。


5

这里有另一种方法。

git checkout {SOURCE_BRANCH}               # switch to Source branch.
git checkout {COMMIT_HASH}                 # go back to the desired commit.
git checkout -b {temp_branch}              # create a new temporary branch from {COMMIT_HASH} snapshot.
git checkout {TARGET_BRANCH}               # switch to Target branch.
git merge {temp_branch}                    # merge code to your Target branch.
git branch -d {temp_branch}                # delete the temp branch.

1
感觉这是一个非常好的方法,以防您重置进度并拥有单独的分支(在我的情况下是测试环境),您可以从中恢复提交。 - Alex
我使用了这种方法来添加在重置头部期间错过的提交。谢谢! - Dmytro

5

使用内置的git gui更安全地进行挑选特定提交:

例如:从dev分支复制一个提交到main分支:

git checkout main
gitk --all

接着右键单击所需提交,选择挑选此次提交

enter image description here

Mac 上的 gitk:安装 gitk


这个回答假设了特定的操作系统和应用程序安装。你能让它更通用吗? - Brandon Dewey
gitk 可用于多个操作系统。Mac 版本请参考:https://dev59.com/F2Mm5IYBdhLWcg3wfu82 - GorvGoyl
你能解释一下为什么通过用户界面(UI)挑选更安全,而不是通过命令行界面(CLI)挑选吗? - Talal916
你能解释一下为什么通过用户界面(UI)挑选比通过命令行界面(CLI)更安全吗? - undefined

3

cherry-pick 命令可以从标准输入中读取提交列表。

以下命令按时间顺序挑选用户John在"develop"分支上存在但在"release"分支上不存在的提交。

git log develop --not release --format=%H --reverse --author John | git cherry-pick --stdin

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接