如何 git cherrypick 在特定分支中引入的所有更改

How to git cherrypick all changes introduced in specific branch

背景资料:

由于现有系统的工作流程限制,我们需要设置一个有点非正统的 git 流程。

(patch)    A-B---F
             |   |
(hotfix)     C-D-E
                 |
(dev)      1-2-3-G

在补丁分支上,有一些提交。这里的文件与 dev 上的文件相似但不完全相同(同步脚本会切换许多文件中的设置顺序,使它们看起来已更改,但功能相同)。

此分支需要修复,因此创建并处理了一个修补程序分支。这个分支然后被合并回补丁,到目前为止,一切顺利。

同样的修复需要部署到 dev 分支,以便它与补丁保持相对同步,但尝试合并 hotfix 分支会导致 git 尝试合并所有不相关的和 'unchanged' 来自 A 和 B 的文件,而不仅仅是 C、D 和 E。

问题:

似乎 cherry-pick 做了我们想要的,只从选定的提交中获取更改,但我真的很想要一种方法来一次 cherry-pick 给定分支中的所有提交,而不必查找每次提交 ID。

It seems that cherry-pick does what we want in terms of only getting changes from selected commits, but I would really like a way to cherry-pick all commits in a given branch at once, without having to look up the commit ids every time.


使用cherry-pick

git cherry-pick 允许您选择在任何分支中对任何其他分支所做的任何提交。在您的情况下,您可以简单地签出 master 分支,然后 cherry-pick 您希望的任何分支的所有提交(cherry-pick 支持范围,因此您可以指定开始和结束提交而不是列出所有提交)。

这样您就可以控制您的提交在所需分支中的显示方式。

例如:

git cherry-pick ebe6942..905e279

# Find the range of commits you wish to re-add to your branch.
# then use cherry-pick to add them back to the branch
git cherry-pick start..end

# If you wish to include the start commit as well add the ^
# This will result in a cherry-pick of the start commit included as well 
git cherry-pick start^..end

如何找到分支的第一次提交?

git log

 # print out the latest commit (first one) of the given branch
 git log  --oneline | tail -1

merge-base

使用merge-base命令查找分支从原始分支分裂的位置:

git merge-base A B

检查您的分支(目标分支)并执行

git cherry-pick -m 1 hashCode

如果你想从分支 dev 中挑选所有提交。

尝试:

git cherry-pick ..dev

这应该也可以。我们调用 HEAD(在本例中为 master)和分支之间的分歧点并将其传递给 cherry-pick.

git merge-base origin/dev_single_doc_fit HEAD | xargs git cherry-pick {}..HEAD^

另一种方法 fast-forward。

案例:

您启动了 newBranch,提交了第一次提交并重置了 Hard

结果你得到了空 newBranch,没有那个迟到的提交和 git 树在 HEAD 是干净的。您在 newBranch 分行(可以与 git 分行核实)。

这里有两步操作:

  • 通过
  • 获取 'trash' 中的提交列表

git fsck --lost-found

  • 将错过的提交提交回您的分支:

git cherry-pick --ff 43a7a2d

根据接受的答案中的讨论,这里是一个单行。当你 运行 这个(大概是一些非默认分支)时,你当前在哪个分支上应该无关紧要。这假设 devel 是默认分支,并且您希望在分支 Bdevel 分支分离后从分支 B 中挑选所有提交。

git cherry-pick $(git log devel..B --pretty=format:"%h" | tail -1)^..$(git log B -n 1 --pretty=format:"%h")

命令语法为 first..last,但我们在这里使用的变体更像 first-1^..last

命令 git log devel..B --pretty=format:"%h" | tail -1 获取 devel 分支中的提交,该分支是 B 分支中第一个提交的父级。这有点笨拙,但 git merge-base 命令没有所需的格式选项。

命令 git log B -n 1 --pretty=format:"%h" 只是获取该分支中的最后一次提交。

这将使您处于一个交互式的挑选上下文中。因此,如果进展不顺利,请务必中止。

假设您知道您希望从分支中选择的提交数,您可以使用相对提交符号。

git cherry-pick BRANCH_A~10^..BRANCH_A

这将挑选从 (~10) BRANCH_A 的 HEAD 之前的 10 个提交开始的所有提交,包括起始提交 (^),并将获取所有提交在 (..) 到 BRANCH_A 的 HEAD 范围内。

一个班轮将是:

git cherry-pick $(git merge-base master my/branch)..my/branch

您也可以将其转换为 git 别名或 bash 函数。我更喜欢这样使用它,所以我只更改一个地方:

BRANCH=my/branch; git cherry-pick $(git merge-base master ${BRANCH})..${BRANCH}

我发现 rebase 上的这篇文章对同一用例很有帮助。

假设您有如下提交

commit 6 [my-feature-branch] | merged onto master commit
commit 5
commit 4 [master]
commit 3
commit 2 [production]
commit 1

并且您只想将 6 个包含在生产中。

git checkout my-feature-branch
git rebase production 5

这里的 5 是你想要(不包括 5)进行更改并放在生产分支上的提交哈希。

使用rebase

    A _ _ _ B
  /
- - - C

git rebase --onto=target start end

git rebase --onto=C A B

    A
  /
- - - C - - - B

我很失望没有真正好的解决方案,所以我创建了自己的别名

aliasName = "!f() { git cherry-pick -n -Xsubtree $(str=$(git log  --grep 'Create branch' -n1 --oneline) && echo ${str:0:11})... ; }; f"

所以这看起来像

git aliasName my/branch-name-to-cherry-pick

我很幸运,我们对所有被剪切的分支都有一个通用名称,并且它们在消息中包含“创建分支”。此别名运行一个 bash 函数,该函数从与在指定分支参数 ($1) 上找到的 grep 匹配的第一个提交哈希匹配到分支头部。

由于名称匹配问题,它并不完美,但在我的情况下效果很好,也许对其他人也适用。