gitignore: Ignore all files in folder hierarchy ex

2019-01-18 02:09发布

I'd like to ignore all files below and in a folder except a specific filetype that could be somewhere in the folders hierarchy:

Example

/Test
/Test/unknown/folder/structure/below

Now I'd like to ignore all files in and below the Test folder except a certain css file named layout.css, e.g.:

/Test/layout.css
/Test/fileto.ignore
/Test/another/folder/ig.nore
/Test/in/a/unknown/folder/layout.css
/Test/in/a/unknown/folder/ignore.me

.gitignore should ignore

/Test/fileto.ignore
/Test/another/folder/ig.nore
/Test/in/a/unknown/folder/ignore.me

My .gitignore file does not work:

Test/
!layout.css

Any suggestions?

标签: git gitignore
3条回答
Viruses.
2楼-- · 2019-01-18 02:41

Quite old question but since I was struggling with this issue even now I figured I would share the actual solution to the problem.

The thing is while you cannot commit empty directories in Git as if they were ignored, this rule does not apply to .gitignore.

From https://git-scm.com/docs/gitignore

A trailing "/**" matches everything inside. For example, "abc/**" matches all files inside directory "abc", relative to the location of the .gitignore file, with infinite depth.

A slash followed by two consecutive asterisks then a slash matches zero or more directories. For example, "a/**/b" matches "a/b", "a/x/b", "a/x/y/b" and so on.

** Matches everything inside - literaly everything - files AND directories.

This leads us to another point in gitignore docs:

An optional prefix "!" which negates the pattern; any matching file excluded by a previous pattern will become included again. 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. Put a backslash ("\") in front of the first "!" for patterns that begin with a literal "!", for example, "!important!.txt".

If the pattern ends with a slash, it is removed for the purpose of the following description, but it would only find a match with a directory. In other words, foo/ will match a directory foo and paths underneath it, but will not match a regular file or a symbolic link foo (this is consistent with the way how pathspec works in general in Git).

Now, let's say we have a following dir structure:

/path/to/git/repo
|-- .gitignore
|-- /cache
    |-- somefile
    |-- README    
    |-- /dir
        |-- somefile2
        |-- README

And we want to ignore all files inside cache/ except for README files.

If we specify gitignore like this:

/cache/**
!README

Git will ignore everything except for /cache/README. The other README won't show up because its directory /cache/dir was excluded with /cache/** and !README won't even apply to it. To solve the issue we need to specify gitignore as this:

# Ignore everything inside cache/ dir
/cache/**
# Except for subdirectories(won't be commited anyway if there is no commited file inside)
!/cache/**/
# And except for README files
!README
查看更多
爷的心禁止访问
3楼-- · 2019-01-18 02:44

I was able to get your example to work by putting a .gitignore file in the Test/ directory with the following contents.

*
!*/
!.gitignore
!layout.css
查看更多
做个烂人
4楼-- · 2019-01-18 02:44

In order to accomplish what you want, you'll need to use some negative exclusions.

The basic idea is that you need to exclude every parent directory of any file that you want unignored.

So, if you want /Test/in/a/unknown/folder/layout.css to be added to your git repo, then you'll have to unignore /Test/, /Test/in/, /Test/in/a/, /Test/in/a/unknown/, and /Test/in/a/unknown/folder/.

Then, when you finally get to the directory with some ignored and some unignored files, you'll need to specify each individually as follows:

# Fille: .gitignore
!Test/
Test/*
!Test/layout.css
Test/fileto.ignore
!Test/another/
Test/another/*
!Test/another/folder/
Test/another/folder/*
!Test/in/
Test/in/*
!Test/in/a/
Test/in/a/*
!Test/in/a/unknown/
Test/in/a/unknown/*
!Test/in/a/unknown/folder/
Test/in/a/unknown/folder/ignore.me
!Test/in/a/unknown/folder/layout.css

So when you run $ git add-all you'll see your desired results:

$ git status
On branch master

Initial commit

Changes to be committed:
  (use "git rm --cached <file>..." to unstage)

    new file:   .gitignore
    new file:   Test/in/a/unknown/folder/layout.css
    new file:   Test/layout.css

Note: You can find an explanation of why git add-all is the best way to add files to a git repo at http://lexsheehan.blogspot.com/2014/05/git-add-update.html

查看更多
登录 后发表回答