使用空提交消息重新设置git历史记录

16

我最近使用git svn将一个svn代码库转换为git。不幸的是,svn历史记录中存在一些空提交消息。当我在没有提交消息的最新提交之前对提交进行编辑/重命名时,这会成为一个问题。

$ git rebase -i d01
[detached HEAD ff9839c] asdf
 2 files changed, 9 insertions(+), 0 deletions(-)
Aborting commit due to empty commit message.
Could not apply 054e890... 

$ git branch
* (no branch)
  master

$ git commit --amend
fatal: You are in the middle of a cherry-pick -- cannot amend.
在这个例子中,我使用了一个空提交消息为第二个最近的提交做了一个提交,并且重置停止在带有空提交消息的最新提交上。
我想一次编辑所有具有空消息的提交。有没有办法可以做到这一点?也许我可以首先将所有带有空提交消息的提交更改为具有提交消息“empty”吗?

你将不再遇到这个问题,因为 Git 2.17(2018年第一季度,6年多以后)已经解决了。请参见下面的答案 - VonC
6个回答

13

要将空的提交消息替换为某个模板,您可以执行以下操作:

git filter-branch -f --msg-filter '
read msg
if [ -n "$msg" ] ; then
    echo "$msg"
else
    echo "The commit message was empty"
fi'

完全按照规定工作,使得变基变得更加容易!谢谢! - schmmd
9
如果您的提交消息包含换行符,则它们会被完全清除。我曾经吃过这方面的亏。 - lucasmo

10

如我之前所评论的,如果你的注释里包含换行符,这样做会完全破坏它们。下面是一个 Perl 脚本,可以在不破坏注释的情况下完成此操作:

#!/usr/bin/perl

my $data = "";    
while(<STDIN>) {
    $data .= $_;
}

if($data =~ /^\s*$/) { $data="[Empty message]\n"; }
print "$data";

然后,只需执行git filter-branch -f --msg-filter /path/to/perlfilter.pl


这个可以运行,但是我想知道为什么当我在git extensions中查看我的存储库的图形历史记录时,我会看到旧的提交和旧的空消息,以及另一个具有相同提交的分支,但是有新的消息? - Didier A.
@didibus,由于git filter-branch重写了历史记录并更改了SHA-1哈希值,所有重写的提交都会出现为新提交。删除指向旧版本的引用(我有refs/remotes/master和refs/origin/master挂在那里)从git日志输出中删除了重复的分支,但我认为这些提交仍然存在于数据库中。我还没有真正研究如何删除它们,但你应该在SO上找到一些使用git gc --prune或类似方法的东西。 - ChrAfonso

5

由于提交信息为空,提交被中止。

在Git 2.17(2018年第一季度)中,"git rebase"学会了使用新选项:"--allow-empty-message",因此您将不会遇到此问题。(在Git 2.19中,默认选项是"--allow-empty-message",请参见下文)

请参见提交a6c612b(由Genki Sky (``)于2018年2月4日提交)
(由Junio C Hamano -- gitster --合并于提交2f6128d,2018年2月21日)

rebase: 添加 --allow-empty-message 选项

此选项允许将带有空提交消息的提交进行变基,与 git-commitgit-cherry-pick 中的相同选项匹配。
虽然空日志消息不被看好,但有时您会在旧存储库中找到它们(例如从另一个版本控制系统翻译而来),或者有其他原因需要它们。
该选项在 git-commitgit-cherry-pick 中可用,因此自然而然地使其他 git 工具与它们协同工作。
将其作为选项添加可以使默认选项为“给用户修复的机会”,同时不会干扰用户的工作流程。

注意:Git 2.33 中删除了“空消息中止”部分(请参见下面的结尾)


Elan Ruusamäe 确认 在评论中

运行得很好。应用了提到的提交构建。+

git rebase --root HEAD --allow-empty-message 
 Successfully rebased and updated detached HEAD. 

git checkout -B master 

从 Git 2.19(2018年第三季度)开始,--allow-empty-message 是默认选项:

请见提交 b00bf1c, 提交 1634688, 提交 0661e49, 提交 4d34dff, 提交 983f464, 提交 c840e1a, 提交 9929430(2018年6月27日)和提交 d4e8062, 提交 5dacd4a(2018年6月25日),由Elijah Newren (newren)撰写。
(由Junio C Hamano -- gitster --合并于提交 0ce5a69,2018年7月24日)

git-rebase: 将--allow-empty-message设为默认值

rebase后端目前在处理空提交信息时表现不同,这在很大程度上是基于它们所基于的不同底层命令的副作用。
基于 am 的 rebase 应用带有空提交消息的提交而不会停止或要求用户指定额外的标志。
(有趣的是,基于 am 的 rebase 是默认的 rebase 类型,没有人要求使用 --no-allow-empty-message 标志来更改此行为。)
基于合并和交互式的 rebase(最终基于 git commit),当前将在任何此类提交上停止,并要求用户手动指定如何处理提交并继续。

差异行为的一个可能理由是,"am" 基础的 rebase 的目的仅仅是移植现有的历史记录,而 "交互式" rebase 的目的是在使其可发布之前对系列进行修整。
因此,在后者的情况下停止并询问可能存在的问题更加合适。

然而,这种理由存在两个问题:

    1. 基于合并的 rebase 也是非交互式的,并且有多种类型的 rebase 使用交互式机制,但并不是显式交互的(例如,当指定 --rebase-merges--keep-empty 而没有使用 --interactive 时)。
      这些 rebase 也仅用于移植现有的历史记录,因此也应默认为 --allow-empty-message
    1. 这种理由仅表示用户在显式交互式 rebase 的情况下更容易接受停止,而不是停止出于这种特定原因实际上是有意义的。
      探索是否有意义需要倒退并分析基础命令......

如果 git-commit 默认不对空提交报错,那么意外创建带有空消息的提交将是非常常见的情况(这个检查已经抓住了我很多次)。
此外,几乎所有这样的空提交消息都被认为是意外错误(这可以从版本控制系统和各种博客文章中的大量文档证明提交消息的重要性)。
因此,对于本来可能是常见错误的简单检查就显得非常有意义,git-commit 增加了一个 --allow-empty-message 标志,以进行特殊情况的覆盖。
这使得带有空消息的提交非常罕见。

rebase(和 cherry-pick)带有空消息的提交有两个来源:

  • (a) 在 git 中创建的提交,用户以前指定了 --allow-empty-message 给 git-commit,和
  • (b) 从其他版本控制系统导入到 git 中的提交。

在情况 (a) 中,用户已经明确指定此提交存在某些特殊之处,使他们不想指定提交消息;强制他们在每次 cherry-pick 或 rebase 时重新指定似乎更可能引起愤怒而不是帮助。

在情况 (b) 中,提交极不可能由导入历史记录并执行 rebase 或 cherry-pick 的人编写,因此


注意:Git 2.19 中的 "git rebase" 等操作在编辑后出现空提交日志消息时无法中止,这个问题已在 Git 2.20(2018年第四季度)得到修复。

序列化器:修复 --allow-empty-message 行为,使其更智能化

In commit b00bf1c ("git-rebase: make --allow-empty-message the default", 2018-06-27, Git v2.19.0-rc0), several arguments were given for transplanting empty commits without halting and asking the user for confirmation on each commit.

These arguments were incomplete because the logic clearly assumed the only cases under consideration were transplanting of commits with empty messages (see the comment about "There are two sources for commits with empty messages).

It didn't discuss or even consider rewords, squashes, etc. where the user is explicitly asked for a new commit message and provides an empty one.
(My bad, I totally should have thought about that at the time, but just didn't.)

Rewords and squashes are significantly different, though, as described by SZEDER:

Let's suppose you start an interactive rebase, choose a commit to squash, save the instruction sheet, rebase fires up your editor, and then you notice that you mistakenly chose the wrong commit to squash. What do you do, how do you abort?

Before [that commit] you could clear the commit message, exit the editor, and then rebase would say "Aborting commit due to empty commit message.", and you get to run 'git rebase --abort', and start over.

But [since that commit, ...] saving the commit message as is would let rebase continue and create a bunch of unnecessary objects, and then you would have to use the reflog to return to the pre-rebase state.

Also, he states:

The instructions in the commit message template, which is shown for 'reword' and 'squash', too, still say...

# Please enter the commit message for your changes. Lines starting
# with '#' will be ignored, and an empty message aborts the commit.

These are sound arguments that when editing commit messages during a sequencer operation, that if the commit message is empty then the operation should halt and ask the user to correct.
The arguments in commit b00bf1c (referenced above) still apply when transplanting previously created commits with empty commit messages, so the sequencer should not halt for those.

Furthermore, all rationale so far applies equally for cherry-pick as for rebase.

Therefore, make the code:

  • default to --allow-empty-message when transplanting an existing commit, and to
  • default to halting when the user is asked to edit a commit message and provides an empty one -- for both rebase and cherry-pick.

注意,当处理空提交时,请确保使用Git 2.25+(2020年第二季度)。请参见“ 如何填写空提交消息?”(最后一节)。

在 Git 2.26(2020年第一季度)中,文档已经更新。

请看提交 10cdb9f, 提交 2ac0d62, 提交 8295ed6, 提交 76340c8, 提交 980b482, 提交 c2417d3, 提交 6d04ce7, 提交 52eb738, 提交 8af14f0, 提交 be50c93, 提交 befb89c, 提交 9a70f3d, 提交 93122c9, 提交 55d2b6d, 提交 8a997ed, 提交 7db00f0, 提交 e98c426, 提交 d48e5e2 (2020年2月15日), 以及提交 a9ae8fd, 提交 22a69fd (2020年1月16日) 由Elijah Newren (newren)完成。
(由Junio C Hamano -- gitster --于2020年3月2日合并至提交 8c22bd9)

git-rebase.txt: 更新 --allow-empty-message 的描述

署名:Elijah Newren

提交b00bf1c9a8dd("git-rebase: make --allow-empty-message the default",2018-06-27,Git v2.19.0-rc0 -- merge列在批次#4中)将--allow-empty-message设置为默认值,因此将--allow-empty-message变成了无操作,但未更新文档以反映此更改。

现在更新文档,并从正常的-h输出中隐藏该选项,因为它没有用。

git rebase man page现在包括:

--allow-empty-message

无操作。
以前,具有空消息的提交会失败,此选项将覆盖该行为,允许重新提交具有空消息的提交。
现在,具有空消息的提交不会导致重新提交停止。


使用Git 2.26(2020年第一季度),"git rebase"默认使用合并后端(即驱动"rebase -i"的机制),同时允许"--apply"选项使用"apply"后端(例如,道德等效的"format-patch piped to am")。(可以设置rebase.backend配置变量进行自定义。)
它更好地管理空提交:

请查看 提交记录10cdb9f, 提交记录2ac0d62, 提交记录8295ed6, 提交记录76340c8, 提交记录980b482, 提交记录c2417d3, 提交记录6d04ce7, 提交记录52eb738, 提交记录8af14f0, 提交记录be50c93, 提交记录befb89c, 提交记录9a70f3d, 提交记录93122c9, 提交记录55d2b6d, 提交记录8a997ed, 提交记录7db00f0, 提交记录e98c426, 提交记录d48e5e2(2020年2月15日),以及提交记录a9ae8fd, 提交记录22a69fd (2020年1月16日)由Elijah Newren (newren)提交。
(由Junio C Hamano -- gitster --合并于提交记录8c22bd9,2020年3月2日)

rebase(交互式后端):修复处理变为空的提交的问题

签名作者:Elijah Newren

在之前的提交和提交b00bf1c9a8dd("git-rebase: make --allow-empty-message the default",2018-06-27,Git v2.19.0-rc0 -- 合并列在批次#4中),在各种边缘或特殊情况下,使用不同后端进行变基的行为往往更多是偶然而非设计。
这个提交解决了另一个边角情况:变为空的提交。仔细阅读者可能会注意到,由于变基,有两种类型的提交会变为空:
  • [干净的樱桃拣选] 是清洁的樱桃拣选上游提交的提交,由 git log --cherry-mark ... 确定。重新应用这些提交将导致一组空更改和重复的提交消息;即这些提交在上游已经被“应用”了。
  • [变为空] 是不是空的开始,不是干净的樱桃拣选上游提交的提交,但在被变基后仍然变为空。
    例如,当一个提交具有严格子集中的更改时,或者当一个提交的更改可以在几个上游提交之间分散或分布时,就会发生这种情况。
显然,在这两种情况下,所讨论的提交中的更改已经在上游找到了,但在后一种情况下,提交消息可能不在。 当樱桃标记能够确定提交已经在上游时,由于樱桃标记的工作方式,这意味着上游提交消息与完全相同的更改。 因此,可以假设提交消息是完全可互换的(实际上很可能完全相同)。 因此,清洁的樱桃拣选案例表示在保留额外提交时没有信息可以获得。 所有变基类型始终会删除这些提交,据我所知,从未有人要求我们做出其他处理。 对于许多变为空的情况(很可能是大多数情况),我们也将能够在不丢失信息的情况下删除提交--但并非总是如此。 由于这些提交代表了不干净的樱桃拣选的情况,因此没有上游提交消息解释相同的更改。 具有良好提交消息卫生的项目可能会将我们的提交消息说明包含在相关上游提交中或分散其中,但并非所有项目都运行该方式。 因此,正在变基的提交消息可能具有建议适应新基础的其他更改的推理,或者可能具有某人想要添加为另一个提交的注释的信息,或者甚至可能有人想要创建一个空提交,其中提交消息不变。 Junio 对“变为空”的提交发表了评论如下: 关于最终为空的更改(而不是从一开始就为空的更改),我认为当前的行为是期望的。基于“am”的变基仅是移植现有历史,并且希望停止比“交互式”的变基更少,从工作流程的角度来看,“询问(“这已经变为空了--你要删除它吗?”)更合适。 我只想补充说,他对于基于“am”的变基的论点实际上适用于所有非明确交互的变基。 此外,由于我们声明不同情况应该具有不同的默认值,因此可能值得提供一个标志,以允许用户选择他们想要的行为。 引入一个新的命令行标志来选择所需的行为:

--empty={drop,keep,ask}

其定义如下:
  • drop:删除变为空的提交
  • keep:保留变为空的提交
  • ask:为用户提供逐个选择变
    在Git 2.33(2021年第三季度)之前, "git commit --allow-empty-message"(man)不会因为空消息而中止操作,但编辑器中显示的提示与此相反。现已修复。

    请见提交 6f70f00, 提交 54ba2f1 (2021年7月10日) by 胡嘉伦 (SuibianP)
    (由Junio C Hamano -- gitster --提交 14793a4中合并,2021年7月28日)

    commit: 移除关于 --allow-empty-message 的无关提示

    帮助者:Junio C Hamano
    帮助者:Đoàn Trần Công Danh
    帮助者:Felipe Contreras
    签名作者:胡嘉伦

    即使使用了--allow-empty-message选项, "git commit"(man) 仍会提供一个交互式编辑器会话,其中填充的消息说,如果缓冲区被清空,提交将被中止,这是错误的。当使用该选项时,请从消息中删除 "an empty message aborts" 部分以修复它。

运行得非常好。使用提及的提交应用程序构建。 + git rebase --root HEAD --allow-empty-message 成功地重新设置了分离的HEAD并进行了更新。 + git checkout -B master - glen
@ElanRuusamäe 谢谢。我已经将您的评论包含在答案中,以增加其可见性。 - VonC

4

如果你有多行文本需要处理,你可以使用cat命令:

git filter-branch -f --msg-filter \
 'msg=$(cat); if [ "$msg" = "" ]; then echo "Here was an empty commit message"; else echo "$msg"; fi'

这处理具有多行的提交消息以及包含空行进行间距的消息。


2
你可以重新提交并继续,留空提交消息即可:
Aborting commit due to empty commit message.
Could not apply XXX... XXX
$ git commit --allow-empty -C $(<"$(git rev-parse --git-dir)/rebase-merge/onto")
$ git rebase --continue

0

你可以使用cherry pick:

git checkout -b temp_branch master
git checkout
git cherry-pick --allow-empty-message first_commit_to_include^..my_branch
# delete old my_branch and rename temp_branch to my_branch
git branch -D my_branch
git branch -m my_branch

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