git cherry-pick 在提交消息包含字符串/匹配正则表达式的范围内提交?

git cherry-pick commits in range where commit message contains string / matches regex?

背景/场景

我有一个本地工作流程

例如一种方法:

git checkout local
git checkout -b 123-my-feature
git rebase -i master

在交互式 rebase 编辑器中,删除消息不以 #123 开头的所有提交。 这留下了一个功能分支 123-my-feature,它只包含问题 #123 的提交。

稍后,在分支 123-my-feature 已经存在后,如果消息以 #123 开头,则本地的后续提交也需要被挑选/复制到 123-my-feature 分支。这也可以通过交互式 rebase 或单独的 cherry-picks 来实现。但是过程比较繁琐

问题

有没有办法用 cherry-pick 来做到这一点,并自动按提交消息过滤?

例如

git checkout master
git checkout -b 123-my-feature
git cherry-pick master..local --commit-message-begins-with="#123"

或者甚至是一个交互式的 cherry-pick,我可以在其中手动删除不以“#123”开头的提交。

如果有另一个命令可以实现这一点,为什么不呢。不必是精心挑选的。

不直接,不——但是编写一个脚本来完成它很容易。

大多数其他 Git 命令背后的通用命令是 git rev-list, which has a huge option list。不过,关于 git rev-list 首先要知道的是,它实际上与 git log 几乎是同一个命令。它们都采用相同的选项,并做大部分相同的事情,除了 git rev-list 旨在为其他 Git 程序生成哈希 ID——主要是提交的哈希 ID,而 git log 旨在向您或其他人展示提交的内容。

因为 git log--grep 可以让你 select 承诺根据消息显示,git rev-list 也有 --grep 可以让你 select 提交。例如,使用模式 '^#123: ' 会 select 提交其日志消息的行以 123:  开头(包括结尾的 space — 在此处选择您的模式并使用 git log 进行测试)。

git log 一样,git rev-list 会从你给它的任何开始(或结束?)点开始倒退,并一直持续下去,除非你告诉它在哪里停止 也在搜索。你需要选择一个停止点;通常这将是一个分支名称,或标签,甚至可能是一个原始哈希 ID,到 select 一个提交,其中 Git 应该终止其向后搜索提交。在这种情况下,停止点可能是 master。这的一般语法正是您所写的:master..local(或等效的 local ^master)。因此,您想要的提交列表是由以下内容生成的:

git rev-list --grep=... master..local

(试试 运行 宁这个,或者 - 甚至更好 - 首先 运行 git log,然后 运行 git rev-list 并观察差异)。

现在,还剩下一两个问题。第一个是像 git log,几乎大部分 Git,git rev-list 都是倒退的,从最新的提交开始,然后及时回溯。但是当你挑选樱桃时,你往往必须从最早的时间点开始并向前推进。还好git log--reverse,所以git rev-list也有--reverse.

最后一个明显的问题是 git rev-list 可能找不到任何提交。这种情况,你可以让git cherry-pick自己投诉,也可以检查自己投诉。让git cherry-pick抱怨:

git checkout -b 123-my-feature master
git cherry-pick $(git rev-list --reverse --grep '^#123' master..local)

(我将 123-my-feature 的分支创建与其初始值的设置结合起来,指向与 master 相同的提交,因此整个事情只是两行shell 脚本。要以更奇特的方式执行此操作,您可以先捕获 git rev-list 输出,并且仅在列表不为空时创建分支和 运行 cherry-pick。 )

GIT_SEQUENCE_EDITOR 不是很出名,它在文档中只提到过一次(在 git 配置页面中,对于 sequence.editor),但这正是您要查找的内容对于.

GIT_SEQUENCE_EDITOR='sed -i /^pick [^ ]* #123 /!d' git rebase -i master

你应该做的。