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

标签 git timestamp commit git-rebase

我想进行 rebase 以从我的历史记录中删除某个提交。我知道该怎么做。但是,如果我这样做,提交时间戳将设置为我完成 rebase 的那一刻。我希望提交保留时间戳。

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

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

>

所以我提出了一个新问题。

最佳答案

设置

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

... o - o - o - o ...       ... o
        ^   ^   ^               ^
        |   |   +- next         |
        |   +- bad              +-- master (HEAD)
      start

在哪里:
  • bad是您要删除的提交;
  • start是要删除的提交的父级;
  • nextbad 之后的下一次提交;很好,你想保留它以及它之后的所有时间线;它将取代 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/

    相关文章:

    version-control - 我应该什么时候提交并插入测试驱动开发?

    git - 有没有办法在远程存储库树上执行 git-grep 命令?

    git - 如何删除 Netlify 部署的分支

    php - 时间戳检查是否超过 12 小时

    git - 如何使用某些提交创建标签并将其推送到源?

    Git:即使我已经解决了冲突也无法提交文件

    git local master branch 停止跟踪 remotes/origin/master,无法推送

    git - 错误 :/c/Users/USER/AppData/Local/atom/bin/. ./app-1.36.1/resources/cli/atom.sh : line 23: cmd. exe:找不到命令

    c# - 如何在 .NET/C# 中获取刻度精度的时间戳?

    python - 为什么 Pandas 有自己的日期时间对象时间戳?