如何在Git中重命名提交消息?

时间:2012-05-25 05:10:45

标签: git

我知道这对很多场景来说都是一个坏主意。我正在学习Git并进行实验。在此练习中不会损害任何代码。

我创建了一个这样的结构:

* [cf0149e] (HEAD, branch_2) more editing
* [8fcc106] some edit
|
|  * [59e643e] (branch_2b) branch 2b
| /
|/
|  * [0f4c880] (branch_2_a) branch 2a
| /
|/
*  [a74eb2a] checkout 1
*  [9a8dd6a] added branch_2 line
|
|
| * [bb903de] (branch_3) branch 3
|/
|
| * [674e08c] (branch_1) commit 1
| * [7d3db01] added branch_1 line
|/
* [328454f] (0.0.0) test

现在我想通过这个图并重命名各种提交,以便它们有意义。

例如:

    * | [a74eb2a] checkout 1
    * | [9a8dd6a] added branch_2 line

renamed to:

    * | [a74eb2a] branch 2 commit 2
    * | [9a8dd6a] branch 2 commit 1

请注意:

[cf0149e] (HEAD, branch_2) more editing
[59e643e] (branch_2b) branch 2b
[0f4c880] (branch_2_a) branch 2a

全部分支出来:

[a74eb2a] checkout 1

我已经尝试了

git rebase -i 328454f

然后在我想要修改并随后运行的提交中将“pick”更改为“edit”

git commit --amend -m "the new message" 

随着rebase过程的继续。

这种方法的问题在于,在最后一次git rebase --continue我最终提交了两个新提交(我要重命名的两个重复)后,我碰巧在。例如,如果我在HEAD位于“branch_2”时运行了rebase,那么图形可能如下所示:

* [cf0149e] (HEAD, branch_2) more editing
* [8fcc106] some edit
* [3ff23f0] branch 2 commit 2
* [2f287a1] branch 2 commit 1
|  
|    * [59e643e] (branch_2b) branch 2b
|   /
|  /
| |  * [0f4c880] (branch_2_a) branch 2a
| | /
| |/
| * [a74eb2a] checkout 1
| * [9a8dd6a] added branch_2 line
|/
|
| * [bb903de] (branch_3) branch 3
|/
|
| * [674e08c] (branch_1) commit 1
| * [7d3db01] added branch_1 line
|/
* [328454f] (0.0.0) test

换句话说,我现在有两组提交代表完全相同的代码状态。

我想做的就是更改提交消息。

我还想将初始消息从“test”重命名为“Initial version”。我似乎无法使用git commit --amend -m "Initial version"执行此操作,因为如果我签出该提交,我会以无头模式结束。

我做错了什么?当然不会那么难。

修改:
这是我尝试过的一种方法。 当然,它重写了历史。所以,在非常特殊的情况下,这是一个坏主意。 以下是步骤:

签出要修改的分支。 创建补丁文件:

git format-patch HEAD~x   // Where x is how far back from HEAD you need to patch

编辑补丁文件以更改提交消息。 现在重置头部。

git reset --hard HEAD~x   // Same x as before

应用补丁:

git am 000*

将使用新的SHA1创建新提交。 如果任何分支现在需要使用更正的消息引用新提交,则必须使用git rebase来移动它们。

使用我自己的例子,在应用补丁程序后,我最终得到了这个:

* [7761415] (HEAD, branch_2) branch 2 commit 4
* [286e1b5] branch 2 commit 3
* [53d638c] branch 2 commit 2
* [52f82f7] branch 2 commit 1
| * [bb903de] (branch_3) branch 3
|/
| * [59e643e] (branch_2b) branch 2b
| | * [0f4c880] (branch_2_a) branch 2a
| |/
| * [a74eb2a] checkout 1
| * [9a8dd6a] added branch_2 line
|/
| * [674e08c] (branch_1) commit 1
| * [7d3db01] added branch_1 line
|/
* [328454f] (0.0.0) test

所以,我的branch_2提交很好地标记了。 现在我想移动branch_2a,使其分支出[53d638c] branch 2 commit 2

结帐branch_2a

git checkout branch_2a

重新基础

git rebase 53d638c

现在我有:

* [fb4d1c5] (HEAD, branch_2a) branch 2a
| * [7761415] (branch_2) branch 2 commit 4
| * [286e1b5] branch 2 commit 3
|/
* [53d638c] branch 2 commit 2
* [52f82f7] branch 2 commit 1
| * [bb903de] (branch_3) branch 3
|/
| * [59e643e] (branch_2b) branch 2b
| * [a74eb2a] checkout 1
| * [9a8dd6a] added branch_2 line
|/
| * [674e08c] (branch_1) commit 1
| * [7d3db01] added branch_1 line
|/
* [328454f] (0.0.0) test

与branch_2b相同的程序导致:

* [ca9ff6c] (HEAD, branch_2b) branch 2b
| * [fb4d1c5] (branch_2a) branch 2a
|/
| * [7761415] (branch_2) branch 2 commit 4
| * [286e1b5] branch 2 commit 3
|/
* [53d638c] branch 2 commit 2
* [52f82f7] branch 2 commit 1
| * [bb903de] (branch_3) branch 3
|/
| * [674e08c] (branch_1) commit 1
| * [7d3db01] added branch_1 line
|/
* [328454f] (0.0.0) test

这正是我想要的。 不太乱。同样,在非常特殊的情况下,你不想做的事情。在我的情况下,我只是在玩Git,所以上面并没有真正影响真正的代码库。很高兴知道你可以这样做。

现在重命名第一次提交。

1 个答案:

答案 0 :(得分:18)

您实际上无法更改提交。如果您在任何地方进行最小的单位更改,从电子邮件行中的名称拼写到提交时间戳的确切秒,您将获得一个新的,不同的提交,使用新的不同SHA1(SHA1是“true”在git数据库中为每个“对象”命名“,”提交是四种对象之一。)

提交的一个不可变的部分是它的“父提交”,它从最近提交到最旧提交向后构建链。

因此,git rebase -i所做的是创建 new 提交链,链中的每个提交都具有与原始提交相同的内容/效果加上或减去您所做的任何更改在互动期间。完成所有操作后,它会从旧提交链的末尾删除标签(粘滞便笺),并将其粘贴到新提交链的末尾。它首先制作要修改/重新定位的最旧提交的副本。这个具有重新定位的父级(可以是与原始链中相同的父级提交,或者是不同的父级:无论哪种方式都可以,因为它是新的提交)。然后它会复制旧链中的下一个,但指向新链。它一直重复到旧链的末端。

这就是为什么所有其他分支机构现在都独立于您的重新分支机构。他们,因为他们使用旧的提交ID。如果你希望他们分支你的新重新分支,你必须进入每个分支并重新定义它们。

有一个强大的Swiss-Army-chainsaw-ish命令git filter-branch,您可以使用它来执行一系列非常大的“重做提交,使所有新提交(大多数)相同内容作为原件“,类似于git rebase类固醇,你可以用于此目的。 (使用--all运行它以影响所有分支。)当然,因为它实际上重做了所有提交,所以最终会得到一个与原始仓库基本无关的仓库。

重写初始提交很难(不是不可能),因为它没有父级,所以常规rebase就不会这样做。 1 filter-branch可以。 )因为这些更改了SHA1 ID,而且克隆了你的repo的任何人正在使用/依赖它们,所以它通常是一个你不能摆弄这样的提交的功能。当你知道没有其他人依赖于某些特定的提交时,那么你可以rebase所有你喜欢的,但你不会回到初始提交,因为那将在repo的共享部分。这是非常罕见的(虽然当然不是“永远不会”)整个事情一直回到“初始提交”都是你自己的私人事物。


1 自从我写这篇文章以来,git rebase已经学会了像初始提交一样复制根提交。 2 使用传统的git rebase语法,你必须为root的父提交命名,当然没有父(这就是图中的“root”)。因此rebase使用--root作为参数来涵盖此案例。

2 可能有多个根;例如,使用git checkout --orphan后跟git commit来创建新的根提交。虽然git源本身保存在具有多个根的git仓库中,但有这些有点不寻常。