To use negative patterns ! in .gitignore , just remember 2 rules.
Rule 1. Files and directories are separated from each other in templates. To include a reverse directory does not mean that its child files / directories are also included back.
those. The goal is to exclude all files / directories except everything that is inside /dir1 . Below does NOT work.
Why? The reason is only in the first rule. Although the dir1 directory is included again, but not all of its child files / directories. And just including a directory without its children does not mean git, since git will not track empty folders.
For comparison:
Why? Since /* matches only files / directories in the root directory directly, but does not ignore files / directories under subdirectories like dir1 here. After !/dir1/ includes dir1 back, all of its child files / directories are correct.
Someone might ask, if so, why the following does not work, since !/dir1/** will include all the dir1 child files / directories back?
The reason is in rule 2.
Rule 2. It will not include files / directories if their parent directory is still ignored. For this rule, please read my answer to the SO question .
This is why previous templates will not work. And if we add another negation pattern to include the parent directory dir1 back, it works.
Having understood the 2 rules, the solutions for the OP case are quite simple.
The following work.
/* !/dir1/ !/dir2/ !/file1 !/file2
And the following also works.
* !/dir1/ !/dir1/** !/dir2/ !/dir2/** !/file1 !/file2
To make it simple as follows.
* !*/ !/dir1/** !/dir2/** !/file1 !/file2