【问题标题】:How do I make a Git commit in the past?我如何在过去进行 Git 提交?
【发布时间】:2014-05-16 18:23:26
【问题描述】:

我正在将所有内容都转换为 Git 以供我个人使用,并且我在存储库中发现了一些旧版本的文件。如何根据文件的“修改日期”以正确的顺序将其提交到历史记录,以便获得准确的文件历史记录?

有人告诉我这样的事情会起作用:

git filter-branch --env-filter="GIT_AUTHOR_DATE=... --index-filter "git commit path/to/file --date " --tag-name-filter cat -- --all  

【问题讨论】:

  • 我想知道寻找答案的人是否只是想以这种方式保持他们的 GitHub“贡献连续性”连续
  • @ZitRo 是的。如果有人想知道,只需设置 git commit --date="xxx day ago" -m "yyy" 就足够了。
  • alexpeattie.com/blog/working-with-dates-in-git : 如果需要一个温和的解释
  • @ZitRo 一个更好的用途是让你对个人项目的所有提交都发生在周末。以防万一雇主想要认领你的工作,CYA。

标签: git version-control repository git-commit


【解决方案1】:

你得到的建议是有缺陷的。在--env-filter 中无条件设置 GIT_AUTHOR_DATE 将重写每次提交的日期。此外,在 --index-filter 中使用 git commit 也是不寻常的。

您在这里处理多个独立的问题。

指定“现在”以外的日期

每个提交都有两个日期:作者日期和提交者日期。您可以通过环境变量 GIT_AUTHOR_DATE 和 GIT_COMMITTER_DATE 为写入新提交的任何命令提供值来覆盖每个。请参阅“Date Formats” in git-commit(1) 或以下内容:

Git internal format = <unix timestamp> <time zone offset>, e.g.  1112926393 +0200
RFC 2822            = e.g. Thu, 07 Apr 2005 22:13:13 +0200
ISO 8601            = e.g. 2005-04-07T22:13:13

在正常使用期间写入新提交的唯一命令是 git commit。它还有一个--date 选项,可让您直接指定作者日期。您的预期用法包括 git filter-branch --env-filter 还使用了上面提到的环境变量(这些是“env”的一部分,以该选项命名;参见 “Options” in git-filter-branch(1) 和底层的“管道”命令 git-commit-tree(1)

将文件插入单个ref历史记录

如果您的存储库非常简单(即您只有一个分支,没有标签),那么您可能可以使用 git rebase 来完成这项工作。

在以下命令中,使用提交的对象名称(SHA-1 哈希)而不是“A”。 运行 git commit 时不要忘记使用“日期覆盖”方法之一。

---A---B---C---o---o---o   master

git checkout master
git checkout A~0
git add path/to/file
git commit --date='whenever'
git tag ,new-commit -m'delete me later'
git checkout -
git rebase --onto ,new-commit A
git tag -d ,new-commit

---A---N                      (was ",new-commit", but we delete the tag)
        \
         B'---C'---o---o---o   master

如果您想更新 A 以包含新文件(而不是在添加文件的位置创建新提交),请使用 git commit --amend 而不是 git commit。结果如下所示:

---A'---B'---C'---o---o---o   master

只要您可以将提交命名为新提交的父提交,上述内容就可以使用。如果您确实希望通过新的根提交(没有父项)添加新文件,那么您需要一些不同的东西:

B---C---o---o---o   master

git checkout master
git checkout --orphan new-root
git rm -rf .
git add path/to/file
GIT_AUTHOR_DATE='whenever' git commit
git checkout -
git rebase --root --onto new-root
git branch -d new-root

N                       (was new-root, but we deleted it)
 \
  B'---C'---o---o---o   master

git checkout --orphan 相对较新(Git 1.7.2),但有 other ways of doing the same thing 可以在较旧版本的 Git 上运行。

将文件插入多ref历史记录

如果您的存储库更复杂(即它有多个引用(分支、标签等)),那么您可能需要使用 git filter-branch在使用 git filter-branch 之前,您应该制作整个存储库的备份副本。 整个工作树的简单 tar 存档(包括.git 目录)就足够了。 git filter-branch 确实会生成备份引用,但通过删除 .git 目录并从备份中恢复它通常更容易从不太正确的过滤中恢复。

注意:以下示例使用较低级别的命令git update-index --add 而不是git add。您可以使用 git add,但首先需要将文件从某个外部位置复制到预期路径(--index-filter 在一个空的临时 GIT_WORK_TREE 中运行它的命令)。

如果您希望将新文件添加到每个现有提交中,那么您可以这样做:

new_file=$(git hash-object -w path/to/file)
git filter-branch \
  --index-filter \
    'git update-index --add --cacheinfo 100644 '"$new_file"' path/to/file' \
  --tag-name-filter cat \
  -- --all
git reset --hard

我真的看不出有任何理由用--env-filter 'GIT_AUTHOR_DATE=…' 更改现有提交的日期。如果您确实使用了它,那么您将使其成为有条件的,以便它会重写每次提交的日期。

如果您希望新文件仅出现在某个现有提交(“A”)之后的提交中,那么您可以这样做:

file_path=path/to/file
before_commit=$(git rev-parse --verify A)
file_blob=$(git hash-object -w "$file_path")
git filter-branch \
  --index-filter '

    if x=$(git rev-list -1 "$GIT_COMMIT" --not '"$before_commit"') &&
       test -n "$x"; then
         git update-index --add --cacheinfo 100644 '"$file_blob $file_path"'
    fi

  ' \
  --tag-name-filter cat \
  -- --all
git reset --hard

如果您希望通过插入历史中间的新提交来添加文件,那么您需要在使用 git filter-branch 之前生成新提交并将--parent-filter 添加到 git filter-branch:

file_path=path/to/file
before_commit=$(git rev-parse --verify A)

git checkout master
git checkout "$before_commit"
git add "$file_path"
git commit --date='whenever'
new_commit=$(git rev-parse --verify HEAD)
file_blob=$(git rev-parse --verify HEAD:"$file_path")
git checkout -

git filter-branch \
  --parent-filter "sed -e s/$before_commit/$new_commit/g" \
  --index-filter '

    if x=$(git rev-list -1 "$GIT_COMMIT" --not '"$new_commit"') &&
       test -n "$x"; then
         git update-index --add --cacheinfo 100644 '"$file_blob $file_path"'
    fi

  ' \
  --tag-name-filter cat \
  -- --all
git reset --hard

您还可以安排文件首先添加到新的根提交中:通过 git rebase 部分中的“孤儿”方法创建新的根提交(在 new_commit 中捕获它),使用无条件的 --index-filter--parent-filter,如 "sed -e \"s/^$/-p $new_commit/\""

【讨论】:

  • 在第一个用例示例中,“将文件插入多引用历史记录”:有没有办法将 --index-filter 应用于 git rev-list 返回的提交?目前我看到索引过滤器应用于 rev-list 的子集。欣赏任何见解。
  • @Hedgehog:所有“多引用”示例都使用-- --all 来处理从任何引用可访问的所有提交。最后一个示例显示了如何仅更改某些提交(只需测试 GIT_COMMIT 即可)。要仅更改特定的提交列表,您可以在过滤之前保存列表(例如git rev-list … &gt;/tmp/commits_to_rewrite),然后在过滤器内测试成员资格(例如if grep -qF "$GIT_COMMIT" /tmp/commits_to_rewrite; then git update-index …)。你到底想完成什么?如果在 cmets 中解释太多,您可能想开始一个新问题。
  • 就我而言,我有一个小项目,我打算将其置于源代码控制之下。 (我没有这样做,因为这是一个单独的项目,我还没有决定使用哪个系统)。我的“源代码控制”只是将我的整个项目树复制到一个新目录中,然后重命名以前的版本目录。我是 Git 新手(在之前使用过 SCCS、RCS 和类似于 CVS 的丑陋专有 RCS 衍生产品之后),所以不要担心跟我说话。我的印象是答案涉及“将文件插入单个参考历史记录”部分的变体。对吗?
  • @Steve:如果您的历史来自单个开发线(即,如果所有快照都被视为单个线性的连续点,这将是有意义的)分支)。如果您的历史记录中有(或曾经有)多个分支,则适用“多参考”方案。除非您的历史很复杂(针对不同客户端的“分叉”版本,维护“错误修复”分支,而“开发”中又发生了新工作等),否则您可能正在查看“单引用”情况。 不过,你的情况好像和提问的不一样……
  • @Steve:既然你有一系列历史目录快照——而且你还没有 Git 存储库——那么你可能只使用 Git 的 contrib/ 中的 import-directories.perl(或 @987654357 @ 或 import-zips.py...)从您的快照创建一个新的 Git 存储库(即使使用“旧”时间戳)。我的答案中的rebase/filter-branch 技术仅在您要插入现有存储库历史记录中“遗漏”的文件时才需要。
【解决方案2】:

我知道这个问题已经很老了,但这确实对我有用:

git commit --date="10 day ago" -m "Your commit message" 

【讨论】:

  • 这非常有效。我很惊讶--date 还支持人类可读的相对日期格式。
  • 警告:git --date 只会修改 $GIT_AUTHOR_DATE ...所以根据具体情况,您会看到提交的当前日期 ($GIT_COMMITTER_DATE)
  • 关注@GuidoU.Draheim 所说的内容。您可以使用git show &lt;commit-hash&gt; --format=fuller 检查提交的完整详细信息。在那里,您会看到 AuthorDate 是您指定的日期,但 CommitDate 是提交的实际日期。
  • 那么有没有办法改变 CommitDate 或作出完全过去日期的提交? @d4nyll
  • 如果它是最后一次提交,或一系列提交,则添加git rebase --committer-date-is-author-date &lt;ref&gt; 其中&lt;ref&gt; 是您不想更改的最后一次提交/分支/标签/...。它按照它在锡上说的做。警告购买者:如果它不是您分支上的最后一次提交,则显然不起作用。
【解决方案3】:

您可以像往常一样创建提交,但在提交时,请将环境变量 GIT_AUTHOR_DATEGIT_COMMITTER_DATE 设置为适当的日期时间。

当然,这将使提交位于分支的顶端(即,在当前 HEAD 提交之前)。如果你想在 repo 中把它推得更远,你必须有点花哨。假设您有这段历史:

o--o--o--o--o

并且您希望您的新提交(标记为“X”)出现

o--X--o--o--o--o

最简单的方法是从第一个提交分支,添加您的新提交,然后在新提交的基础上重新设置所有其他提交。像这样:

$ git checkout -b new_commit $desired_parent_of_new_commit
$ git add new_file
$ GIT_AUTHOR_DATE='your date' GIT_COMMITTER_DATE='your date' git commit -m 'new (old) files'
$ git checkout master
$ git rebase new_commit
$ git branch -d new_commit

【讨论】:

  • 我总是能找到这个好的答案,但后来不得不匆匆忙忙找到日期格式,所以这是下次'Fri Jul 26 19:32:10 2013 -0400'
  • GIT_AUTHOR_DATE='Fri Jul 26 19:32:10 2013 -0400' GIT_COMMITTER_DATE='Fri Jul 26 19:32:10 2013 -0400' git commit
  • 还有更多,例如相当助记符2013-07-26T19:32:10:kernel.org/pub/software/scm/git/docs/…
  • 顺便说一下,'-0400' 部分指定时区偏移量。请注意正确选择它......因为如果没有,您的时间会根据它而改变。例如,对于住在芝加哥的我自己,我不得不选择“-0600”(北美 CST)。你可以在这里找到代码:timeanddate.com/time/zones
  • 由于日期需要重复,我发现创建另一个变量更容易:THE_TIME='2019-03-30T8:20:00 -0500' GIT_AUTHOR_DATE=$THE_TIME GIT_COMMITTER_DATE=$THE_TIME git commit -m 'commit message here'
【解决方案4】:

就我而言,随着时间的推移,我已经将 myfile 的一堆版本保存为 myfile_bak、myfile_old、myfile_2010、backups/myfile 等。我想使用它们的修改日期将 myfile 的历史记录放入 git。所以将最旧的重命名为 myfile,git add myfile,然后git commit --date=(modification date from ls -l) myfile,将下一个最旧的重命名为 myfile,另一个 git commit 使用--date,重复...

为了稍微自动化,您可以使用 shell-foo 来获取文件的修改时间。我从ls -lcut开始,但是stat(1)更直接

git commit --date="`stat -c %y myfile`" myfile

【讨论】:

  • 不错。在我的 git days 之前,我肯定有文件,我想使用文件修改时间。但是,这不只是设置提交日期(不是作者日期吗?)
  • 来自 git-scm.com/docs/git-commit:--date“覆盖提交中使用的作者日期。” git log 的日期似乎是 AuthorDate,git log --pretty=fuller 显示 AuthorDate 和 CommitDate。
  • git commit 选项--date 只会修改GIT_AUTHOR_DATE,而不是GIT_COMMITTER_DATE。正如Pro Git Book 解释的那样:“作者是最初编写作品的人,而提交者是最后应用作品的人。” 在日期的上下文中,GIT_AUTHOR_DATE 是文件更改的日期,而 GIT_COMMITTER_DATE 是提交的日期。需要注意的是,默认情况下,git log 将作者日期显示为“日期”,但在给定 --since 选项时使用提交日期进行过滤。
  • 在 OS X 10.11.1 中没有 -c 选项用于 stat
  • 在 macOS(和其他 BSD 变体)中 stat -c %y 的等价物是 stat -f %m
【解决方案5】:

要进行看起来像是在过去完成的提交,您必须同时设置 GIT_AUTHOR_DATEGIT_COMMITTER_DATE

GIT_AUTHOR_DATE=$(date -d'...') GIT_COMMITTER_DATE="$GIT_AUTHOR_DATE" git commit -m '...'

其中date -d'...' 可以是精确的日期,如2019-01-01 12:00:00 或相对的日期,如5 months ago 24 days ago

要在 git log 中查看两个日期,请使用:

git log --pretty=fuller

这也适用于合并提交:

GIT_AUTHOR_DATE=$(date -d'...') GIT_COMMITTER_DATE="$GIT_AUTHOR_DATE" git merge <branchname> --no-ff

【讨论】:

    【解决方案6】:

    以下是我在过去几天内用于提交更改的 fooN=1

    git add foo
    git commit -m "Update foo"
    git commit --amend --date="$(date -v-1d)"
    

    如果您想提交更早的日期,比如 3 天前,只需更改 date 参数:date -v-3d

    例如,当您忘记提交昨天的某事时,这非常有用。

    更新--date 也接受--date "3 days ago" 甚至--date "yesterday" 之类的表达式。所以我们可以将其简化为一行命令:

    git add foo ; git commit --date "yesterday" -m "Update"
    

    【讨论】:

    • 警告:git --date 只会修改 $GIT_AUTHOR_DATE ...所以根据具体情况,您会看到提交的当前日期 ($GIT_COMMITTER_DATE)
    • 将两种方法混合在一起几乎完美契合:git commit --amend --date="$(stat -c %y fileToCopyMTimeFrom)"
    • 太棒了!具有人类可读日期的相同命令git commit --amend --date="$(date -R -d '2020-06-15 16:31')"
    【解决方案7】:

    这是一个老问题,但我最近偶然发现了它。

    git commit --date='2021-01-01 12:12:00' -m "message" 工作正常并在GitHub 上进行了验证。

    【讨论】:

    • 它对我来说很好,只需添加更多说明, git commit --date='year-month-day hour:minutes:seconds' -m "message"
    • 我使用自己的时区还是 UTC?
    【解决方案8】:

    就我而言,在使用 --date 选项时,我的 git 进程崩溃了。可能是我做了什么可怕的事情。结果出现了一些 index.lock 文件。所以我从 .git 文件夹中手动删除了 .lock 文件并执行,所有修改过的文件都在过去的日期提交,这次它工作了。感谢这里的所有答案。

    git commit --date="`date --date='2 day ago'`" -am "update"
    

    【讨论】:

    • comment above on git commit --date。此外,应该修改您的示例提交消息,以阻止像这些 @JstRoRR 这样的糟糕的单行消息
    • 警告:git --date 只会修改 $GIT_AUTHOR_DATE ...因此根据具体情况,您会看到提交的当前日期 ($GIT_COMMITTER_DATE)
    【解决方案9】:

    或者只使用fake-git-history 为特定数据范围生成它。

    【讨论】:

      【解决方案10】:

      您可以随时更改计算机上的日期,进行提交,然后将日期更改回并推送。

      【讨论】:

      • 我认为这不是正确的做法,它可能会产生一些未知的问题
      • 这是个坏主意。
      【解决方案11】:

      预步骤。

      • 将所有数据从远程拉取到本地存储库。

      • 我们正在使用 --amend 和 --date 开关。

      具体命令如下:

      $ git commit --amend --date="YYYY-MM-DD HH:MM:SS"

      【讨论】:

        【解决方案12】:
        1. git --date 仅更改 GIT_AUTHOR_DATE 但许多 git 应用程序,例如 GitHub 显示 GIT_COMMITTER_DATE。确保也更改GIT_COMMITTER_DATE
        2. git 不知道默认 UNIX 日期输出中的时区。确保将日期时间格式设置为兼容的格式,例如 ISO8601。

        OS X 中的完整示例(将 GIT_COMMITTER_DATEGIT_AUTHOR_DATE 都更改为 4 小时前):

        x=$(date -v -4H +%Y-%m-%dT%H:%M:%S%z); export GIT_COMMITTER_DATE=$x; git commit --amend --date $x
        

        【讨论】:

          猜你喜欢
          • 2023-03-08
          • 2021-04-30
          • 2012-10-27
          • 2014-06-29
          • 1970-01-01
          • 2012-01-13
          • 2021-11-12
          相关资源
          最近更新 更多