Home > Mobile >  How to tell .gitignore to ignore all subdirectories regardless of their name?
How to tell .gitignore to ignore all subdirectories regardless of their name?

Time:02-04

I'm working with a legacy app that creates a new subdirectory in the source directory every time a new user logs in. Until now, it hasn't been under source control, so I added it to Git and pushed to our company repository--source code plus a few hundred subdirectories. Now, every time I make a change to the code and deploy to production, I have to first git add all of the subdirectories that have been created since my last commit, then commit these before I can pull down. Tedious, to say the least.

So, my question is, how can I command .gitignore to turn blind eye to any subdirectory old or new, with the exception of those that are related to the code?

CodePudding user response:

Let's say you have the following structure in your project directory (which I assume is also your repository root):

./
├── foo/
│   └── foo
├── bar/
│   └── baz
├── docs/
│   └── release/
│       └── 0.0.1
├── README.md
└── src/
    └── code

where directories foo/ and bar/ are examples of build results (that you don't want to track) with non-predictable names (or otherwise hard to explicitly list in .gitignore individually or with some sufficiently narrow pattern), while docs/ and src/ contain (only) files (and subdirs) to be tracked, and that you also want to track any files directly in the repository's root directory (such as currently README.md).

This can be achieved with the following .gitignore in the root directory of the repository:

# Ignore all subirectories (but not files in the repo root dir):
*/

# ... except for the actual project sub dirs:
!src/
!docs/

Explanation

See the documentation (available also through git help gitignore) for how the patterns in .gitignore work and how they interact with each other. Here's the ones used in the solution above (mostly copied straight or partially slightly rephrased from said documentation):

* matches anything except a slash (/, which is used as the directory separator) and thus any directory name or file name, at any level. That's a bit too broad, as we don't want to ignore files in the root directory. Appending a / makes a pattern only match directories, so */ matches any directory (at any level). (Files within ignored directories are also ignored, as Git doesn't track directories themselves anyway.)

So with */ we ignore the whole file tree except for files directly in the root directory.

But that's still too much. So we need selectively undo some of the ignoring. We can do that by additional patterns: The prefix ! "negates" a pattern; any matching file excluded by a previous pattern (here, by */) will become included again. But, as it is not possible to re-include a file if a parent directory of that file is excluded (Git doesn’t list excluded directories for performance reasons, so any patterns on contained files have no effect, no matter where they are defined.), docs/ and src/ will only match the top-level directory docs and src, but not, say, foo/src in case that'd exist.

(equivalent) solutions slightly longer, but easier to reason about

The interaction described above, while well-documented, seems somewhat obscure to me, so one might prefer to explicitly anchor the negated patterns to the root directory by prepending a /:

# Ignore all subirectories (but not files in the repo root dir):
*/

# ... except for the actual source dirs:
!/src/
!/docs/

It's possible to also anchor the first pattern that way (without any change to the resulting behavior):

# Ignore all subirectories (but not files in the repo root dir):
/*/

# ... except for the actual source dirs:
!/src/
!/docs/

CodePudding user response:

To ignore all subdirectories starting from a given point, simply put a slash a the end of your wildcard:

*/

For the rest, however, this shouldn't prevent you from pulling down, except if those directories get modified by the application after being commited.

Note that nothing prevents you to explicitly add a file to index then commit it even if it's ignored by .gitignore. In this case, it will be tracked just as all other regular files.

You probably want to use git rm --cached to mark them as deleted "from now on" in Git's history without actually removing them from working directory if you still need them to be here.

  •  Tags:  
  • git
  • Related