Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

gitignore all files in folders but keep folder structure

Tags:

git

gitignore

I have the following folder structure:

/foo/
/foo/test.txt
/foo/.gitkeep
/foo/bar/test.txt
/foo/bar/.gitkeep
/foo/bar/baz/test.txt
/foo/bar/baz/.gitkeep

Now I want to exclude all files in the folder "foo" and all files in its subfolders (and subsubfolders), except all the .gitkeeps (so that the folder structure is kept). This should also work for more than 3 levels of subfolders.

The following gitignore-rules work:

/foo/**
!/foo/**/
!/foo/**/.gitkeep

Can somebody explain, why this works? Is there a cleaner way of doing this?

like image 982
David Vielhuber Avatar asked Sep 19 '17 13:09

David Vielhuber


People also ask

Does Gitignore work in subfolders?

gitignore file is usually placed in the repository's root directory. However, you can create multiple . gitignore files in different subdirectories in your repository.

Does Gitignore need to be in every folder?

A . gitignore file is a plain text file where each line contains a pattern for files/directories to ignore. Generally, this is placed in the root folder of the repository, and that's what I recommend. However, you can put it in any folder in the repository and you can also have multiple .

How do I ignore all files in Gitignore?

If you want to ignore a file that you've committed in the past, you'll need to delete the file from your repository and then add a . gitignore rule for it. Using the --cached option with git rm means that the file will be deleted from your repository, but will remain in your working directory as an ignored file.

Does Gitignore ignore subdirectories?

gitignore file does not exclude the directories unless I fully qualify the ignore pattern as Solution/Project/bin/Debug - which I don't want to do as I will need to include this full pattern for each project in my solution, as well as add it for any new projects added. Any suggestions?


2 Answers

The rule is simple:

It is not possible to re-include a file if a parent directory of that file is excluded.

That is why you need to

  • ignore files and folders recursively:

    /foo/** 

(if you only ignore /foo/, that is the folder, then no amount of '!' exclusion rule will work, since the foo/ folder itself is ignored: Git will stop there)

  • Then exclude folders from the ignore rules:

    !/foo/**/ 
  • Before whitelisting files like the .gitkeep

    !/foo/**/.gitkeep 

That works because the .gitkeep parent folders are excluded from the ignore rules.

As opposed to Bernardo original proposal (before his edit):

/foo/**  !**/.gitkeep 

If does not exclude folders from the /** ignore rule, so the exclusion rule for files is inoperative.

You can check that with:

git check-ignore -v -- /path/to/.gitkeep 

As mentioned in scm .gitignore:

  • using '*' is the same as '**'
  • using !.gitkeep (without any / anchor) would exclude that file recursively.

In both cases, 'recursively' is the key term which explains why exclusion rules can apply: if you ignore a folder (like . or /foo/), you won't be able to exclude anything inside that folder.
But if you ignore elements (files and folder) recursively (* or **), and exclude folders from gitignore rules (again through recursive rule !*), then you can exclude (white-list) files.

like image 158
VonC Avatar answered Sep 20 '22 07:09

VonC


I've done it. It seems that what is needed is a .gitignore file inside the desired folder.

Look at this repository, which does exactly what you want.

I've tried to add any filename inside foo/, foo/bar/ and foo/bar/baz/ but it only accepts .gitkeep.

The trick is to create a .gitignore with this content, inside the folder:

*
!*/
!.gitignore
!.gitkeep

[Edit: Added based on comments]

And you should be able to shorten this to:

*
!*/
!.git*

Though it may be less clear in the future what was intended.

While this solution doesn't shorten the number of lines required from your original .gitignore, this does have several other advantages - the main one being that this will be a .gitignore file inside the folder, which means it can be a clean file that has just these lines, and is specific to just this folder.

That means this .gitignore file can be easily moved into any specific folder that requires this filter. Also, the entire folder can be easily moved around within the repo, or even moved to a different repo, without needing to modify the .gitignore. It is much cleaner and more maintainable, and not buried in the typically large .gitignore file in the repo's root folder.

like image 31
Bernardo Duarte Avatar answered Sep 21 '22 07:09

Bernardo Duarte