306

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

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

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

12 回答 12

237

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

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

指定“现在”以外的日期</h1>

每个提交都有两个日期:作者日期和提交者日期。您可以通过环境变量 GIT_AUTHOR_DATE 和 GIT_COMMITTER_DATE 为写入新提交的任何命令提供值来覆盖每个。请参阅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”的一部分,在该选项之后命名选项;请参阅git-filter-branch(1) 中的“选项”和底层的“管道”命令git-commit -树(1)

将文件插入单个参考历史记录

如果您的存储库非常简单(即您只有一个分支,没有标签),那么您可能可以使用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),但还有其他方法可以在旧版本的 Git 上做同样的事情。

将文件插入多参考历史记录

如果您的存储库更复杂(即它有多个引用(分支、标签等)),那么您可能需要使用git filter-branch在使用git filter-branch之前,您应该制作整个存储库的备份副本。整个工作树(包括 .git 目录)的简单tar存档就足够了。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-filtergit 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),使用 unconditional--index-filter等。--parent-filter"sed -e \"s/^$/-p $new_commit/\""

于 2010-10-10T03:03:08.090 回答
226

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

git commit --date="10 day ago" -m "Your commit message" 
于 2017-04-09T19:31:48.210 回答
165

您可以像往常一样创建提交,但在提交时,请设置环境变量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
于 2010-10-09T15:16:12.513 回答
42

就我而言,随着时间的推移,我将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,使用 --date 进行另一个 git 提交,重复...

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

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

于 2012-03-19T22:33:54.650 回答
40

要进行看起来像是在过去完成的提交,您必须同时设置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
于 2019-06-25T17:29:18.747 回答
34

以下是我用来提交对过去几天的更改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"
于 2017-04-20T17:48:14.420 回答
33

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

git commit --date='year-month-day hour:minutes:seconds' -m "message"

所以它看起来像这样: 正常工作并在和git commit --date='2021-01-01 12:12:00' -m "message"上验证它。GitHubGitLab

于 2021-01-04T18:43:30.547 回答
21

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

git commit --date="`date --date='2 day ago'`" -am "update"
于 2013-10-30T11:03:45.870 回答
7

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

于 2019-08-17T05:57:46.200 回答
5
  1. git --date仅更改GIT_AUTHOR_DATE但许多 git 应用程序,例如 GitHub 显示GIT_COMMITTER_DATE. 确保也改变GIT_COMMITTER_DATE
  2. git 不知道默认 UNIX 日期输出中的时区。确保将日期时间格式格式化为兼容的格式,例如 ISO8601。

OS X 中的完整示例(更改GIT_COMMITTER_DATEGIT_AUTHOR_DATE4 小时前):

x=$(date -v -4H +%Y-%m-%dT%H:%M:%S%z); export GIT_COMMITTER_DATE=$x; git commit --amend --date $x
于 2021-08-11T03:37:22.557 回答
3

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

于 2018-03-27T10:31:52.837 回答
3

预步骤。

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

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

确切的命令如下:

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

于 2021-06-16T09:39:09.927 回答