GIT:如何将文件添加到第一次提交中(并在过程中重写历史)?

GIT:如何将文件添加到第一次提交中(并在过程中重写历史)?,git,Git,我想向git存储库添加一个文件,就好像它从一开始就存在一样。我只找到了如何从整个历史记录中删除文件的解释,而没有找到如何添加文件的解释 我尝试了git filter branch--tree filter'git add LICENSE.txt',但出现了找不到文件的错误。git filter branch可以做到这一点,但重量可能比需要的要重得多 你的历史有多大?如果它又小又短,最简单的方法就是现在添加新文件,然后使用git-rebase-i--root将新提交移动到第二个位置,并将其压缩到根

我想向git存储库添加一个文件,就好像它从一开始就存在一样。我只找到了如何从整个历史记录中删除文件的解释,而没有找到如何添加文件的解释


我尝试了git filter branch--tree filter'git add LICENSE.txt',但出现了找不到文件的错误。

git filter branch
可以做到这一点,但重量可能比需要的要重得多

你的历史有多大?如果它又小又短,最简单的方法就是现在添加新文件,然后使用
git-rebase-i--root
将新提交移动到第二个位置,并将其压缩到根提交中

例如,假设您有:

$ git log --oneline --graph --decorate --all
* e8719c9 (HEAD, master) umlaut
* b615ade finish
* e743479 initial
(您的SHA-1值当然会有所不同),并且您希望将
LICENSE.txt
(已在工作目录中)添加到树中,作为根提交的一部分。您现在就可以这样做:

$ git add LICENSE.txt && git commit -m 'add LICENSE, for fixup into root'
[master 924ccd9] add LICENSE, for fixup into root
 1 file changed, 1 insertion(+)
 create mode 100644 LICENSE.txt
然后运行
git-rebase-i--root
。抓取最后一行(
pick…add LICENSE,
)并将其移动到第二行,将
pick
更改为
fixup
,然后写出rebase命令文件并退出编辑器:

".git/rebase-merge/git-rebase-todo" 22L, 705C written
[detached HEAD 7273593] initial
 2 files changed, 4 insertions(+)
 create mode 100644 LICENSE.txt
 create mode 100644 x.txt
Successfully rebased and updated refs/heads/master.
(新的、完全重写的)历史现在看起来更像这样:

git log --oneline --graph --decorate --all
* bb71dde (HEAD, master) umlaut
* 7785112 finish
* 7273593 initial
所有提交中都有
LICENSE.txt


如果您确实有一个更复杂的(branchy)历史记录,并且希望使用
git filter branch
来更新它,那么您需要的
--树过滤器
不是:

'git add LICENSE.txt'
而是:

'cp /somewhere/outside/the/repo/LICENSE.txt LICENSE.txt'
每次将新文件复制到树中。(更快的方法是使用
--索引过滤器
,但这更复杂。)

--树过滤器
这是用于重写树及其内容的筛选器。这个
参数在shell中求值,工作目录设置为
签出树的根。然后按原样使用新树
(自动添加新文件,自动删除消失的文件-
gitignore文件和任何其他ignore规则都没有
效果!)。
请注意,您从每个版本的新签出开始。因此,您需要的命令类似于
--tree filter'cp$HOME/LICENSE.txt。
我更喜欢torek的答案(使用
rebase
),除非您有太多的修订,因此它是不切实际的。

--index filter
提供了一个简单而快速的解决方案:

git filter-branch --index-filter "cp /abs/path/to/LICENSE.txt . && git add LICENSE.txt" --tag-name-filter cat --prune-empty -- --all

这里是一个与其他建议方法相比的非常简单的基准

第一列(
large
)以秒为单位显示Git项目存储库副本中每个过滤器的运行时间(45885次提交,签出约30M)。
rebase
方法不适用,因为它不会自动处理合并,即使使用
-c
选项也是如此

第二列(
medium
)显示了一个具有线性历史和相当大的树(2430次提交,约80M的签出)的中型存储库副本中每个方法三次运行的中间时间

第三列(
small
)显示了一个小型存储库副本中每个方法三次运行的中间时间(554次提交,约100K次签出)


还要注意的是,
rebase
在功能上并不等同于
过滤器分支
变体,因为它更新提交者日期。

我尝试了您的解决方案,在我的情况下效果很好。但新文件似乎只存在于主分支中。因此,我从其他分支中选择了不同的提交来构建其余的分支,并且成功了。我不认为这是解决这个问题的推荐方法。那么正确的方法是什么呢?非常感谢@walterlv要重写所有分支,只需将
--all
附加到命令中。我已经尝试了您的参数
--all
,它对我有效。谢谢!顺便说一下,git文档说,
--
将路径和修订分开。
git filter-branch --index-filter "cp /abs/path/to/LICENSE.txt . && git add LICENSE.txt" --tag-name-filter cat --prune-empty -- --all
              large medium  small
index-filter   1064     38     10
tree-filter    4319     81     15
rebase            -    116     28