.gitignore文件可以用来排除某个文件夹,但是同时又包含特定的子文件夹。

1442
我有一个文件夹application/,我将其添加到.gitignore中。
application/文件夹内有一个application/language/gr文件夹。 我该如何包含这个文件夹? 我尝试了以下方法:
application/
!application/language/gr/

1
希望“.gitignore模式格式”的文档现在更加清晰易懂(2013年12月)。请参见下面的我的回答 - VonC
1
我的最爱的问题和答案,已经被添加到收藏夹以及浏览器书签中。 - codekiddy
20个回答

2297

如果你排除application/,那么它下面的所有内容都将被排除(即使后面有一些负排除模式(“取消忽略”)可能会匹配application/下的某些东西)。

要实现你想要的效果,你必须“取消忽略”所有要“取消忽略”的父目录。通常情况下,你需要成对编写规则:忽略目录中的所有内容,但不包括某个特定的子目录。

# you can skip this first one if it is not already excluded by prior patterns
!application/

application/*
!application/language/

application/language/*
!application/language/gr/

注意
结尾的/*是有意义的:

  • 模式dir/会排除名为dir的目录和(隐含地)目录下的所有内容。 使用dir/,Git永远不会查看dir下面的任何内容,因此也不会将“非排除”模式应用到dir下的任何内容。
  • 模式dir/*仅表示不包括dir本身;它排除dir下的所有内容。 使用dir/*,Git将处理dir的直接内容,从而给其他模式机会来“取消排除”某个内容的一部分(例如!dir/sub/)。

18
结尾的星号在这里是否有重要意义?如果是,那它们的区别是什么?根据 gitignore 文档中描述的算法,以斜杠结尾与其目录和该目录下的路径匹配。以星号结尾则会被视为 glob 模式处理。实验表明星号变体可以工作,但只以斜杠结尾的那种则不行。我想了解为什么会这样。 - seh
154
@seh: 是的,尾随的 /* 很重要。如果排除一个目录,Git 将永远不会查看该目录的内容。dir/ 模式排除了名为 dir 的目录以及(含蓄地)其下的所有内容。dir/* 模式仅仅排除了 dir 下的所有内容,而未涉及 dir 本身。使用 dir/,Git 将不会查看 dir 下面的任何东西,因此不会将“取消排除”模式应用于 dir 下的任何内容。使用 dir/*,Git 将处理 dir 的直接内容,使其他模式有机会“取消排除”某些位于内容中的部分 (!dir/sub/)。 - Chris Johnsen
13
啊,这就解释了。无论我读了多少遍 gitignore 文档,当倒置的模式不起作用时,我都没有理解。有了你的解释,现在清楚了。gitignore 文档需要一个“示例”部分来说明如何实现这一点。 - seh
10
由于.gitignore文件的问题,我没能让它正常工作。所以我改为在进入目录后强制添加这些文件。命令为 cd 进入目标目录后使用 git add -f . - K0D4
2
请注意,您不能依赖于git status的输出,它只会告诉您顶级目录将被添加。相反,对顶级目录进行git add,然后git status将(希望)列出已通过模式匹配的子集文件。 - Matthew Strawbridge
显示剩余12条评论

185

Karsten Blees (kblees)提交的Commit 59856de是为了Git 1.9/2.0(2014年第一季度)的内容,澄清了以下问题:

gitignore.txt:澄清被排除的目录的递归性质

可选的前缀“!”会否定该模式;任何与先前模式不匹配的文件都将被再次包含。 如果一个文件的父目录被排除在外,那么是不可能重新包含该文件的。(*) (*: 除非在git 2.8+中满足某些条件,请参见下文) 出于性能原因,Git不会列出被排除的目录,因此对所包含文件的任何模式都没有影响,无论它们定义在何处。 对于以字面量“!”开头的模式,需在第一个“!”之前加上反斜杠(“\”),例如“\!important!.txt”。 以下是一个示例,排除除特定目录foo/bar之外的所有内容(注意/* - 如果没有斜杠,通配符还将排除foo/bar内的所有内容):
 --------------------------------------------------------------
     $ cat .gitignore
     # exclude everything except directory foo/bar
     /*
     !/foo
     /foo/*
     !/foo/bar
 --------------------------------------------------------------
在您的情况下:
application/*
!application/**/
application/language/*
!application/language/**/
!application/language/gr/**

在能够将给定文件夹中的文件列入白名单之前,您必须首先将文件夹列入白名单。


2016年2月/3月更新:

请注意,使用 Git 2.9.x / 2.10(2016年中期?)可能会重新包含一个文件,如果该文件的父目录被排除如果在路径中没有通配符,则可以重新包含

Nguyễn Thái Ngọc Duy(pclouds正在尝试添加此功能:

所以在 git 2.9+ 中可能会起作用,但最终被撤销了:

application/
!application/language/gr/

1
@DavidHancock 抱歉,我已经编辑了答案:这还不可用。 - VonC
谢谢你澄清了这个问题。我现在有一个庞大的.gitignore文件。希望它能被纳入2.9版本中。 - David Hancock
1
@DavidHancock 我也是:我不得不多次编辑超过13个Stack Overflow答案! - VonC
5
Git 2.9昨天发布了。确认答案中提到的application/ + !application/language/gr/模式按预期工作。 - Ray Shan
2
@RayShan Strange:我看到了撤销该功能的提交,但我没有看到(且发布说明https://github.com/git/git/blob/master/Documentation/RelNotes/2.9.0.txt中也没有提到)任何改进.gitignore规则的提交。 - VonC
显示剩余3条评论

79

@Chris Johnsen的答案很好,但是对于更新版本的Git(1.8.2或更高版本),你可以利用双星号模式来实现更简洁的解决方案:

# assuming the root folder you want to ignore is 'application'
application/**/*

# the subfolder(s) you want to track:
!application/language/gr/

这样您就不必“取消忽略”要跟踪的子文件夹的父目录。


从 Git 2.17.0 开始(不确定在此版本之前的多早,可能可以追溯到1.8.2),使用**模式与排除每个子目录的方式结合使用,可以跟踪到您的文件。例如:

# assuming the root folder you want to ignore is 'application'
application/**

# Explicitly track certain content nested in the 'application' folder:
!application/language/
!application/language/gr/
!application/language/gr/** # Example adding all files & folder in the 'gr' folder
!application/language/gr/SomeFile.txt # Example adding specific file in the 'gr' folder

9
哀哉,这个操作失败了(Git 1.8.4.msysgit.0),因为模式“*”可以匹配零个子文件夹,“”会匹配“language”并将其排除在外,防止包括“gr”。像@Chris Johnson建议的完整的父级链仍然似乎是必要的。 - Sean Gugler
3
听起来很完美,但在我的git 2.3.7上无效... /www/**/* !/www/config.xml !/www/res config.xml和res目录仍然被忽略了。 - Rob
@Rob,你还需要添加!/www/res/。你可以使用folder/**/*模式,但仍然需要为每个要添加的子目录添加排除项。这仍然比忽略/排除组合更短且更易读。 - Ben Kane
我知道这是一个旧评论,但如果你感兴趣的话,我已经添加了一个编辑来记录这种方法。 - Ben Kane

53

我发现只有这个实际上有效。

**/node_modules/*
!**/node_modules/keep-dir

31

关于这个问题已经有许多类似的提问,所以我将发布之前编写的内容:

我在我的机器上让它工作的唯一方法是这样做:

# Ignore all directories, and all sub-directories, and it's contents:
*/*

#Now ignore all files in the current directory 
#(This fails to ignore files without a ".", for example 
#'file.txt' works, but 
#'file' doesn't):
*.*

#Only Include these specific directories and subdirectories:
!wordpress/
!wordpress/*/
!wordpress/*/wp-content/
!wordpress/*/wp-content/themes/
!wordpress/*/wp-content/themes/*
!wordpress/*/wp-content/themes/*/*
!wordpress/*/wp-content/themes/*/*/*
!wordpress/*/wp-content/themes/*/*/*/*
!wordpress/*/wp-content/themes/*/*/*/*/*
请注意,您需要明确允许每个要包含的级别的内容。因此,如果我在主题下有五个子目录,则仍然需要拼写出来。 这是来自@Yarin在此处的评论:https://dev59.com/Mm435IYBdhLWcg3wvyw5#5250314 以下是一些有用的主题: 我也尝试过
*
*/*
**/**

以及 **/wp-content/themes/**

或者 /wp-content/themes/**/*

对我来说都没有用,需要进行大量的试错!


3
注意:在 .gitignore 文件中顺序也很重要,因此请确保将你的 ! 规则放在底部。 - starbeamrainbowlabs
3
这比 JavaScript 作为一种语言本身更令人尴尬。为什么会有那么多不同且荒谬的答案来解决一个如此简单的问题?(我是在批评 Git,而不是你的解决方案)。Git 不是坏的,但它有一些不好的部分。 - Joel M

17

最简单且可能最好的方法是尝试手动添加文件(通常这比.gitignore风格的规则优先):

最简单且可能最好的方法是尝试手动添加文件(通常这比.gitignore风格的规则优先):

git add /path/to/module

如果文件已经被忽略,你可能需要使用-f。你甚至可能想要使用-N意向添加标志,建议你添加它们,但不是立即。对于我尚未准备好暂存的新文件,我经常这样做。


这是一个回答的副本,发布在可能是重复的问答上。我在这里重新发布它以增加可见性——我觉得不必遵循混乱的gitignore规则更容易。


14

我想要追踪位于/etc/nagios/的Nagios配置文件,以及位于/usr/lib64/nagios/plugins/的插件。为此,我已在/中初始化了一个git存储库,并使用以下排除列表:

/*
!etc
etc/*
!etc/nagios
!usr
usr/*
!usr/lib64
usr/lib64/*
!usr/lib64/nagios
usr/lib64/nagios/*
!usr/lib64/nagios/plugins

Git 按照这样的方式逐步遍历列表:

/*             exclude everything under / ...
!etc           but include /etc back
etc/*             exclude everything under /etc/...
!etc/nagios       but include /etc/nagios back
!usr           but include /usr back
usr/*             exclude everything under /usr/...
                  and so on...

git add node_modules/.vite/deps/HavokPhysics.wasm -f - STEEL

14

在子文件夹中添加名为.gitignore的文件,然后填写以下内容

!/Bin/

这对我有效!


13

我在这里找到了一个类似的案例,在laravel中默认情况下,.gitignore通过使用星号忽略所有内容,然后覆盖公共目录。 (这也是@Chris Johnsen主要答案的解决方案,可能只是更加简洁明了。)

我在这里发现了一个相似的案例,在 Laravel 中,默认情况下,.gitignore 使用通配符忽略所有文件,然后覆盖 public 目录。(这与 @Chris Johnsen 的主要答案相同,只不过更加简洁。)

*
!public
!.gitignore

如果你遇到了 OP 场景,仅此是不够的。

如果你想要提交 public 目录下的特定子文件夹(比如在 public/products 目录下),你想要包含一层子文件夹内的文件,例如要包含 public/products/a/b.jpg 这个文件,即使你添加了特定的规则,例如 !/public/products, !public/products/* 等等,这些文件也无法被正确地检测到。

解决方案是确保你为每个路径级别都添加一个条目来覆盖它们,像这样:

*
!.gitignore
!public/
!public/*/
!public/products/
!public/products/*
!public/products/*/
!public/products/*/
!public/products/*/*

12

2
顺便说一句,对我来说,只需要使用.idea/*!.idea/runConfigurations/就足够了。不需要写.idea!.idea/ - undefined
1
顺便说一句,对我来说只需要使用.idea/*!.idea/runConfigurations/就足够了。不需要写.idea!.idea/ - Mike

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,