Remove a directory from all previous commits
Instead of using --index-filter
, try with --tree-filter
:
--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!).
The commandline:
git filter-branch --tree-filter 'rm -rf path/to/ABC' \
--prune-empty --tag-name-filter cat -- --all
Say you begin with a history of
$ git lola --name-status * e709131 (HEAD, master) bar | A bar * 61493ac ABC/file - 3.ext | A ABC/file - 3.ext * 34cce9e ABC/file - 2.ext | A ABC/file - 2.ext * 115e6d5 ABC/file - 1.ext | A ABC/file - 1.ext * 5ea5b42 foo A foo
Note: git lola is a non-standard but highly useful alias.
git rm
supports an option for removing subtrees.
-r
Allow recursive removal when a leading directory name is given.
After running
$ git filter-branch --index-filter 'git rm --cached -r --ignore-unmatch ABC' \ --prune-empty --tag-name-filter cat -- --all
you will see output that resembles
Rewrite 115e6d5cd06565ca08f1e5c98c4b91246cf59fa1 (2/5)rm 'ABC/file - 1.ext' Rewrite 34cce9e90f832460137e620ebacc8a73a99e64ce (3/5)rm 'ABC/file - 1.ext' rm 'ABC/file - 2.ext' Rewrite 61493ac3211808f34f616dbc33d51d193b3f45a3 (4/5)rm 'ABC/file - 1.ext' rm 'ABC/file - 2.ext' rm 'ABC/file - 3.ext' Rewrite e709131f1fe6103adf37616c9fa500994aeb30d0 (5/5)rm 'ABC/file - 1.ext' rm 'ABC/file - 2.ext' rm 'ABC/file - 3.ext' Ref 'refs/heads/master' was rewritten
If you are happy with the result, delete the old master with
$ git update-ref -d refs/original/refs/heads/master
and now you have a history of
$ git lola --name-status * 19680d4 (HEAD, master) bar | A bar * 5ea5b42 foo A foo
To answer your second question, say you wanted to delete ABC/file - 2.ext
only. Remember that you will need two layers of quoting: one layer for the whole command and another to escape spaces in an argument to that command, the name of the file to remove.
One way to do that is
$ git filter-branch --index-filter \ 'git rm --cached --ignore-unmatch "ABC/file - 2.ext"' --prune-empty \ --tag-name-filter cat -- --all
Note the double quotes inside the single quotes.
If you had run this command instead, your history would have become
$ git lola * a8d1b0d (HEAD, master) bar * cff0c4e ABC/file - 3.ext * 115e6d5 ABC/file - 1.ext * 5ea5b42 foo