git rebase - 将原始提交哈希添加到提交信息中

14

在rebase操作中是否有办法像cherry-pick -x一样(将原始提交的哈希添加到复制提交的消息中)?

目前可以通过替换以下内容来解决问题。

git checkout other-branch
git rebase master
git checkout master
git merge other-branch
git checkout master
....
git cherry-pick -x other-branch^^^^
git cherry-pick -x other-branch^^^
git cherry-pick -x other-branch^^
git cherry-pick -x other-branch^
git cherry-pick -x other-branch

git rebase master -x 不起作用吗? - evolutionxbox
不,那是在每次提交后执行脚本,例如在每次提交后运行测试。如果可以这样做就太好了。 - Alex028502
另外,你为什么想要这个? - evolutionxbox
因为如果在解决冲突时犯了错误,至少在接下来的几个小时或几天内能够查看原始版本是很有好处的。 - Alex028502
2
你可以不用这样做。只需在变基之前创建一个分支。或者检查reflog。 - evolutionxbox
4个回答

9
它并不漂亮,但它完成了工作。
git rebase --exec='git log --pretty="format:%B" -n 1 > tmp;
    grep -o "\w\{40\}" .git/rebase-merge/done | tail -n 1 >> tmp; 
    git commit --amend -F tmp; 
    rm tmp;' master

解释--exec脚本的每个部分:

  • 将我们刚完成的提交消息放入tmp中;
  • .git/rebase-merge/done获取我们刚刚重新定位的提交哈希,并将其附加到tmp
  • 使用tmp作为提交消息修改我们刚刚进行的提交;
  • 删除tmp文件。

我相信你可以把它改成你喜欢的格式。

原始工作日志;

commit 1ebdfc2fd26b0eed9f131197dc3274f6d5048e97
Author: Adam
Date:   Thu Jan 24 16:33:09 2019 +0000

    Content C

commit 632f1a4e1ab5d47c9e4c3ca3abd02a207a5dda09
Author: Adam
Date:   Thu Jan 24 16:33:06 2019 +0000

    Content B

commit a7e0d1eb2e412ec51865ccd405ea513c7677c150
Author: Adam
Date:   Thu Jan 24 16:33:04 2019 +0000

    Content A

重新定位工作的日志;
commit 79d7ece06185b21631248a13416e5ca5c23e55b2
Author: Adam
Date:   Thu Jan 24 16:33:09 2019 +0000

    Content C
    1ebdfc2fd26b0eed9f131197dc3274f6d5048e97

commit d2fe6267165fa05f5fe489a6321b0b1742d1a74c
Author: Adam
Date:   Thu Jan 24 16:33:06 2019 +0000

    Content B
    632f1a4e1ab5d47c9e4c3ca3abd02a207a5dda09

commit da72fab2008e74f6a8e247f93619943805ebf86e
Author: Adam
Date:   Thu Jan 24 16:33:04 2019 +0000

    Content A
    a7e0d1eb2e412ec51865ccd405ea513c7677c150

1
太好了!我将它转换为可执行文件,以便能够像这样编写它: git rebase some-branch --exec update_commit_previous_hashes_listhttps://gist.github.com/maxlath/03cf6e6dcb525ff84f6c07b6ff58ed8e - maxlath
我一直在使用这个,但不幸的是,当它运行两次时,它可能会破坏最后一次提交。.git/rebase-merge/done 不会包含正确的信息,因为最后一次提交已经被 rebase,没有什么可做的了,但最后一次提交仍将被修改。 - deadalnix
@deadalnix 如果你运行两次,分别针对 branchA 和 branchB,最终提交会是 fubar 吗? - Adam
所以我进行了更深入的调查,虽然问题确实存在,但我描述问题时出现了错误。问题发生在重演过程中跳过一个提交时,例如因为该提交的内容已经包含在其他提交中,那么修改会将之前的提交与被跳过的提交的信息进行合并。 - deadalnix
当你对相同的代码进行两次变基时,就会出现这种情况,这最终会损坏一个提交。我最终使用了filter-branch来删除所有将被删除的提交,然后进行变基。我不知道这个解决方案是否适合你。 - deadalnix
@deadalnix 那很有道理。如果你想出一个解决方案,我很乐意将其添加到答案中。 - Adam

6
所以,这是我的解决方案:
git rebase <branch> \
-ix "git rev-parse --short HEAD > tmp &&  \
echo 'from: $<branch_shortid>' > tmp && \
git commit --amend -F tmp"

请确认branch_shortid正确,并且是在您希望从中变基内容的分支上生成的。

免责声明:我不确定这对所有情况都适用,特别是如果您有一些奇怪或复杂的引用系统。我在一个非常简单的git存储库上运行了此操作,该存储库是通过以下方式生成的:

$ git init 
$ echo "a" > a.txt && git add . && git commit -m "first commit"
$ git checkout -b "feature1" 
$ echo "b" > b.txt && git add . && git commit -m "second commit"
$ echo "c" > c.txt && git add . && git commit -m "third commit"
$ feature1id=$(git rev-parse --short HEAD)
$ git checkout master
$ git rebase feature1 \
  -ix "git rev-parse --short HEAD > tmp &&  \
  echo 'from: $feature1_id' > tmp && \
  git commit --amend -F tmp"

以下是对应的输出:

在此输入图片描述


讨论:

正如之前指出的,我认为您使用git reflog更好地解决了从哪个提交到哪个分支合并到所需分支的问题。

重新定位的目的是将提交应用于另一个分支的顶部,就像这是第一次的提交结构一样:

重新定位产生一个线性历史


这只是给出旧头部的原始提交ID,对吧?它不会放置每个提交的原始ID,从它被应用的地方。 - Alex028502
抱歉,给我一个小时。 - Alex028502
@Alex028502,你只是执行了 echo 'from: $feature1_id' > tmp,但是没有加上 git rev-parse --short HEAD > tmp - 9301293
这似乎是您在Bash脚本方面的误解。您可能想要查找shell变量以开始。如果您真的想要,可以省略“from”。那将简化为:git rebase <branch> \ -ix“git rev-parse --short HEAD > tmp && \ git commit --amend -F tmp” - 9301293
c2的新版本具有正确的哈希值,但是c3的新版本没有。必须有一种访问原始哈希值的方法,因为它在被应用的补丁中。 - Alex028502
显示剩余2条评论

2

https://dev59.com/e1QJ5IYBdhLWcg3wSTx1#54352290的基础上,我最终得到了以下脚本:

Original Answer翻译成"最初的回答"

#! /usr/bin/env bash

set -e

if [[ "$@" != "" ]];
then
    git rebase $@ --exec="$0"
    exit 0
fi

HASH="$(grep -o "\w\{40\}" .git/rebase-merge/done | tail -n1)"

git log --pretty="format:%B%n%nwas $HASH" -n1 | git commit --amend -F -

我希望避免使用临时文件。最初我用两个脚本实现,但后来将它们合并为一个递归脚本,以便更容易共享。我将其命名为git-record,因此如果您运行git record HEAD^^^^,它将在所有提交中放置当前哈希值(并在此过程中更改所有哈希值)。

注:Original Answer的翻译是“最初的回答”。


1
如果你只是使用rebase做批量cherrypick,你可以轻松地自己构建它的pick列表,并使用任何你想要的选项进行cherrypicks:
batchxpickto() {
        local U=${1-@{u\}}      # given branch or default to configured upstream 
        local B=`git symbolic-ref -q --short HEAD`  # branch name to move if any
        local L=`git cherry $U.. | awk /^+/{print\$2}`  # commits to pick if any
        git checkout $U && ${L:+git cherry-pick -x $L}
        ${B:+git checkout -B $B}
}

使用batchxpickto master的方式,就像使用git rebase master一样。
实际上,我更喜欢Adam's answer,但这是另一种选择,其策略可能更普遍适用。

我认为自己在bash和git方面相当熟练,但是我完全不理解那些魔法操作!{L:+ git的作用是什么?你能给我提供任何bash文档的链接吗? - Adam
1
@Adam man bash 并查找参数扩展,变量名周围的大括号允许修改扩展,:+ 表示“如果变量已设置且不为空,则扩展为以下内容”,:- 表示“如果变量未设置或为空,则扩展为以下内容”,省略 : 以省略空测试。因此 ${1-@{u\}} 是第一个参数(如果已设置),否则是带有反斜杠的 @{u},其中反斜杠是通常的语法转义;${L:+git cherry-pick -x $L} 如果没有要挑选的内容,则扩展为空;等等。这不是特定于 bash 的,这是被低估的标准内容。 - jthill

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