I\'d like to add one file to git repository as if it was there from the start. I only found explanations how to remove a file from entire history, not how to add one.
<--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!).
Note that you start in a fresh checkout of each version. So the command you want is something like --tree-filter 'cp $HOME/LICENSE.txt .'
I like torek's answer better (using rebase
) unless you have so many revisions that it is impractical.
--index-filter
provides a simple and fast solution:
git filter-branch --index-filter "cp /abs/path/to/LICENSE.txt . && git add LICENSE.txt" --tag-name-filter cat --prune-empty -- --all
Here is a very simple benchmark against other proposed methods.
The first column (large
) shows the timings in seconds for one run of each filter in copies of the Git project repository (45885 commits, checkout of ~30M). The rebase
method does not apply since it does not handle merges automatically, even with the -c
option.
The second columns (medium
) shows median times for three runs of each method in copies of a medium sized repository with a linear history and fairly large trees (2430 commits, checkout of ~80M).
The third column (small
) shows median times for three runs of each method in copies of a small repository (554 commits, checkout of ~100K).
large medium small
index-filter 1064 38 10
tree-filter 4319 81 15
rebase - 116 28
Also note that the rebase
is not functionally equivalent to the filter-branch
variants, as it updates the committer dates.
git filter-branch
can do this but is probably a lot heavier weight than needed.
How big and branch-y is your history? If it's small and short, the easiest way to do this would be add the new file now, then use git rebase -i --root
to move the new commit to the 2nd position and squash it into the root commit.
For instance, let's say you have:
$ git log --oneline --graph --decorate --all
* e8719c9 (HEAD, master) umlaut
* b615ade finish
* e743479 initial
(your SHA-1 values will differ of course) and you want to add LICENSE.txt
(already in the work dir) to the tree as part of the root commit. You can just do it now:
$ 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
then run git rebase -i --root
. Grab the last line (pick ... add LICENSE, ...
) and move it to the second line, changing pick
to fixup
, and write the rebase-commands file out and exit the editor:
".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.
The (new, completely-rewritten) history now looks more like this:
git log --oneline --graph --decorate --all
* bb71dde (HEAD, master) umlaut
* 7785112 finish
* 7273593 initial
and LICENSE.txt
is in all commits.
If you do have a more complicated (branchy) history and want to use git filter-branch
to get it all updated, the --tree-filter
you need is not:
'git add LICENSE.txt'
but rather:
'cp /somewhere/outside/the/repo/LICENSE.txt LICENSE.txt'
to copy the new file into the tree each time. (A faster method would be to use the --index-filter
but this is more complex.)