如何制作git rebase并保留提交时间戳?

The*_*her 2 git timestamp commit git-rebase

我想做一个rebase来从我的历史中删除某个提交.我知道该怎么做.但是,如果我这样做,则提交时间戳设置为我完成rebase的那一刻.我希望提交保持时间戳.

我在这里看到了最后一个答案:https: //stackoverflow.com/a/19522951/3995351,但它没有用.

最后一个重要的命令只显示了一个新行

>
Run Code Online (Sandbox Code Playgroud)

所以我开了一个新问题.

axi*_*iac 10

设置

假设这是您要删除的提交的历史记录

... o - o - o - o ...       ... o
        ^   ^   ^               ^
        |   |   +- next         |
        |   +- bad              +-- master (HEAD)
      start
Run Code Online (Sandbox Code Playgroud)

哪里:

  • bad 是您要删除的提交;
  • start 是您要删除的提交的父级;
  • next是下一个提交之后bad; 它很好,你想保留它和它之后的所有时间表; 它将bad在rebase之后取代.

先决条件

为了能够安全地删除bad,重要的bad是在创建时没有其他分支被合并到主时间轴之后bad.即通过bad从历史图表中删除它与其父和子提交的连接,您将获得两个断开连接的时间轴片段.

bad即使另一个现有分支合并后,也可能删除bad.我没有检查这种情况,但由于合并提交,我预计会遇到一些障碍.

这个想法

每个git提交都由使用提交的属性计算的哈希标识:内容,消息,作者和提交者日期和电子邮件.

rebase始终会更改提交者日期.它还可以更改提交者电子邮件,提交消息和内容.

为了在rebase之后恢复原始提交者日期,我们需要将它们与可以识别rebase之后的每个提交的一些信息一起保存.

因为您要修改提交,所以提交内容在rebase期间会发生更改.添加或删除文件或提交会在将来的所有提交中更改内容.

这使我们没有唯一标识提交的属性,并且在期望的rebase期间不会更改.我们可以尝试使用两个或多个在rebase期间不会更改的属性.

电子邮件(作者和提交者)几乎没用.如果有一个人在项目上工作,则所有提交都是相同的,不能使用.保留的属性(在大多数提交中不同,不受rebase的影响)是作者日期提交消息(第一行).

如果对(作者日期,提交消息)为受rebase影响的所有提交提供唯一值,那么我们可以在之后恢复提交日期而不会出现错误.

验证是否可以安全地完成

有一种简单的方法可以验证(作者日期,提交消息)对是否对受影响的提交是唯一的.

运行以下两个命令:

$ git log --format="%aI %s" start...master | uniq | wc -l
$ git log --oneline start...master | wc -l
Run Code Online (Sandbox Code Playgroud)

如果它们显示相同的数字,那么您很幸运:该对(作者日期,提交消息)可用于唯一标识提交.继续阅读.

如果数字不同(第一个命令将始终产生小于或等于第二个命令产生的数字),那么你运气不好.

提取修改rebase之后的提交日期所需的信息

这个命令

$ git log --format="%H %cI %aI %s" start...master > /tmp/hashlist
Run Code Online (Sandbox Code Playgroud)

提取所有提交的提交哈希,提交者日期(有效负载),作者日期和提交消息(密钥),start并将其存储在文件中.

备份当前主服务器

虽然git"重写历史" 是一种常见的误解,但事实上它只是生成了另一种历史记录,并确定它是正确的历史记录.它不会更改或删除"重写"提交; 它们在数据库中仍然存在一段时间,并且可以在操作失败时恢复.

我们可以主动备份当前历史记录行,以便在需要时轻松恢复.我们所要做的就是创建一个指向的新分支master.这样,当git rebase移动master到新时间轴时,仍然可以使用新分支访问旧时间轴.

$ git branch old_master
Run Code Online (Sandbox Code Playgroud)

上面的命令创建了一个名为的分支old_master,它保持当前时间轴的焦点,直到我们完成所有更改并对新的世界顺序感到满意为止.

做坏事

bad从历史记录中删除提交非常简单:

$ git rebase --preserve-merges --onto start bad
Run Code Online (Sandbox Code Playgroud)

修复提交日期

以下命令使用我们之前保存的值"重写"历史记录并更改提交者日期:

$ git filter-branch --env-filter 'export GIT_COMMITTER_DATE=$(fgrep -m 1 "$(git log -1 --format="%aI %s" $GIT_COMMIT)" /tmp/hashlist | cut -d" " -f2)' -f start...master
Run Code Online (Sandbox Code Playgroud)

工作原理:

git在标记的提交之间遍历历史记录start,master并且对于每个提交,它--env-filter在重写提交之前运行作为参数提供的命令.它GIT_COMMIT使用正在重写的提交的哈希设置环境变量.

由于我们已经rebase修改了所有提交的哈希值,因此我们无法$GIT_COMMIT直接使用它来识别提交的原始提交日期(因为$GIT_COMMIT是生成的提交git rebase,我们对提交者日期不感兴趣).

我们提供的命令 --env-filter

export GIT_COMMITTER_DATE=$(fgrep -m 1 "$(git log -1 --format="%aI %s" $GIT_COMMIT)" /tmp/hashlist | cut -d" " -f2)
Run Code Online (Sandbox Code Playgroud)

运行git log -1 --format="%aI %s" $GIT_COMMIT以生成上面讨论的密钥对(作者日期,提交消息).它的输出作为参数传递给在fgrep -m 1 "..." /tmp/hashlist | cut -d" " -f2先前保存的哈希(fgrep)列表中找到该对的命令,并从保存的行(cut)中提取原始提交日期.最后,提交日期的值存储在GIT_COMMITTER_DATE用于git重写提交的环境变量中.

验证

git log再次使用该命令

$ git log --format="%cI %aI %s" start...master
Run Code Online (Sandbox Code Playgroud)

您可以验证重写的历史记录是否与原始历史记录匹配.如果使用图形git客户端,则可以通过目视检查更轻松地检查结果.分支old_master使旧历史记录行在客户端中可见,您可以轻松地将每个old_master分支提交的日期与分支的相应日期进行比较master.

如果某些事情进展不顺利或您需要修改程序,您可以轻松地重新开始:

$ git reset --hard old_master
Run Code Online (Sandbox Code Playgroud)

清理

如果对结果满意,可以删除备份分支和用于存储原始提交日期的文件:

$ git branch -D old_master
$ rm /tmp/hashlist
Run Code Online (Sandbox Code Playgroud)

就这样!


Dav*_*sch 6

因此,这是一种乏味的方法(取决于您需要变基的提交数量),但我尝试了一下并且它有效。当您进行交互式变基时,请用“e”标记每个提交,以便您可以对其进行编辑。这将导致 git 在每次提交后暂停。每次暂停时,您可以指定要使用的日期并继续下一次提交:

GIT_COMMITTER_DATE="Wed Feb 16 14:00 2011 +0100" git commit --amend   
git rebase --continue
Run Code Online (Sandbox Code Playgroud)

或者,如果您想让提交者和作者日期保持相同:

GIT_COMMITTER_DATE="Wed Feb 16 14:00 2011 +0100" git commit --amend --date "Wed Feb 16 14:00 2011 +0100"
git rebase --continue
Run Code Online (Sandbox Code Playgroud)

如果您不希望打开编辑器来更改提交,请添加--no-editafter 。--amend

当然,这是一个很大的痛苦,你必须事先知道所有的提交日期,但如果你不能以任何其他方式做到这一点,它至少应该有效。