2

I'm trying to use git rm as part of the filter-branch. I want to delete directories */*/dir1 but nothing is happening. The command I'm using is:

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

Command

find . -wholename "*/*/dir1"

shows a lot of matches.

How I could remove those directories?

Update: I'm trying to remove these folders from the history.

Solution:

With combination of the answers from @j6t and @Sam Gleske here is my solution:

git filter-branch --tag-name-filter cat \
   --index-filter 'git ls-files | grep -E "^([^/]+/){2,3}dir1" |
                   xargs -I {} --no-run-if-empty git rm -r --cached "{}"' \
   --prune-empty -f -- --all
Michael
  • 1,014
  • 2
  • 15
  • 26
  • Check if http://stackoverflow.com/questions/41593424/git-how-do-you-checkout-all-deleted-files/41598200#41598200 solves your problem. It's an answer I previously posted for a similar question. – Gayan Jan 14 '17 at 10:04
  • This is absolutely not related to the question. – Michael Jan 14 '17 at 22:52

4 Answers4

1

Use this (exactly the same order):

git filter-branch --force --index-filter 'git rm --cached -r --ignore-unmatch */*/dir1' --prune-empty --tag-name-filter cat -- --all
rm -rf .git/refs/original/
git reflog expire --expire=now --all
git gc --prune=now
git gc --aggressive --prune=now
Igor
  • 12,165
  • 4
  • 57
  • 73
  • This is not working. No folders were removed. It looks like "*/*/dir1" is not a correct argument for "git rm". – Michael Jan 14 '17 at 05:48
0

According to git rm documentation, you can pass it one or more files or folders. Since that is the case you can take advantage of shell globbing. In bash, you must glob outside of quotes.

git rm -r */*/dir1

However, it is possible to hit the ARG_MAX limit in Linux (i.e. getconf ARG_MAX) and get an error. You can resolve that by making use of xargs with your original find command.

find . -wholename "*/*/dir1" -print0 | xargs -0 git rm -r

I also recommend adding -type d to your find command because it's possible for other file types to have the same name as dir1.

That will only delete those directories as a new commit.


Purge all history with xargs

Since your intention is to purge all history and globbing isn't working as intended with git rm; you still have another option. Making use of your original find command with xargs to remove the entire history using the paths from find. Here's how:

find . -wholename "*/*/dir1" -print0 | xargs -0 -I '{}' -n10 -- git filter-branch --tag-name-filter cat --index-filter "git rm -r --cached --ignore-unmatch {}" --prune-empty -f -- --all

The trick is paying attention to the -I '{}' option of xargs and using {} in the command xargs will be executing.

See also

Community
  • 1
  • 1
Sam Gleske
  • 950
  • 7
  • 19
  • I don't think "find" will work with "index-filter". An I wrong? – Michael Jan 14 '17 at 05:50
  • Your question still isn't clear to me. You don't need index-filter to delete directories. Are you trying to remove the full history of these directories ever existing? i.e. modifying the full history of your git repo? – Sam Gleske Jan 14 '17 at 18:57
  • Yes, I'm trying to remove all "*/*/dir1" folders from the history. What else filter-branch with rm could do? – Michael Jan 14 '17 at 22:52
  • Why? I typically create a new repository at that point and bump the version major for the next repository as a clear separation. i.e. older versions are in the old repo, etc. According to `git rm` docs, it takes a glob so the xargs find example may not be necessary. Check out bfg repo cleaner. – Sam Gleske Jan 15 '17 at 04:07
  • Unfortunately I must proceed with the cleaning repository instead of creating a new one. Also, BFG is not working with directories like I need - it can remove ALL "dir1" but not "*/*/dir1". This is huge difference. – Michael Jan 15 '17 at 10:16
  • It's not working. Here is the answer from BFG developer: http://stackoverflow.com/questions/21142986/remove-filenames-from-specific-path – Michael Jan 15 '17 at 22:12
  • Hi Michael, I just edited my answer to use `find`, `xargs`, and `git filter-branch` with `git rm`. Try that out on a copy of your repository and let me know how it goes. – Sam Gleske Jan 16 '17 at 04:27
  • As far as I understand this implementation will execute branch-filter multiple time. For my repo it takes about 9 hours for a single round. But I think it should work for small repos. – Michael Jan 17 '17 at 02:52
  • It's a solution given so far the globbing examples you cite don't work. You can adjust the amount of times it executes based on the arguments if you read the man pages. – Sam Gleske Jan 17 '17 at 05:37
0

The following should do what you need:

git filter-branch --tag-name-filter cat \
   --index-filter 'git ls-files -z -- "*/*/dir1" |
                   xargs -0 --no-run-if-empty git rm -r --cached' \
   --prune-empty -f -- --all

Note that the command does descend into directories, i.e., it will match a/b/c/d/dir1 even though ** was not used in the argument of git ls-files.

j6t
  • 9,150
  • 1
  • 15
  • 35
  • Thanks. Can I limit it just to a/b/dir1? I don't want to go deeper that two levels. – Michael Jan 16 '17 at 11:40
  • I'm afraid but git ls-files -- "*/*/dir1" shows nothing. It's not working. – Michael Jan 16 '17 at 12:15
  • @Michael _I don't want to go deeper than two levels_ I don't have an idea how to do that except to make the pattern more specific. _git ls-files -- "*/*/dir1" shows nothing_ It does here for me. What does `git version` say? Make sure your pattern begins with `*/`. – j6t Jan 16 '17 at 12:43
0

With combination of the answers from @j6t and @Sam Gleske here is my solution:

git filter-branch --tag-name-filter cat \
   --index-filter 'git ls-files | grep -E "^([^/]+/){2,3}dir1" |
                   xargs -I {} --no-run-if-empty git rm -r --cached "{}"' \
   --prune-empty -f -- --all
Michael
  • 1,014
  • 2
  • 15
  • 26