0

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.

SymboLinker
  • 485
  • 3
  • 12
Tryer
  • 2,922
  • 1
  • 21
  • 33

4 Answers4

1

Including only deeply nested sets of files:

Generate the rules (offline) via a .gitinclude

Because of this question I've opened a GitHub repo for playing with the idea of having a .gitinclude file and let a command line app generate a section in a .gitignore file: https://github.com/gitinclude/gitinclude.NET

Executable files for windows, linux and osx can be downloaded via the links in the repository readme.

Or: add another .gitignore file in the deep subdirectory

Note that if you find it OK to have multiple .gitignore files, you could ignore a folder in one .gitignore file and then not-ignore a few files via a .gitignore file that you put in the deep subfolder where those files are. That would require only 2 rules instead of 8.

Or: use !*/ as suggested by torek

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

!*/ # tell git to look in all folders, even ignored ones, on the last line of your .gitignore file
SymboLinker
  • 485
  • 3
  • 12
  • I'd rather retain the flexibility of naming my files arbitrarily and have some other app do the heavy work of generating the appropriate multiple lines that go into the `.gitignore`. `.gitignore` generation difficulty should not force me to change my prior way of working/naming files, imo. – Tryer Apr 03 '22 at 17:22
  • I don’t know. Choosing a slightly different structure may let disappear the burden of needing to maintain a long gitignore file. The need to update the gitignore file for each new feature you add seems the problem to me. Not the way you can update it. – SymboLinker Apr 03 '22 at 17:54
  • I've written some code that solves the problem of your example. See my updated answer – SymboLinker Apr 03 '22 at 19:06
  • This is quite promising and thanks for sharing. I have to point out that the answer [here](https://stackoverflow.com/a/54870120/492307) allows specification of multiple files/folders: e.g.doc/*.lyx and doc/*.tex without repetition of the lines that may be common to these subfolders since they share the same parent folder. I suspect, that in the approach you have provided, one would have to separately list each folder/file combination and manually remove the repetitions. While I have not indicated this use case in the OP, it is quite common. – Tryer Apr 04 '22 at 02:02
  • @Tryer I've updated my code to support input of multiple files (or file sets) to include. I'm also playing with the idea of creating a console app that your editor could call to update your `.gitignore` file via a `.gitinclude` file. – SymboLinker Apr 04 '22 at 09:44
  • the link above leads to a page not found error. Could you please check/correct it? – Tryer Apr 04 '22 at 10:16
  • 1
    Oops, the repo was not public but private. Now it is public. – SymboLinker Apr 04 '22 at 10:59
1

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.

torek
  • 389,216
  • 48
  • 524
  • 664
1

As far as creating and maintaining the .gitignore you might look at plugins for your IDEs.

For example, with IntelliJ/PyCharm, this might be GUI enough for what you are trying to do: https://plugins.jetbrains.com/plugin/7495--ignore

astrochun
  • 1,279
  • 1
  • 3
  • 15
  • The title is no longer “Easier way to create a .gitignore file” and now better reflects the problem, which is not about creating a `.gitignore` file, so you may want to delete this answer. – SymboLinker Apr 06 '22 at 09:46
  • If you look at the feature list for this plugin, you will see: "Add a selected file/directory to Gitignore rules from the popup menu" and "Generate Gitignore rules basing on [GitHub's templates collection][github-gitignore]", so I still think this is a good solution – astrochun Apr 06 '22 at 13:24
1

For your sample by far the simplest is

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

which I'd pronounce "recursively exclude everything in StrategicProject but still scan all the directories for further exceptions, like these tex files".


Unless or until you've got a remarkably large and complicated source tree it's often more compact to put something like

!*/
build/

at the end of your .gitignore to simply shut off all directory exclusions except for example here any build directory, I'd call that idiomatic, something people who've seen it before immediately understand.

jthill
  • 48,781
  • 4
  • 72
  • 120