41

When I have .gitignore data/* and run git clean -fd, the data folder and all its content files are deleted.

What I want is to delete all unrevisioned files in a git repo while excluding all ignored files (i.e. DON'T delete gitignored files). What could I do?

talles
  • 11,195
  • 8
  • 39
  • 55
Danny Lin
  • 1,700
  • 1
  • 14
  • 31
  • By default `git clean` *shouldn't* remove ignored files. (You'd have to specify the `-x` option to get it to remove ignored paths.) – Amber Oct 18 '13 at 06:03
  • 5
    What if your `.gitignore` contains `data/` instead of `data/*`, would you observe the same issue? – VonC Oct 18 '13 at 06:09
  • @Amber I used to think so. But it seems that git clean doesn't remove ignored _files_, while it removes ignored _folders_ (and their contents). – Danny Lin Oct 18 '13 at 06:18
  • 2
    `foo/*` doesn't ignore the `foo` folder - it ignores files within the `foo` folder. That may be why `git clean` is wiping it - because it sees an "unignored folder" named `foo` and thus removes it (which happens to remove all of its contents). – Amber Oct 18 '13 at 06:24

3 Answers3

46

Git normally doesn't clean ignored files unless the -x flag is specified, but strangely it cleans out when configured as you did (folder/*).

As @VonC pointed out, you should change your .gitignore-file to ignore the directory (data/) rather than its contents (data/*).

It's a subtle difference, but it matters to git.

Bernd-L
  • 39
  • 2
  • 5
  • 11
talles
  • 11,195
  • 8
  • 39
  • 55
  • 9
    I agree with my comment ;) +1 – VonC Oct 18 '13 at 06:26
  • 4
    The slightly annoying thing is that if you ignore `folder/` instead of `folder/*` then you can't make exceptions like `!folder/foo`. There is currently an issue like this in Github's standard gitignore for Visual Studio: https://github.com/github/gitignore/commit/9aa3e1c3a7a5d9a7ecbab67f63eb81e8b4a493b7#commitcomment-6092133 – Jamie Humphries Apr 23 '14 at 08:37
  • Wow that's annoying... Seems like a bug in Git clean? – Erfan Apr 09 '15 at 02:40
  • @Erfan, I don't think so...how would you propose to have it work? If you want an exception like `!folder/foo`, doesn't that mean you would be tracking both `foo` and `folder`? Which would preserve them from `git clean -fd` anyway. Am I missing something here? – Wildcard Dec 02 '15 at 22:53
  • 1
    @Wildcard If you want an exception like !folder/foo, you would need to specify folder/* in your gitignore first, otherwise you can't add the exception in the first place. If you run git clean -fd, it will remove folder/foo because it isn't tracked by git AND it's not in the gitignore, remember, only the contents of folder are ignored, not folder itself. But folder is also not in Git because it's an empty directory.. So when you run git-clean, it wipes the folder (and the contents with it), does that make sense? I might be getting confused myself now.. – Erfan Dec 03 '15 at 13:36
  • That's only if folder/foo is not tracked in your repo. But why would you add it as an exception in .gitignore (i.e. it is an exception to the ignore rule, it should NOT be ignored) unless you wanted to track it? (And if it's tracked, it wouldn't be removed by git clean.) – Wildcard Dec 03 '15 at 16:05
  • Ah I see what you mean. Hmm.. I think in my case the situation arises because I have a generic .gitignore file that I drop in my projects, and sometimes folder/foo exists, and sometimes it doesn't. When it doesn't exist, I'll clear out `folder` with its contents. When `foo` does exist, it works as expected. If `folder/foo` doesn't exist, should it still delete `folder`? – Erfan Dec 10 '15 at 02:54
  • @Wildcard, this seems to be fixed in Git 2.7.0 :) You can now specify `/cache` `!cache/.htaccess` And it will properly retain the htaccess file – Erfan Jan 06 '16 at 01:33
  • @Erfan, good to know, but the quadruple-tuplet negative here is going to make my head asplode. ;) – Wildcard Jan 06 '16 at 01:47
  • to remove all untracked files, and keep `data/*` in gitignore you can use `git ls-files -z -o --exclude-standard | xargs -0 rm -f` – Charlestone Aug 19 '19 at 10:09
1

I've found some more details. Having /tmp/* in gitignore, git clean -fd will remove it. As it was said in other answers, this does not happens with /tmp/ in gitignore.

But once you have any checked-in any file in this directory, git clean -fd will ignore this path. This can be achieved with git add -f or adding !/tmp/.keep to gitignore and checking this file in.

prcu
  • 816
  • 1
  • 9
  • 22
0

Changing data/* to data/ is not usable for me, because after that you can't whitelist files/folders in excluded folders.

when you put this in .gitignore

data/
!data/foo.txt

the file foo.txt won't be included.

To remove all untracked files (and folders) as they are shown in git status (and keep something like data/* in gitignore) you can use

git ls-files -z -o --exclude-standard | xargs -0 rm -rf

This will list all untracked files and pass them to rm -rf function, which will delete them.

Credits to https://stackoverflow.com/a/3801554/4710968

Charlestone
  • 1,078
  • 1
  • 10
  • 22