44#include "attrcache.h"
55#include "path.h"
66#include "config.h"
7+ #include "fnmatch.h"
78
89#define GIT_IGNORE_INTERNAL "[internal]exclude"
910
1011#define GIT_IGNORE_DEFAULT_RULES ".\n..\n.git\n"
1112
13+ /**
14+ * A negative ignore can only unignore a file which is given explicitly before, thus
15+ *
16+ * foo
17+ * !foo/bar
18+ *
19+ * does not unignore 'foo/bar' as it's not in the list. However
20+ *
21+ * foo/<star>
22+ * !foo/bar
23+ *
24+ * does unignore 'foo/bar', as it is contained within the 'foo/<star>' rule.
25+ */
26+ static int does_negate_rule (int * out , git_vector * rules , git_attr_fnmatch * match )
27+ {
28+ int error = 0 ;
29+ size_t i ;
30+ git_attr_fnmatch * rule ;
31+ char * path ;
32+ git_buf buf = GIT_BUF_INIT ;
33+
34+ /* path of the file relative to the workdir, so we match the rules in subdirs */
35+ if (match -> containing_dir ) {
36+ git_buf_puts (& buf , match -> containing_dir );
37+ }
38+ if (git_buf_puts (& buf , match -> pattern ) < 0 )
39+ return -1 ;
40+
41+ path = git_buf_detach (& buf );
42+
43+ git_vector_foreach (rules , i , rule ) {
44+ /* no chance of matching w/o a wilcard */
45+ if (!(rule -> flags & GIT_ATTR_FNMATCH_HASWILD ))
46+ continue ;
47+
48+ /*
49+ * If we're dealing with a directory (which we know via the
50+ * strchr() check) we want to use 'dirname/<star>' as the
51+ * pattern so p_fnmatch() honours FNM_PATHNAME
52+ */
53+ git_buf_clear (& buf );
54+ if (rule -> containing_dir ) {
55+ git_buf_puts (& buf , rule -> containing_dir );
56+ }
57+ if (!strchr (rule -> pattern , '*' ))
58+ error = git_buf_printf (& buf , "%s/*" , rule -> pattern );
59+ else
60+ error = git_buf_puts (& buf , rule -> pattern );
61+
62+ if (error < 0 )
63+ goto out ;
64+
65+
66+ if ((error = p_fnmatch (git_buf_cstr (& buf ), path , FNM_PATHNAME )) < 0 ) {
67+ giterr_set (GITERR_INVALID , "error matching pattern" );
68+ goto out ;
69+ }
70+
71+ /* if we found a match, we want to keep this rule */
72+ if (error != FNM_NOMATCH ) {
73+ * out = 1 ;
74+ error = 0 ;
75+ goto out ;
76+ }
77+ }
78+
79+ * out = 0 ;
80+ error = 0 ;
81+
82+ out :
83+ git__free (path );
84+ git_buf_free (& buf );
85+ return error ;
86+ }
87+
1288static int parse_ignore_file (
1389 git_repository * repo , git_attr_file * attrs , const char * data )
1490{
@@ -32,6 +108,8 @@ static int parse_ignore_file(
32108 }
33109
34110 while (!error && * scan ) {
111+ int valid_rule = 1 ;
112+
35113 if (!match && !(match = git__calloc (1 , sizeof (* match )))) {
36114 error = -1 ;
37115 break ;
@@ -48,11 +126,16 @@ static int parse_ignore_file(
48126 match -> flags |= GIT_ATTR_FNMATCH_ICASE ;
49127
50128 scan = git__next_line (scan );
51- error = git_vector_insert (& attrs -> rules , match );
129+
130+ /* if a negative match doesn't actually do anything, throw it away */
131+ if (match -> flags & GIT_ATTR_FNMATCH_NEGATIVE )
132+ error = does_negate_rule (& valid_rule , & attrs -> rules , match );
133+
134+ if (!error && valid_rule )
135+ error = git_vector_insert (& attrs -> rules , match );
52136 }
53137
54- if (error != 0 ) {
55- git__free (match -> pattern );
138+ if (error != 0 || !valid_rule ) {
56139 match -> pattern = NULL ;
57140
58141 if (error == GIT_ENOTFOUND )
0 commit comments