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