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

声明:本页面是StackOverFlow热门问题的中英对照翻译,遵循CC BY-SA 4.0协议,如果您需要使用它,必须同样遵循CC BY-SA许可,注明原文地址和作者信息,同时你必须将它归于原作者(不是我):StackOverFlow 原文地址: http://stackoverflow.com/questions/10748749/
Warning: these are provided under cc-by-sa 4.0 license. You are free to use/share it, But you must attribute it to the original authors (not me): StackOverFlow

提示:将鼠标放在中文语句上可以显示对应的英文。显示中英文
时间:2020-09-19 06:58:11  来源:igfitidea点击:

How to rename commit messages in Git?

git

提问by martin's

I understand that this is a bad idea for lots of scenarios. I am learning Git and experimenting. No code will be harmed in this exercise.

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

I have created a structure like this:

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

* [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

Now I want to go through this graph and rename various commits so that they make sense.

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

For example:

例如:

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

renamed to:

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

Note that:

注意:

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

are all branched out of:

都分支出:

[a74eb2a] checkout 1

I've experimented with

我已经尝试过

git rebase -i 328454f

then changing "pick" to "edit" on the commits that I wanted to modify and subsequently running

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

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

as the rebase process continued.

随着 rebase 过程的继续。

The problem with this approach is that, after the last git rebase --continueI end-up with two new commits (duplicates of the two I wanted to rename) on the branch I happened to be on. For example, if I ran the rebase while HEAD was at "branch_2" the graph might look something like this:

这种方法的问题是,在最后一次之后,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

In other words I now have two sets of commits that represent exactly the same code state.

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

All I wanted to do was change the commit messages.

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

I also want to rename the initial message from "test" to something like "Initial version". I can't seem to do it with git commit --amend -m "Initial version"because I end-up in headless mode if I checkout that commit.

我还想将初始消息从“测试”重命名为“初始版本”之类的内容。我似乎无法做到这一点,git commit --amend -m "Initial version"因为如果我签出该提交,我最终会处于无头模式。

What am I doing wrong? Surely it can't be that hard.

我究竟做错了什么?当然不可能那么难。

EDIT:
Here's an approach I just tried that works. Of course, it re-writes history. So, outside of very special cases it is a bad idea. Here are the steps:

编辑:
这是我刚刚尝试过的一种方法。当然,它改写了历史。因此,在非常特殊的情况之外,这是一个坏主意。以下是步骤:

Checkout the branch to be modified. Create patch files:

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

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

Edit the patch files to change the commit message. Now reset the head.

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

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

Apply the patches:

应用补丁:

git am 000*

New commits will be created with new SHA1's. If any branches now need to reference the new commits with the corrected messages you have to use git rebaseto move them over.

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

To use my own example, after applying the patch procedure I ended-up with this:

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

* [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

So, I have my branch_2 commits nicely labeled. Now I want to move branch_2a so that it branches out of [53d638c] branch 2 commit 2

所以,我有我的 branch_2 提交很好地标记。现在我想移动 branch_2a 以便它从[53d638c] branch 2 commit 2

Checkout branch_2a

结帐 branch_2a

git checkout branch_2a

Rebase it

重新设置它

git rebase 53d638c

Now I have:

我现在有:

* [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

Same procedure with branch_2b results in this:

与 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

Which is exactly what I was looking for. Not too messy. Again, not something you'd want to do outside of very special cases. In my case I am just playing to learn Git, so the above isn't really affecting a real code repository. It's nice to know that you can do this if you have to.

这正是我正在寻找的。不会太乱。同样,除了非常特殊的情况外,您不想做任何事情。在我的情况下,我只是为了学习 Git,所以上述内容并没有真正影响真正的代码存储库。很高兴知道您可以在必要时执行此操作。

Now on to renaming the very first commit.

现在重命名第一个提交。

回答by torek

You can't actually ever change a commit. If you make the tiniest single-bit change anywhere, from the spelling of the name in the email line to the exact second of the commit timestamp, you get a new, different commit with a new, different SHA1 (the SHA1 is the "true name", as it were, of each "object" in the git database, with commits being one of the four types of object).

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

One of the immutable pieces of a commit is its "parent commits", which build up the chain backwards from most-recent-commit to oldest-commit.

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

Hence, what git rebase -idoes is make a newcommit-chain, with each commit in the chain having the same contents/effects as the original commit plus or minus any changes you make during the interaction. When it is all done it removes the label (the sticky-note, as it were) from the end of the old commit-chain and paste it on the end of the new commit-chain. It starts by making a copy of the oldest commit to be modified/rebased. This one has the rebased parent (which can be the same parent commit as in the original chain, or a different parent: either way it is OK because it is a newcommit). Then it makes a copy of the next one in the old chain, but pointing to the new chain. It repeats all the way to the end of the old chain.

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

This is why all your other branches are now independent of your rebased branch. They haveto be, because they use the old commit IDs. If you want them to branch off your new rebased branch, you must go into each of those and rebase them.

这就是为什么您的所有其他分支现在都独立于您重新定位的分支的原因。他们必须是,因为他们使用旧的提交 ID。如果你想让它们从你的新的 rebase 分支中分支出来,你必须进入每个分支并对它们进行 rebase。

There is a powerful Swiss-Army-chainsaw-ish command, git filter-branch, that you can use to do a very large series of "redo lots of commits, making all new commits that have (mostly) the same contents as the originals", sort of git rebaseon steroids as it were, that you can use for this purpose. (Run it with --allto affect all branches.) Of course, since it does in fact redo all the commits, you wind up with a repo that is basically unrelated to the original repo.

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

It's tough to rewrite the initial commit (not impossible) because it has no parent, so regular rebasejust won't do it.1(filter-branchcan.) Because these change SHA1 IDs, and anyone who has cloned your repo is using / depending-on those, it's normally a feature that you can't go fiddling with commits like this. When you know nobody else is depending on some particular set of commits, then you can rebaseall you like, but you won't be going back to the initial commit because that will be in the shared parts of the repo. It's pretty rare (though of course not "never") that the entire thing all the way back to "initial commit" is all your own private stuff.

很难重写初始提交(并非不可能),因为它没有父级,因此常规rebase不会这样做。1filter-branch可以。)因为这些会更改 SHA1 ID,并且任何克隆了您的存储库的人都在使用 / 依赖于这些 ID,所以您通常无法摆弄像这样的提交。当你知道没有其他人依赖于某些特定的提交集时,你可以rebase随心所欲,但你不会回到最初的提交,因为那将在 repo 的共享部分。非常罕见(尽管当然不是“从不”),直到“初始提交”的整个过程都是您自己的私人内容。



1Since I wrote this, git rebasehas learned to copy a root commit like the initial commit.2Using conventional git rebasesyntax, you would have to name the parent commit of the root, and of course there isno parent (that's what makes it a "root" in the graph). So rebaseuses --rootas an argument to cover this case.

1自从我写这篇文章以来,我git rebase学会了像初始提交一样复制根提交。2使用传统的git rebase语法,你就必须命名父根的承诺,当然也有无父(这是什么使得它在图中的“根”)。所以rebase使用--root作为参数来弥补这一情况。

2It's possible to have multiple roots; use git checkout --orphan, for instance, followed by git commit, to create a new root commit. It's a bit unusual to have these, although the git source itself is kept in a git repo with multiple roots.

2可以有多个根;git checkout --orphan例如,使用, 后跟git commit, 创建一个新的根提交。尽管 git 源本身保存在具有多个根的 git 存储库中,但拥有这些有点不寻常。