如何通过程序编辑旧的git提交信息?

17

您可以通过编程的方式仅编辑最后一次提交的消息:

git commit --amend -m 'xxxxxxx'

或者交互式地随机提交:

git rebase -i HEAD~n
# Vim opens up, select the commit you want to modify, and change the word "pick" for "edit"
git commit --amend -m "Changing an old commit message!"
git rebase --continue

我该如何进行合并?我想以程序方式修改消息,但不仅限于最后一个提交,而是要修改到之前的某个提交。

我想要修改的提交已经被推送到 Git 服务器上,但不必考虑其他人重新同步 Git 项目的问题。


2
为什么rebase -i对你不起作用? - pishpish
2
@destoryer。我认为这个问题在两个方面都非常清楚... - Mad Physicist
1
相关:https://dev59.com/D18e5IYBdhLWcg3wJXrm 和 https://dev59.com/EXDYa4cB1Zd3GeqPGPdV - jub0bs
3
git rebase -i 命令有一个 "reword" 选项。如果你只想修改一个提交记录的提交信息,那么使用此选项即可。 - Schwern
1
@AndrewC 你提供的链接讨论的是交互式解决方案,而不是编程解决方案。我不明白为什么链接的问题会是重复的。 - Jesus H
显示剩余5条评论
4个回答

15

你不能简单地“修改”一个任意的提交的原因在于提交是不可变的。当你修改一个提交时,它实际上会用另一个提交替换当前的提交并将你的分支移动到新的提交。具有旧消息、作者名称等的提交仍然存在于历史记录中,直到你清理它为止:

Before:

        master
          |
          v
A -- B -- C

After:

        master
          |
          v
A -- B -- C'
      \
       \- C

要模拟“修改”任意提交,你不仅需要重写该提交,还需要重写其后的整个历史记录,因为提交作为其不可变数据的一部分包括其父提交:

Before:

        master
          |
          v
A -- B -- C

After:

         master
           |
           v
A -- B' -- C'
 \ 
  \- B --- C

你可以通过在你感兴趣的提交上创建一个分支,进行修改,并将原始提交后面的所有提交范围重新基于新分支放置到原始分支的末尾来实现这一点。以下是一个示例,展示了你想要的操作:

Start:

             master
               |
               v
A -- B -- C -- D

New Branch:

             master
               |
               v
A -- B -- C -- D
     ^
     |
    temp

Amend:

             master
               |
               v
A -- B -- C -- D
 \
  \- B'
     ^
     |
    temp

Rebase:

A -- B  -- C  -- D
 \
  \- B' -- C' -- D'
     ^           ^
     |           |
    temp       master

Cleanup:

A -- B  -- C  -- D
 \
  \- B' -- C' -- D'
                 ^
                 |
               master

顺便提一下,这与交互式变基仅修改单个提交时的操作几乎完全相同,但没有显式的临时分支。


13
如果您只想更改几个提交,请使用git rebase -i和“reword”选项。例如...
pick 6256642 mv file1 file2
pick 20c2e82 Add another line to file2

# Rebase 8236784..20c2e82 onto 8236784 (2 commands)
#
# Commands:
# p, pick = use commit
# r, reword = use commit, but edit the commit message
# e, edit = use commit, but stop for amending
# s, squash = use commit, but meld into previous commit
# f, fixup = like "squash", but discard this commit's log message
# x, exec = run command (the rest of the line) using shell
# d, drop = remove commit

pick 改为 reword,然后你将获得一个编辑器来重新编写提交消息。


如果你需要对很多次提交执行相同的操作,请使用 git filter-branch 加上 --msg-filter。原始的提交消息在标准输入中,新的提交消息在标准输出中。这里是一个示例,在当前分支中更改所有提交中的 "color" 为 "colour"。

git filter-branch --msg-filter "perl -ple 's{color}{colour}g'"

filter-branch 应用于所有提交。是否可能将过滤器应用于特定提交?我尝试在末尾传递提交哈希,但 filter-branch 期望哈希..ref。如果我传递 hash..HEAD,则会应用于指定哈希之后的所有提交。如果不可能,我认为我可以使用提供的解决方案(通过解析 stdin 消息)。谢谢! - Jesus H
@JesusH git filter-branch 接受与 git rev-list 相同的修订参数,因此您可以想出一种指定一个提交的方法。例如,git rev-list HEAD^..HEAD 只需执行 HEAD。如果您只想执行一个提交,请使用 git rebase -i。我不明白为什么您想编写一个程序来更改一个提交的消息。这非常不寻常。如果您解释一下,我们可以更好地帮助您。 - Schwern

7

由于您希望以编程方式进行更改,交互式变基(git rebase -i)不是一个选项。

无论出于何种原因编辑旧的提交都将有效地对其上面的所有提交进行变基。如果您只更改提交消息,则无需担心合并冲突。

您可以创建一个以目标提交为其HEAD的新临时分支,编辑提交消息,将旧分支合并到新分支上,然后删除旧临时分支。

在shell脚本中:

CURBRANCH=`git rev-parse --abbrev-ref HEAD`
TMPBRANCH=tmp$$
git checkout $SHA -b $TMPBRANCH
MSG=`tempfile`
git log --format=%B -n 1 HEAD > $MSG
... edit the $MSG file
git commit --amend -F $MSG
SHA=`git rev-list -n 1 HEAD`   # Commit has change, so SHA has also changed
rm $MSG
git rebase --onto $TMPBRANCH HEAD $CURBRANCH
git branch -d $TMPBRANCH

0
不必重新基于和强制推送修改的分支,可以用不同的消息替换提交,而不影响现有的提交哈希值。
git checkout <commit>
git commit --amend -m "New message"
git replace <commit> $(git rev-parse HEAD)

这也可以通过互动方式完成:
git replace --edit <commit>

请注意,修改后的引用必须通过以下方式显式地推送和获取:
git push origin 'refs/replace/*'
git fetch origin 'refs/replace/*:refs/replace/*'

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