Commit 33d532dcfacbc0d936341848efc60dd55956ec10

Russell Belfer 2013-08-09T09:32:06

Merge pull request #1462 from yorah/fix/libgit2sharp-issue-379 status: fix handling of filenames with special prefixes

diff --git a/src/attr_file.c b/src/attr_file.c
index d880398..ca5f213 100644
--- a/src/attr_file.c
+++ b/src/attr_file.c
@@ -85,7 +85,7 @@ int git_attr_file__parse_buffer(
 		}
 
 		/* parse the next "pattern attr attr attr" line */
-		if (!(error = git_attr_fnmatch__parse(
+		if (!(error = git_attr_fnmatch__parse_gitattr_format(
 				&rule->match, attrs->pool, context, &scan)) &&
 			!(error = git_attr_assignment__parse(
 				repo, attrs->pool, &rule->assigns, &scan)))
@@ -337,23 +337,16 @@ void git_attr_path__free(git_attr_path *info)
  * GIT_ENOTFOUND if the fnmatch does not require matching, or
  * another error code there was an actual problem.
  */
-int git_attr_fnmatch__parse(
+int git_attr_fnmatch__parse_gitattr_format(
 	git_attr_fnmatch *spec,
 	git_pool *pool,
 	const char *source,
 	const char **base)
 {
-	const char *pattern, *scan;
-	int slash_count, allow_space;
+	const char *pattern;
 
 	assert(spec && base && *base);
 
-	if (parse_optimized_patterns(spec, pool, *base))
-		return 0;
-
-	spec->flags = (spec->flags & GIT_ATTR_FNMATCH_ALLOWSPACE);
-	allow_space = (spec->flags != 0);
-
 	pattern = *base;
 
 	while (git__isspace(*pattern)) pattern++;
@@ -375,6 +368,39 @@ int git_attr_fnmatch__parse(
 		pattern++;
 	}
 
+	if (git_attr_fnmatch__parse_shellglob_format(spec, pool, 
+		source, &pattern) < 0)
+			return -1;
+
+	*base = pattern;
+
+	return 0;
+}
+
+/*
+ * Fills a spec for the purpose of pure pathspec matching, not
+ * related to a gitattribute file parsing.
+ *
+ * This will return 0 if the spec was filled out, or
+ * another error code there was an actual problem.
+ */
+int git_attr_fnmatch__parse_shellglob_format(
+	git_attr_fnmatch *spec,
+	git_pool *pool,
+	const char *source,
+	const char **base)
+{
+	const char *pattern, *scan;
+	int slash_count, allow_space;
+
+	assert(spec && base && *base);
+
+	if (parse_optimized_patterns(spec, pool, *base))
+		return 0;
+
+	allow_space = (spec->flags & GIT_ATTR_FNMATCH_ALLOWSPACE) != 0;
+	pattern = *base;
+
 	slash_count = 0;
 	for (scan = pattern; *scan != '\0'; ++scan) {
 		/* scan until (non-escaped) white space */
@@ -610,6 +636,7 @@ static void git_attr_rule__clear(git_attr_rule *rule)
 	/* match.pattern is stored in a git_pool, so no need to free */
 	rule->match.pattern = NULL;
 	rule->match.length = 0;
+	rule->match.flags = 0;
 }
 
 void git_attr_rule__free(git_attr_rule *rule)
diff --git a/src/attr_file.h b/src/attr_file.h
index 15bba1c..afea1e1 100644
--- a/src/attr_file.h
+++ b/src/attr_file.h
@@ -115,7 +115,13 @@ extern uint32_t git_attr_file__name_hash(const char *name);
  * other utilities
  */
 
-extern int git_attr_fnmatch__parse(
+extern int git_attr_fnmatch__parse_gitattr_format(
+	git_attr_fnmatch *spec,
+	git_pool *pool,
+	const char *source,
+	const char **base);
+
+extern int git_attr_fnmatch__parse_shellglob_format(
 	git_attr_fnmatch *spec,
 	git_pool *pool,
 	const char *source,
diff --git a/src/ignore.c b/src/ignore.c
index cc90b0c..7d82804 100644
--- a/src/ignore.c
+++ b/src/ignore.c
@@ -39,7 +39,7 @@ static int parse_ignore_file(
 
 		match->flags = GIT_ATTR_FNMATCH_ALLOWSPACE;
 
-		if (!(error = git_attr_fnmatch__parse(
+		if (!(error = git_attr_fnmatch__parse_gitattr_format(
 			match, ignores->pool, context, &scan)))
 		{
 			match->flags |= GIT_ATTR_FNMATCH_IGNORE;
diff --git a/src/pathspec.c b/src/pathspec.c
index 625726e..4266bb9 100644
--- a/src/pathspec.c
+++ b/src/pathspec.c
@@ -85,7 +85,7 @@ int git_pathspec__vinit(
 
 		match->flags = GIT_ATTR_FNMATCH_ALLOWSPACE;
 
-		ret = git_attr_fnmatch__parse(match, strpool, NULL, &pattern);
+		ret = git_attr_fnmatch__parse_shellglob_format(match, strpool, NULL, &pattern);
 		if (ret == GIT_ENOTFOUND) {
 			git__free(match);
 			continue;
diff --git a/tests-clar/status/ignore.c b/tests-clar/status/ignore.c
index 4f6879c..1b41fed 100644
--- a/tests-clar/status/ignore.c
+++ b/tests-clar/status/ignore.c
@@ -459,3 +459,37 @@ void test_status_ignore__automatically_ignore_bad_files(void)
 	cl_git_pass(git_status_should_ignore(&ignored, g_repo, "path/whatever.c"));
 	cl_assert(!ignored);
 }
+
+void test_status_ignore__filenames_with_special_prefixes_do_not_interfere_with_status_retrieval(void)
+{
+	status_entry_single st;
+	char *test_cases[] = {
+		"!file",
+		"#blah",
+		"[blah]",
+		"[attr]",
+		"[attr]blah",
+		NULL
+	};
+	int i;
+
+	for (i = 0; *(test_cases + i) != NULL; i++) {
+		git_buf file = GIT_BUF_INIT;
+		char *file_name = *(test_cases + i);
+		git_repository *repo = cl_git_sandbox_init("empty_standard_repo");
+
+		cl_git_pass(git_buf_joinpath(&file, "empty_standard_repo", file_name));
+		cl_git_mkfile(git_buf_cstr(&file), "Please don't ignore me!");
+
+		memset(&st, 0, sizeof(st));
+		cl_git_pass(git_status_foreach(repo, cb_status__single, &st));
+		cl_assert(st.count == 1);
+		cl_assert(st.status == GIT_STATUS_WT_NEW);
+
+		cl_git_pass(git_status_file(&st.status, repo, file_name));
+		cl_assert(st.status == GIT_STATUS_WT_NEW);
+
+		cl_git_sandbox_cleanup();
+		git_buf_free(&file);
+	}
+}