Merge branch 'jc/directory-attrs-regression-fix'
Fix 1.8.1.x regression that stopped matching "dir" (without trailing slash) to a directory "dir". * jc/directory-attrs-regression-fix: t: check that a pattern without trailing slash matches a directory dir.c::match_pathname(): pay attention to the length of string parameters dir.c::match_pathname(): adjust patternlen when shifting pattern dir.c::match_basename(): pay attention to the length of string parameters attr.c::path_matches(): special case paths that end with a slash attr.c::path_matches(): the basename is part of the pathnamemaint
						commit
						f30366b27a
					
				
							
								
								
									
										25
									
								
								attr.c
								
								
								
								
							
							
						
						
									
										25
									
								
								attr.c
								
								
								
								
							|  | @ -657,24 +657,24 @@ static void prepare_attr_stack(const char *path, int dirlen) | ||||||
| } | } | ||||||
|  |  | ||||||
| static int path_matches(const char *pathname, int pathlen, | static int path_matches(const char *pathname, int pathlen, | ||||||
| 			const char *basename, | 			int basename_offset, | ||||||
| 			const struct pattern *pat, | 			const struct pattern *pat, | ||||||
| 			const char *base, int baselen) | 			const char *base, int baselen) | ||||||
| { | { | ||||||
| 	const char *pattern = pat->pattern; | 	const char *pattern = pat->pattern; | ||||||
| 	int prefix = pat->nowildcardlen; | 	int prefix = pat->nowildcardlen; | ||||||
|  | 	int isdir = (pathlen && pathname[pathlen - 1] == '/'); | ||||||
|  |  | ||||||
| 	if ((pat->flags & EXC_FLAG_MUSTBEDIR) && | 	if ((pat->flags & EXC_FLAG_MUSTBEDIR) && !isdir) | ||||||
| 	    ((!pathlen) || (pathname[pathlen-1] != '/'))) |  | ||||||
| 		return 0; | 		return 0; | ||||||
|  |  | ||||||
| 	if (pat->flags & EXC_FLAG_NODIR) { | 	if (pat->flags & EXC_FLAG_NODIR) { | ||||||
| 		return match_basename(basename, | 		return match_basename(pathname + basename_offset, | ||||||
| 				      pathlen - (basename - pathname), | 				      pathlen - basename_offset - isdir, | ||||||
| 				      pattern, prefix, | 				      pattern, prefix, | ||||||
| 				      pat->patternlen, pat->flags); | 				      pat->patternlen, pat->flags); | ||||||
| 	} | 	} | ||||||
| 	return match_pathname(pathname, pathlen, | 	return match_pathname(pathname, pathlen - isdir, | ||||||
| 			      base, baselen, | 			      base, baselen, | ||||||
| 			      pattern, prefix, pat->patternlen, pat->flags); | 			      pattern, prefix, pat->patternlen, pat->flags); | ||||||
| } | } | ||||||
|  | @ -703,7 +703,7 @@ static int fill_one(const char *what, struct match_attr *a, int rem) | ||||||
| 	return rem; | 	return rem; | ||||||
| } | } | ||||||
|  |  | ||||||
| static int fill(const char *path, int pathlen, const char *basename, | static int fill(const char *path, int pathlen, int basename_offset, | ||||||
| 		struct attr_stack *stk, int rem) | 		struct attr_stack *stk, int rem) | ||||||
| { | { | ||||||
| 	int i; | 	int i; | ||||||
|  | @ -713,7 +713,7 @@ static int fill(const char *path, int pathlen, const char *basename, | ||||||
| 		struct match_attr *a = stk->attrs[i]; | 		struct match_attr *a = stk->attrs[i]; | ||||||
| 		if (a->is_macro) | 		if (a->is_macro) | ||||||
| 			continue; | 			continue; | ||||||
| 		if (path_matches(path, pathlen, basename, | 		if (path_matches(path, pathlen, basename_offset, | ||||||
| 				 &a->u.pat, base, stk->originlen)) | 				 &a->u.pat, base, stk->originlen)) | ||||||
| 			rem = fill_one("fill", a, rem); | 			rem = fill_one("fill", a, rem); | ||||||
| 	} | 	} | ||||||
|  | @ -752,7 +752,8 @@ static void collect_all_attrs(const char *path) | ||||||
| { | { | ||||||
| 	struct attr_stack *stk; | 	struct attr_stack *stk; | ||||||
| 	int i, pathlen, rem, dirlen; | 	int i, pathlen, rem, dirlen; | ||||||
| 	const char *basename, *cp, *last_slash = NULL; | 	const char *cp, *last_slash = NULL; | ||||||
|  | 	int basename_offset; | ||||||
|  |  | ||||||
| 	for (cp = path; *cp; cp++) { | 	for (cp = path; *cp; cp++) { | ||||||
| 		if (*cp == '/' && cp[1]) | 		if (*cp == '/' && cp[1]) | ||||||
|  | @ -760,10 +761,10 @@ static void collect_all_attrs(const char *path) | ||||||
| 	} | 	} | ||||||
| 	pathlen = cp - path; | 	pathlen = cp - path; | ||||||
| 	if (last_slash) { | 	if (last_slash) { | ||||||
| 		basename = last_slash + 1; | 		basename_offset = last_slash + 1 - path; | ||||||
| 		dirlen = last_slash - path; | 		dirlen = last_slash - path; | ||||||
| 	} else { | 	} else { | ||||||
| 		basename = path; | 		basename_offset = 0; | ||||||
| 		dirlen = 0; | 		dirlen = 0; | ||||||
| 	} | 	} | ||||||
|  |  | ||||||
|  | @ -773,7 +774,7 @@ static void collect_all_attrs(const char *path) | ||||||
|  |  | ||||||
| 	rem = attr_nr; | 	rem = attr_nr; | ||||||
| 	for (stk = attr_stack; 0 < rem && stk; stk = stk->prev) | 	for (stk = attr_stack; 0 < rem && stk; stk = stk->prev) | ||||||
| 		rem = fill(path, pathlen, basename, stk, rem); | 		rem = fill(path, pathlen, basename_offset, stk, rem); | ||||||
| } | } | ||||||
|  |  | ||||||
| int git_check_attr(const char *path, int num, struct git_attr_check *check) | int git_check_attr(const char *path, int num, struct git_attr_check *check) | ||||||
|  |  | ||||||
							
								
								
									
										58
									
								
								dir.c
								
								
								
								
							
							
						
						
									
										58
									
								
								dir.c
								
								
								
								
							|  | @ -59,6 +59,35 @@ inline int git_fnmatch(const char *pattern, const char *string, | ||||||
| 	return fnmatch(pattern, string, fnm_flags); | 	return fnmatch(pattern, string, fnm_flags); | ||||||
| } | } | ||||||
|  |  | ||||||
|  | static int fnmatch_icase_mem(const char *pattern, int patternlen, | ||||||
|  | 			     const char *string, int stringlen, | ||||||
|  | 			     int flags) | ||||||
|  | { | ||||||
|  | 	int match_status; | ||||||
|  | 	struct strbuf pat_buf = STRBUF_INIT; | ||||||
|  | 	struct strbuf str_buf = STRBUF_INIT; | ||||||
|  | 	const char *use_pat = pattern; | ||||||
|  | 	const char *use_str = string; | ||||||
|  |  | ||||||
|  | 	if (pattern[patternlen]) { | ||||||
|  | 		strbuf_add(&pat_buf, pattern, patternlen); | ||||||
|  | 		use_pat = pat_buf.buf; | ||||||
|  | 	} | ||||||
|  | 	if (string[stringlen]) { | ||||||
|  | 		strbuf_add(&str_buf, string, stringlen); | ||||||
|  | 		use_str = str_buf.buf; | ||||||
|  | 	} | ||||||
|  |  | ||||||
|  | 	if (ignore_case) | ||||||
|  | 		flags |= WM_CASEFOLD; | ||||||
|  | 	match_status = wildmatch(use_pat, use_str, flags, NULL); | ||||||
|  |  | ||||||
|  | 	strbuf_release(&pat_buf); | ||||||
|  | 	strbuf_release(&str_buf); | ||||||
|  |  | ||||||
|  | 	return match_status; | ||||||
|  | } | ||||||
|  |  | ||||||
| static size_t common_prefix_len(const char **pathspec) | static size_t common_prefix_len(const char **pathspec) | ||||||
| { | { | ||||||
| 	const char *n, *first; | 	const char *n, *first; | ||||||
|  | @ -626,15 +655,20 @@ int match_basename(const char *basename, int basenamelen, | ||||||
| 		   int flags) | 		   int flags) | ||||||
| { | { | ||||||
| 	if (prefix == patternlen) { | 	if (prefix == patternlen) { | ||||||
| 		if (!strcmp_icase(pattern, basename)) | 		if (patternlen == basenamelen && | ||||||
|  | 		    !strncmp_icase(pattern, basename, basenamelen)) | ||||||
| 			return 1; | 			return 1; | ||||||
| 	} else if (flags & EXC_FLAG_ENDSWITH) { | 	} else if (flags & EXC_FLAG_ENDSWITH) { | ||||||
|  | 		/* "*literal" matching against "fooliteral" */ | ||||||
| 		if (patternlen - 1 <= basenamelen && | 		if (patternlen - 1 <= basenamelen && | ||||||
| 		    !strcmp_icase(pattern + 1, | 		    !strncmp_icase(pattern + 1, | ||||||
| 				  basename + basenamelen - patternlen + 1)) | 				   basename + basenamelen - (patternlen - 1), | ||||||
|  | 				   patternlen - 1)) | ||||||
| 			return 1; | 			return 1; | ||||||
| 	} else { | 	} else { | ||||||
| 		if (fnmatch_icase(pattern, basename, 0) == 0) | 		if (fnmatch_icase_mem(pattern, patternlen, | ||||||
|  | 				      basename, basenamelen, | ||||||
|  | 				      0) == 0) | ||||||
| 			return 1; | 			return 1; | ||||||
| 	} | 	} | ||||||
| 	return 0; | 	return 0; | ||||||
|  | @ -654,6 +688,7 @@ int match_pathname(const char *pathname, int pathlen, | ||||||
| 	 */ | 	 */ | ||||||
| 	if (*pattern == '/') { | 	if (*pattern == '/') { | ||||||
| 		pattern++; | 		pattern++; | ||||||
|  | 		patternlen--; | ||||||
| 		prefix--; | 		prefix--; | ||||||
| 	} | 	} | ||||||
|  |  | ||||||
|  | @ -680,13 +715,22 @@ int match_pathname(const char *pathname, int pathlen, | ||||||
| 		if (strncmp_icase(pattern, name, prefix)) | 		if (strncmp_icase(pattern, name, prefix)) | ||||||
| 			return 0; | 			return 0; | ||||||
| 		pattern += prefix; | 		pattern += prefix; | ||||||
|  | 		patternlen -= prefix; | ||||||
| 		name    += prefix; | 		name    += prefix; | ||||||
| 		namelen -= prefix; | 		namelen -= prefix; | ||||||
|  |  | ||||||
|  | 		/* | ||||||
|  | 		 * If the whole pattern did not have a wildcard, | ||||||
|  | 		 * then our prefix match is all we need; we | ||||||
|  | 		 * do not need to call fnmatch at all. | ||||||
|  | 		 */ | ||||||
|  | 		if (!patternlen && !namelen) | ||||||
|  | 			return 1; | ||||||
| 	} | 	} | ||||||
|  |  | ||||||
| 	return wildmatch(pattern, name, | 	return fnmatch_icase_mem(pattern, patternlen, | ||||||
| 			 WM_PATHNAME | (ignore_case ? WM_CASEFOLD : 0), | 				 name, namelen, | ||||||
| 			 NULL) == 0; | 				 WM_PATHNAME) == 0; | ||||||
| } | } | ||||||
|  |  | ||||||
| /* | /* | ||||||
|  |  | ||||||
|  | @ -27,6 +27,25 @@ test_expect_success 'setup' ' | ||||||
| 	echo ignored-only-if-dir/ export-ignore >>.git/info/attributes && | 	echo ignored-only-if-dir/ export-ignore >>.git/info/attributes && | ||||||
| 	git add ignored-only-if-dir && | 	git add ignored-only-if-dir && | ||||||
|  |  | ||||||
|  | 	mkdir -p ignored-without-slash && | ||||||
|  | 	echo "ignored without slash" >ignored-without-slash/foo && | ||||||
|  | 	git add ignored-without-slash/foo && | ||||||
|  | 	echo "ignored-without-slash export-ignore" >>.git/info/attributes && | ||||||
|  |  | ||||||
|  | 	mkdir -p wildcard-without-slash && | ||||||
|  | 	echo "ignored without slash" >wildcard-without-slash/foo && | ||||||
|  | 	git add wildcard-without-slash/foo && | ||||||
|  | 	echo "wild*-without-slash export-ignore" >>.git/info/attributes && | ||||||
|  |  | ||||||
|  | 	mkdir -p deep/and/slashless && | ||||||
|  | 	echo "ignored without slash" >deep/and/slashless/foo && | ||||||
|  | 	git add deep/and/slashless/foo && | ||||||
|  | 	echo "deep/and/slashless export-ignore" >>.git/info/attributes && | ||||||
|  |  | ||||||
|  | 	mkdir -p deep/with/wildcard && | ||||||
|  | 	echo "ignored without slash" >deep/with/wildcard/foo && | ||||||
|  | 	git add deep/with/wildcard/foo && | ||||||
|  | 	echo "deep/*t*/wildcard export-ignore" >>.git/info/attributes && | ||||||
|  |  | ||||||
| 	mkdir -p one-level-lower/two-levels-lower/ignored-only-if-dir && | 	mkdir -p one-level-lower/two-levels-lower/ignored-only-if-dir && | ||||||
| 	echo ignored by ignored dir >one-level-lower/two-levels-lower/ignored-only-if-dir/ignored-by-ignored-dir && | 	echo ignored by ignored dir >one-level-lower/two-levels-lower/ignored-only-if-dir/ignored-by-ignored-dir && | ||||||
|  | @ -49,6 +68,14 @@ test_expect_exists	archive/not-ignored-dir/ignored-only-if-dir | ||||||
| test_expect_exists	archive/not-ignored-dir/ | test_expect_exists	archive/not-ignored-dir/ | ||||||
| test_expect_missing	archive/ignored-only-if-dir/ | test_expect_missing	archive/ignored-only-if-dir/ | ||||||
| test_expect_missing	archive/ignored-ony-if-dir/ignored-by-ignored-dir | test_expect_missing	archive/ignored-ony-if-dir/ignored-by-ignored-dir | ||||||
|  | test_expect_missing	archive/ignored-without-slash/ && | ||||||
|  | test_expect_missing	archive/ignored-without-slash/foo && | ||||||
|  | test_expect_missing	archive/wildcard-without-slash/ | ||||||
|  | test_expect_missing	archive/wildcard-without-slash/foo && | ||||||
|  | test_expect_missing	archive/deep/and/slashless/ && | ||||||
|  | test_expect_missing	archive/deep/and/slashless/foo && | ||||||
|  | test_expect_missing	archive/deep/with/wildcard/ && | ||||||
|  | test_expect_missing	archive/deep/with/wildcard/foo && | ||||||
| test_expect_exists	archive/one-level-lower/ | test_expect_exists	archive/one-level-lower/ | ||||||
| test_expect_missing	archive/one-level-lower/two-levels-lower/ignored-only-if-dir/ | test_expect_missing	archive/one-level-lower/two-levels-lower/ignored-only-if-dir/ | ||||||
| test_expect_missing	archive/one-level-lower/two-levels-lower/ignored-ony-if-dir/ignored-by-ignored-dir | test_expect_missing	archive/one-level-lower/two-levels-lower/ignored-ony-if-dir/ignored-by-ignored-dir | ||||||
|  |  | ||||||
		Loading…
	
		Reference in New Issue
	
	 Junio C Hamano
						Junio C Hamano