你得到的建议是有缺陷的。在--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/\""。