我想进行 rebase 以从我的历史记录中删除某个提交。我知道该怎么做。但是,如果我这样做,提交时间戳将设置为我完成 rebase 的那一刻。我希望提交保留时间戳。
我在这里看到了最后一个答案:https://stackoverflow.com/a/19522951/3995351 ,但是没有用。
最后一个重要的命令只是显示了一个新行
>
所以我提出了一个新问题。
最佳答案
设置
假设这是您要删除的提交的历史记录
... o - o - o - o ... ... o
^ ^ ^ ^
| | +- next |
| +- bad +-- master (HEAD)
start
在哪里:
bad
是您要删除的提交; start
是要删除的提交的父级; next
是 bad
之后的下一次提交;很好,你想保留它以及它之后的所有时间线;它将取代 bad
rebase 后。 先决条件
为了能够安全删除
bad
,重要的是在 bad
时不存在其他分支在bad
之后被创建并入主时间线. IE。通过删除 bad
并且它与它的父子节点的连接从历史图中提交,你会得到两个断开的时间线片段。可能可以删除
bad
即使在 bad
之后 merge 了另一个现有分支.我没有检查这种情况,但由于 merge 提交,我预计会遇到一些障碍。想法
每个
git
提交由使用提交的属性计算的哈希标识:内容、消息、作者和提交者日期和电子邮件。rebase 总是会更改提交者日期。它还可以更改提交者电子邮件、提交消息和内容。
为了在 rebase 后恢复原始提交者日期,我们需要将它们与一些可以在 rebase 后识别每个提交的信息一起保存。
因为您要修改提交,所以在 rebase 期间提交内容会发生变化。添加或删除文件或提交会更改所有 future 提交的内容。
这使我们没有唯一标识提交并且在所需的 rebase 期间不会更改的属性。我们可以尝试使用两个或多个在 rebase 期间不会改变的属性。
电子邮件(作者和提交者)几乎没有用。如果只有一个人参与该项目,则所有提交都相同,不能使用。保留的属性(在大多数提交中不同,不受 rebase 影响)是 作者日期 和 提交消息 (第一行)。
如果这对(作者日期,提交消息)为受 rebase 影响的所有提交提供唯一值,那么我们可以在之后恢复提交日期而不会出错。
验证是否可以安全完成
有一种简单的方法可以验证(作者日期、提交消息)对对于受影响的提交是否唯一。
运行以下两个命令:
$ git log --format="%aI %s" start...master | uniq | wc -l
$ git log --oneline start...master | wc -l
如果它们显示相同的数字,那么您很幸运:这对(作者日期,提交消息)可用于唯一标识提交。继续阅读。
如果数字不同(第一个命令产生的数字总是小于或等于第二个命令产生的数字),那么你就不走运了。
在 rebase 后提取修复提交日期所需的信息
这个命令
$ git log --format="%H %cI %aI %s" start...master > /tmp/hashlist
提取以
start
开头的所有提交的提交哈希、提交者日期(有效负载)、作者日期和提交消息( key )。并将它们存储在一个文件中。备份当前 master
虽然这是一个常见的误解
git
“重写历史”,实际上它只是生成一条替代历史线并确定它是正确的历史。它不会更改或删除“重写”提交;它们在其数据库中仍存在一段时间,并且可以在操作失败时恢复。我们可以主动备份当前历史行,以便在需要时轻松恢复它。我们所要做的就是创建一个指向
master
的新分支。 .这样,当 git rebase
移动 master
对于新时间线,旧时间线仍然可以使用新分支访问。$ git branch old_master
上面的命令创建了一个名为
old_master
的分支在我们完成所有更改并对新的世界秩序感到满意之前,它会一直关注当前的时间表。做rebase
删除提交
bad
从历史来看很简单:$ git rebase --preserve-merges --onto start bad
修复提交日期
以下命令使用我们之前保存的值“重写”历史记录并更改提交者日期:
$ 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
工作原理 :
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)
运行
git log -1 --format="%aI %s" $GIT_COMMIT
生成上面讨论的 key 对(作者日期,提交消息)。它的输出作为参数传递给命令 fgrep -m 1 "..." /tmp/hashlist | cut -d" " -f2
在先前保存的哈希列表( fgrep
)中找到该对,并从保存的行( cut
)中提取原始提交日期。最后,提交日期的值存储在环境变量 GIT_COMMITTER_DATE
中。由 git
使用重写提交。确认
使用
git log
再次命令$ git log --format="%cI %aI %s" start...master
您可以验证重写的历史记录是否与原始历史记录匹配。如果您使用图形
git
客户您可以通过目视检查更容易地检查结果。分行old_master
保持旧的历史行在客户端可见,您可以轻松比较 old_master
的每次提交的日期。与 master
对应的分支分支。如果事情不顺利或者您需要修改程序,您可以通过以下方式轻松重新开始:
$ git reset --hard old_master
清理
当您对结果感到满意时,您可以删除备份分支和用于存储原始提交日期的文件:
$ git branch -D old_master
$ rm /tmp/hashlist
就这样!
关于git - 如何制作 git rebase 并保留提交时间戳?,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/30790645/