Search code examples
gitgitignore

Easier way to not-ignore a specific deeply nested set of files


I have deeply nested files that I would like to include in source control and have git track them.

Example is:

Projects/
    .git/
    StrategicProject/
        SpecificProject1/
            Method1/
                doc/
                   *.tex
    .gitignore

In each of the various folders there are other files/folders that I would not like to track. I only want to track, say, all of the .tex files in Projects/StrategicProject/SpecificProject1/Method1/doc/

As of now, to accomplish this, I have in my .gitignore file:

StrategicProject/* # ignore everything under this, exceptions below
!StrategicProject/SpecificProject1/ # don't ignore this
StrategicProject/SpecificProject1/* # ignore everything under this, exceptions below
!StrategicProject/SpecificProject1/Method1/ # don't ignore this
StrategicProject/SpecificProject1/Method1/* # ignore everything under this, exceptions below
!StrategicProject/SpecificProject1/Method1/doc/ # don't ignore this
StrategicProject/SpecificProject1/Method1/doc/* # ignore everything under this, exceptions below
!StrategicProject/SpecificProject1/Method1/doc/*.tex # don't ignore this

The above works, but is prone to error when I create a new folder structure in my working directory. It is also quite cumbersome. To track one set of .tex files in a nested folder needs creation of 8 lines in the .gitignore file. A fellow-SO user created an web-app to ease this specific difficult activity. See here

While that app works fine for now, it only works over the web. More generally, is there a gui app, that works offline (without having to use the internet), that can take a look at my folder structure and allows me to check/uncheck boxes (as part of its user interface) corresponding to files/folders and based on that automatically generate the appropriate .gitignore file?


ETA:

I use VSCode's default Git interface. While that does provide option to right click on a file and add it to .gitignore, that is not what I would like. I would like to not ignore a specific deeply nested set of files. I am open to trying out other editors for this task.


Solution

  • There is one useful, albeit somewhat computationally expensive, trick you can use. As you've seen, if you ignore a directory, Git never looks inside the directory, making it impossible to un-ignore files nested deeper under the directory. This generally saves Git a lot of time: git status has to recursively look at everything in the working tree, except that every time it hits an ignored directory, it doesn't have to look at anything there. Since lstat-ing each entity in each directory in a recursive travel of the working tree is usually the slowest part of a Git working-tree operation, bypassing some of these calls helps out a lot.

    Still, you can prevent Git from bypassing directories at all by simply making sure that !*/ is the last entry in each .gitignore file. No matter where Git is in its traversal, this last entry says if you encounter a directory, look inside it. Now you no longer need careful and explicit stuff like:

    StrategicProject/*
    !StrategicProject/SpecificProject1/
    StrategicProject/SpecificProject1/*
    !StrategicProject/SpecificProject1/Method1/
    StrategicProject/SpecificProject1/Method1/*
    !StrategicProject/SpecificProject1/Method1/doc/
    StrategicProject/SpecificProject1/Method1/doc/*
    !StrategicProject/SpecificProject1/Method1/doc/*.tex
    

    as you can simply list files that should be ignored; Git will always be searching everywhere.

    A proposed future enhancement (with no implementation)

    I think Git should automatically insert exceptions as needed. That is, we should be able to write:

    *.o
    generated-docs
    !generated-docs/*.in
    

    and Git should figure out that this "means", e.g.:

    generated-docs/*
    !generated-docs/*.in
    

    That is, the fact that we've excepted something within generated-docs means that Git should look inside generated-docs, even though the default is to skip over files that may be found there.