Rakim Rakim - 2 months ago 5
Git Question

git filter-branch using two queries

I am trying to following this link to remove multiple files at once. Using individually the command:

git filter-branch -f --index-filter 'git rm --cached --ignore-unmatch "UserFiles/*"' --prune-empty --tag-name-filter cat -- --all

works fine. But when I try to combine 2 queries at once i get back the error
WARNING: Ref 'refs/heads/master' is unchanged

The command I tried was:

git filter-branch -f --index-filter 'git rm --cached --ignore-unmatch "testDir/*"' && 'git rm --cached --ignore-unmatch "UserFiles/*"' --prune-empty --tag-name-filter cat -- --all

how can I fix that?


The --index-filter argument is given to the shell via eval. What this means in practice is that if you need to run two different index filters, you would spell them out as:

git filter-branch <args> --index-filter 'cmd1; cmd2' <more-args>

Note that this is a semicolon, not a comma, and it goes inside the quotes that make the expression a single argument to --index-filter.

In your particular case, however, your index-filter can probably1 be done with one command. The one command is:

git rm -r --cached --ignore-unmatch UserFiles testDir

When using -r (recursive remove), there is no need for the /*, hence no need for inner quotes. Of course, as always, the command to be used for the index filter has to be passed through as a single argument, so if it contains spaces—and as we can see, it does—this does need the outer quotes. Hence:

git filter-branch -f \
    --index-filter 'git rm -r --cached --ignore-unmatch testDir UserFiles' \
    --prune-empty --tag-name-filter cat -- --all

(I broke this into three lines, the first two ending with backslash-newline, for posting purposes. Since backslash-newline gets the shell to continue reading, they can be entered this way, or you can turn it all back into one big line without the backslashes.)

1If your intent is to remove only files within the UserFiles and testDir directories, and not to remove any subdirectories and their files, this "probably" is wrong. That is:

git rm -r --cached testDir

will remove testDir/a, testDir/b/c, testdir/b/d, testdir/b, testdir/c, and testdir itself, while:

git rm --cached "testDir/*"

will remove only testDir/a and testDir/c, leaving testDir/b/ intact. If you need to do that, you cannot use the -r short-cut.