【问题标题】:Rewrite a git repository to change the date range of commits重写 git 存储库以更改提交的日期范围
【发布时间】:2014-10-01 14:54:24
【问题描述】:

假设,我有一个相当大的 Git 存储库,其中包含 1000 多个提交。提交日期从 2013 年 8 月到现在(2014 年 8 月)。所有提交都由一个用户(我)完成。

现在,出于某种原因,我需要让存储库中的所有提交看起来都发生在 2014 年 3 月到现在之间。

这可以通过更改现有存储库或创建一个新存储库并重新提交所有更改来实现。

如果是少量提交,我会手动检查每个修订版并使用--date 开关将状态提交到新存储库,如Git documentation 中所述。

但是,对于提交的数量,这是不可能的。

【问题讨论】:

标签: git


【解决方案1】:

git filter-branch 就是这样做的。

git filter-branch 列出了应该使用的提交和分支名称。正如文档所说(有点神秘):

该命令只会重写命令行中提到的 refs ...

在您的情况下,这可能意味着您希望 --all 覆盖所有分支,这巧合(或不巧合,真的)还告诉 filter-branch 脚本查看存储库中的所有内容(即,所有提交,以及所有标签/注释标签)。这是因为--all 参数被提供给git rev-list,它列出了所有提交(和带注释的标签)。

filter-branch 脚本通过迭代每个命名的修订来工作。对于那些提交,它应用所有指定的(非标记)过滤器。这里最适合使用的是--env-filter

(对于那些是标签的,如果有的话,它会应用给定的标签名过滤器。如果没有给出,它不会对标签做任何事情。因此,您可能需要--tag-name-filter cat,如示例中所述。有关详细信息,请参阅文档。)

一旦脚本应用了您的过滤器,它就会进行新的提交,1 包含您所做的任何更改。您的过滤器通常2馈送到 shell 的 eval,它允许您设置环境变量。在这种情况下,关键的环境变量是控制提交时间戳的两个:GIT_AUTHOR_DATEGIT_COMMITTER_DATE

您的环境过滤器应首先从提交中提取现有日期,其 ID 在$GIT_COMMIT 中提供给您。如果这些日期超出要修改的范围,您可以取消设置相应的环境变量,或者将其设置为原始提交的日期,以便现有的日期和时间戳将用于新的提交也是如此。但是,如果它们您的“更改范围”内,您需要将变量设置(并再次export)为所需的新值。

你会想要/需要改进它(可能很多,而且它还没有经过测试),但是 env 过滤器可能看起来像这样:

--env-filter 'at=$(git log --no-walk --pretty=format:%ai $GIT_COMMIT) \
    ct=$(git log --no-walk --pretty=format:%ci $GIT_COMMIT); \
    export GIT_AUTHOR_DATE=$($HOME/scripts/massage-time $at) \
    GIT_COMMITTER_DATE=$($HOME/scripts/massage-time $ct)'

其中$HOME/scripts/massage-time 是您编写的脚本,用于获取时间戳(此处,通过 %ai 和 %ci 格式化;选择您自己喜欢的格式)并将其按摩到您的转换范围内。事实上,您的massage 脚本可以直接使用环境变量$GIT_COMMIT,并简单地生成export GIT_AUTHOR_DATE=... 命令作为输出(因为同样,您提供的过滤器的输出被馈送到eval)。 (不过,出于测试目的,最好将提交 ID 作为参数。然后您可以手动确保它对各种示例提交执行正确的操作,然后再将其用作环境过滤器。)

一旦filter-branch 脚本完成了所有这些新提交,它就会进行引用名称重写,以将每个引用名称指向与原始副本对应的任何新副本提交。例如,如果refs/heads/master 曾经指向提交badface,而badface 的副本是deadb17,则脚本使refs/heads/master 现在指向deadb17。这几乎是所有 git 命令的工作方式:它们只是添加新内容到存储库,同时将旧内容留在其中,并创建或移动引用标签以指向新内容。如果旧的东西最终变得不被引用,git gc 可以在那时将其删除。


1此时它实际上运行了您的提交过滤器,但提供了一个默认过滤器来进行新的提交。如果您提供自己的提交过滤器,则提交成为您的责任;这允许您省略一些提交。

2eval 规则适用于除提交过滤器之外的所有内容。您可以自己检查 filter-branch 脚本以查看:它位于 git-core 目录中,通常位于 /usr/local/libexec/git-core/usr/libexec/git-core 中,具体取决于 git 安装。

【讨论】:

  • 感谢@torek 提供的详细答案,这正是我想要的。
【解决方案2】:

BASH 吧 :) 注意以下是凭记忆写的,没有经过测试,所以我会放评论以协助解释

# Run git logs to get all the commit ids you want
git log --before={2014-03-01} --after={2013-01-01} --author="your name" > filename

# Run grep, so you can isolate the commit ids
grep "commit" filename > commit_ids

# Run a bash loop on those ids and change date
for i in $(cat commit_ids); do echo $i; git commit  --amend --date "`date`" $i; done;

【讨论】:

  • git commit --amend 只会让你替换最尖端的提交。 (具体来说,它不允许使用 commit-ID 参数。)
猜你喜欢
  • 1970-01-01
  • 2018-04-25
  • 2020-07-06
  • 1970-01-01
  • 1970-01-01
  • 2014-04-23
  • 1970-01-01
  • 2012-10-13
  • 2014-10-23
相关资源
最近更新 更多