Commit 90997e405d6bf7abb3f9ed336f5f99639d52a15e

Edward Thomson 2015-05-12T12:14:55

attr: less path mangling during attribute matching When handling attr matching, simply compare the directory path where the attribute file resides to the path being matched. Skip over commonality to allow us to compare the contents of the attribute file to the remainder of the path. This allows us to more easily compare the pattern directly to the path, instead of trying to guess whether we want to compare the path's basename or the full path based on whether the match was inside a containing directory or not. This also allows us to do fewer translations on the pattern (trying to re-prefix it.)

diff --git a/src/attr_file.c b/src/attr_file.c
index 5ec5b44..dcf54fd 100644
--- a/src/attr_file.c
+++ b/src/attr_file.c
@@ -359,6 +359,7 @@ bool git_attr_fnmatch__match(
 	git_attr_fnmatch *match,
 	git_attr_path *path)
 {
+	const char *relpath = path->path;
 	const char *filename;
 	int flags = 0;
 
@@ -375,6 +376,8 @@ bool git_attr_fnmatch__match(
 			if (git__prefixcmp(path->path, match->containing_dir))
 				return 0;
 		}
+
+		relpath += match->containing_dir_length;
 	}
 
 	if (match->flags & GIT_ATTR_FNMATCH_ICASE)
@@ -383,7 +386,7 @@ bool git_attr_fnmatch__match(
 		flags |= FNM_LEADING_DIR;
 
 	if (match->flags & GIT_ATTR_FNMATCH_FULLPATH) {
-		filename = path->path;
+		filename = relpath;
 		flags |= FNM_PATHNAME;
 	} else {
 		filename = path->basename;
@@ -393,9 +396,6 @@ bool git_attr_fnmatch__match(
 	}
 
 	if ((match->flags & GIT_ATTR_FNMATCH_DIRECTORY) && !path->is_dir) {
-		int matchval;
-		char *matchpath;
-
 		/* for attribute checks or root ignore checks, fail match */
 		if (!(match->flags & GIT_ATTR_FNMATCH_IGNORE) ||
 			path->basename == path->path)
@@ -403,32 +403,24 @@ bool git_attr_fnmatch__match(
 
 		flags |= FNM_LEADING_DIR;
 
-		/* for ignore checks, use container of current item for check */
-		if (match->containing_dir)
-			matchpath = path->basename;
-		else
-			matchpath = path->path;
-
 		/* fail match if this is a file with same name as ignored folder */
 		bool samename = (match->flags & GIT_ATTR_FNMATCH_ICASE) ?
-			!strcasecmp(match->pattern, matchpath) :
-			!strcmp(match->pattern, matchpath);
+			!strcasecmp(match->pattern, relpath) :
+			!strcmp(match->pattern, relpath);
 
 		if (samename)
 			return false;
 
-		matchval = p_fnmatch(match->pattern, matchpath, flags);
-
-		return (matchval != FNM_NOMATCH);
+		return (p_fnmatch(match->pattern, relpath, flags) != FNM_NOMATCH);
 	}
 
 	/* if path is a directory prefix of a negated pattern, then match */
 	if ((match->flags & GIT_ATTR_FNMATCH_NEGATIVE) && path->is_dir) {
-		size_t pathlen = strlen(path->path);
+		size_t pathlen = strlen(relpath);
 		bool prefixed = (pathlen <= match->length) &&
 			((match->flags & GIT_ATTR_FNMATCH_ICASE) ?
-			 !strncasecmp(match->pattern, path->path, pathlen) :
-			 !strncmp(match->pattern, path->path, pathlen));
+			!strncasecmp(match->pattern, relpath, pathlen) :
+			!strncmp(match->pattern, relpath, pathlen));
 
 		if (prefixed && git_path_at_end_of_segment(&match->pattern[pathlen]))
 			return true;
@@ -647,7 +639,7 @@ int git_attr_fnmatch__parse(
 	}
 
 	if (context) {
-		char *slash = strchr(context, '/');
+		char *slash = strrchr(context, '/');
 		size_t len;
 		if (slash) {
 			/* include the slash for easier matching */
@@ -657,27 +649,7 @@ int git_attr_fnmatch__parse(
 		}
 	}
 
-	if ((spec->flags & GIT_ATTR_FNMATCH_FULLPATH) != 0 &&
-		context != NULL && git_path_root(pattern) < 0)
-	{
-		/* use context path minus the trailing filename */
-		char *slash = strrchr(context, '/');
-		size_t contextlen = slash ? slash - context + 1 : 0;
-
-		/* given an unrooted fullpath match from a file inside a repo,
-		 * prefix the pattern with the relative directory of the source file
-		 */
-		spec->pattern = git_pool_malloc(
-			pool, (uint32_t)(contextlen + spec->length + 1));
-		if (spec->pattern) {
-			memcpy(spec->pattern, context, contextlen);
-			memcpy(spec->pattern + contextlen, pattern, spec->length);
-			spec->length += contextlen;
-			spec->pattern[spec->length] = '\0';
-		}
-	} else {
-		spec->pattern = git_pool_strndup(pool, pattern, spec->length);
-	}
+	spec->pattern = git_pool_strndup(pool, pattern, spec->length);
 
 	if (!spec->pattern) {
 		*base = git__next_line(pattern);