1

I want to delete all files that are neither "foo" nor "bar" in git repository history. Given that rm -rf !(foo|bar) works for deleting files in the current directory that are neither "foo" nor "bar". I came up with the following idea:

$ git filter-branch --tree-filter 'rm -rf !(foo|bar)' --prune-empty HEAD

However, this produced the following error which I really don't understand why it doesn't work.

Rewrite 75c1ec1ef083338ca2e88db9cc6107c1630d91e9 (1/871)~/libexec/git-core/git-filter-branch: 1: eval: Syntax error: "(" unexpected

2
  • Why not list all files that are neither foo nor bar? I don't think you want to run rm in this context. Commented Jul 13, 2014 at 21:58
  • @ Makoto: Don't understand what you meant. What I wanted exactly was to delete all the files in the repository (delete them in all the history), leaving only files "foo" and "bar". Commented Jul 13, 2014 at 22:19

3 Answers 3

2

see https://stackoverflow.com/a/19957874/1870481

Either use

--tree-filter 'bash preserve-only.sh foo bar' 

with preserve-only.sh being

IFS=':' GLOBIGNORE="$*" rm -rf * 

or using

--index-filter \ 'git rm --cached -r -q -- . ; git reset -q $GIT_COMMIT -- foo bar' \ --prune-empty 

The last one is especially nice. Juste delete everything and restore what you want. :)

Sign up to request clarification or add additional context in comments.

2 Comments

That index-filter will be a lot faster, too.
@michas: --index-filter 'git rm --cached -r -q -- . ; git reset -q $GIT_COMMIT -- foo bar' worked and is nice. But needs to add --prune-empty to prune the empty commits that are no longer relevant. It would be even better if you could provide an explanation though. Anyways, great answer and thanks a lot.
2

I got this working using the following code:

git filter-branch -f --tree-filter "bash -O extglob -c 'rm -rf ./path/to/!(FILENAME_TO_KEEP)'" HEAD 

Specifically, passing -O extglob to bash allows you to use the exclamation mark syntax.

Comments

0

The immediate problem is that !(foo|bar) is zsh-specific syntax [Edit: or bash with extglob turned on], not supported in sh. Each filter is run with an eval by the shell script that implements git-filter-branch (plain sh, usually).

You could, of course, invoke zsh (or a bash command that turns on extglob) directly to run the command. Or you could remove the unwanted files by their (bad) name(s), rather than by them not having good names.

(To remove files with filter-branch it's a lot faster to use the --index-filter, which does not require checking out each commit and re-committing. But that's an internal optimization, and with only 871 commits, not as important.)

6 Comments

Yes it is support by bash extglob. Using shopt to turn on this extended pattern matching. In my Ubuntu 14.x, it is on by default.
Ah, I see, it is in bash with extglob set. However, the script is run with either plain sh, or with bash without extglob set. Either way it's not available by default.
Do you mean that even though echo !(foo|bar) produces expected results, using it in git-filter-branch would still not work? If so, why?
Yes: because you're doing it from your command line shell process, while git's filter-branch script uses a different process instance (and even a different shell, /bin/sh, on Linux systems).
So it doesn't call bash right? Anyway to make it work still? And yes, index-filter was a lot faster.
|

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.