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

23

我想将一个文件添加到 Git 存储库中,就好像它一直在那里一样。我只找到了如何从整个历史记录中删除文件的解释,而不是如何添加文件。

我尝试使用 git filter-branch --tree-filter 'git add LICENSE.txt' 但是我收到了文件找不到的错误。

3个回答

24

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-commands文件并退出编辑器:

".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 出现在所有提交中。


如果您的历史记录比较复杂(分支众多),并且想要使用 git filter-branch 来更新所有内容,您需要使用的 --tree-filter 不是:

'git add LICENSE.txt'

但是相反:

'cp /somewhere/outside/the/repo/LICENSE.txt LICENSE.txt'

每次将新文件复制到树中。 (更快的方法是使用--index-filter,但这更加复杂。)


13

--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的检出)的三次运行中,每种方法的中位数时间。
              large medium  small
index-filter   1064     38     10
tree-filter    4319     81     15
rebase            -    116     28

还要注意,rebasefilter-branch变种在功能上并不等同,因为它会更新提交者日期。


我尝试了你的解决方案,它在我的情况下表现良好。但是似乎新文件只存在于主分支中。因此,我从其他分支中挑选了不同的提交来构建其余的分支,这样做起作用了。我不认为这是解决问题的推荐方式。那么正确的方法是什么呢?非常感谢! - walterlv
2
@walterlv 要重写所有分支,只需在命令后添加 -- --all - filipos
我尝试了你的参数“-- --all”,它对我有效。非常感谢!顺便说一下,git文档中说“--”分隔路径和修订版本。 - walterlv

0
   --tree-filter <command>
       This is the filter for rewriting the tree and its contents. The
       argument is evaluated in shell with the working directory set to
       the root of the checked out tree. The new tree is then used as-is
       (new files are auto-added, disappeared files are auto-removed -
       neither .gitignore files nor any other ignore rules HAVE ANY
       EFFECT!).
请注意,您需要在每个版本的新检出中开始。因此,您想要的命令类似于--tree-filter 'cp $HOME/LICENSE.txt .'。我更喜欢torek的答案(使用rebase),除非您有太多修订,这是不切实际的。

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接