I supposed this to do the trick:
*
!/dir1/
!/dir2/
!/file1
!/file2
But to my surprise, it didn't. It seems to work if I prepend a slash before the asterisk. But I don't really understand why it doesn't work before this change. Could anybody point out?
To use negating patterns !
in .gitignore
, just remember 2 rules.
Rule 1. Files and directories are separatedly handled in the patterns. To include a directory back doesn't mean its child files/directories are also included back.
I.E. The goal is to exclude all files/directories except everything inside /dir1
. The following does NOT work.
# CANNOT work
*
!/dir1/
Why? The reason is just the first rule. Though the directory dir1
is included again, but not all its child files/directories. And only including the directory without its children means nothing to git, since git won't track empty folders.
As a comparison, the following works.
# works
/*
!/dir1/
Why? Because /*
only matches the files/directories under the root directory directly, but not ignores any files/directories under sub-directories such as dir1
here. After !/dir1/
includes dir1
back, all its child files/directories are back naturely.
Someone may ask, if so, why the following doesn't work, since !/dir1/**
will includes all child files/directories of dir1
back?
# CANNOT work
*
!/dir1/**
The reason is in rule 2.
Rule 2. It won't include files/directories back if their parent directory is still ignored. For this rule, please read my answer to a SO question first.
That's why the previous patterns won't work. And if we add another negating pattern to include the parent directory dir1
back first, it works.
# works
*
!/dir1/
!/dir1/**
After understanding the 2 rules, the solutions for OP's case is quite straightforward.
The following works.
/*
!/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