如何 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
是默认分支,并且您希望在分支 B
与 devel
分支分离后从分支 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 匹配的第一个提交哈希匹配到分支头部。
由于名称匹配问题,它并不完美,但在我的情况下效果很好,也许对其他人也适用。
背景资料:
由于现有系统的工作流程限制,我们需要设置一个有点非正统的 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
是默认分支,并且您希望在分支 B
与 devel
分支分离后从分支 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 匹配的第一个提交哈希匹配到分支头部。
由于名称匹配问题,它并不完美,但在我的情况下效果很好,也许对其他人也适用。