Subject: [PATCH/WIP 03/10] pathspec: support :(literal) syntax for noglob pathspec

From: Nguyễn Thái Ngọc Duy <[email-masked]>
Date: Sun, 13 Jan 2013 19:49:32 +0700

Signed-off-by: Nguyễn Thái Ngọc Duy <[email-masked]>
---
 archive.c                  |  4 +++-
 builtin/add.c              |  8 +++++---
 builtin/checkout.c         |  4 +++-
 builtin/clean.c            |  4 +++-
 builtin/commit.c           |  6 ++++--
 builtin/diff.c             |  2 +-
 builtin/grep.c             |  4 +++-
 builtin/ls-files.c         |  3 ++-
 builtin/ls-tree.c          |  4 +++-
 builtin/rerere.c           |  4 +++-
 builtin/reset.c            |  4 +++-
 builtin/rm.c               |  4 +++-
 builtin/update-index.c     |  4 +++-
 cache.h                    |  1 +
 dir.c                      |  6 +++---
 revision.c                 |  5 +++--
 setup.c                    |  7 ++++---
 t/t6130-pathspec-noglob.sh | 18 ++++++++++++++++++
 tree-diff.c                |  2 +-
 tree-walk.c                |  2 +-
 20 files changed, 70 insertions(+), 26 deletions(-)

diff --git a/archive.c b/archive.c
index d9da58b..d3b6969 100644
--- a/archive.c
+++ b/archive.c
@@ -234,7 +234,9 @@ static void parse_pathspec_arg(const char **pathspec,
 	 * mark "used" pathspec. The magic mask cannot be lifted until
 	 * it does.
 	 */
-	parse_pathspec(&ar_args->pathspec, PATHSPEC_FROMTOP, 0, "", pathspec);
+	parse_pathspec(&ar_args->pathspec,
+		       PATHSPEC_FROMTOP | PATHSPEC_LITERAL,
+		       0, "", pathspec);
 	if (ar_args->pathspec.nr) {
 		pathspec = ar_args->pathspec._raw;
 		while (*pathspec) {
diff --git a/builtin/add.c b/builtin/add.c
index a7840c8..d09a07a 100644
--- a/builtin/add.c
+++ b/builtin/add.c
@@ -202,7 +202,8 @@ int interactive_add(int argc, const char **argv, const char *prefix, int patch)
 	 * Do not enable fancy magic here.  git-add--interactive may
 	 * not be able to handle it.
 	 */
-	parse_pathspec(&pathspec, PATHSPEC_FROMTOP,
+	parse_pathspec(&pathspec,
+		       PATHSPEC_FROMTOP | PATHSPEC_LITERAL,
 		       PATHSPEC_EMPTY_MATCH_ALL |
 		       PATHSPEC_SYMLINK_LEADING_PATH,
 		       prefix, argv);
@@ -377,7 +378,8 @@ int cmd_add(int argc, const char **argv, const char *prefix)
 	 * Check the "pathspec '%s' did not match any files" block
 	 * below before enabling new magic.
 	 */
-	parse_pathspec(&pathspec, PATHSPEC_FROMTOP,
+	parse_pathspec(&pathspec,
+		       PATHSPEC_FROMTOP | PATHSPEC_LITERAL,
 		       PATHSPEC_SYMLINK_LEADING_PATH |
 		       PATHSPEC_STRIP_SUBMODULE_SLASH_EXPENSIVE,
 		       prefix, argv);
@@ -414,7 +416,7 @@ int cmd_add(int argc, const char **argv, const char *prefix)
 		/*
 		 * file_exists() assumes exact match
 		 */
-		GUARD_PATHSPEC(&pathspec, PATHSPEC_FROMTOP);
+		GUARD_PATHSPEC(&pathspec, PATHSPEC_FROMTOP | PATHSPEC_LITERAL);
 
 		for (i = 0; i < pathspec.nr; i++) {
 			const char *path = pathspec.items[i].match;
diff --git a/builtin/checkout.c b/builtin/checkout.c
index 1b413e2..90f4a01 100644
--- a/builtin/checkout.c
+++ b/builtin/checkout.c
@@ -1112,7 +1112,9 @@ int cmd_checkout(int argc, const char **argv, const char *prefix)
 		 * cannot handle. Magic mask is pretty safe to be
 		 * lifted for new magic when opts.patch_mode == 0.
 		 */
-		parse_pathspec(&opts.pathspec, PATHSPEC_FROMTOP, 0, prefix, argv);
+		parse_pathspec(&opts.pathspec,
+			       PATHSPEC_FROMTOP | PATHSPEC_LITERAL,
+			       0, prefix, argv);
 
 		if (!opts.pathspec.nr)
 			die(_("invalid path specification"));
diff --git a/builtin/clean.c b/builtin/clean.c
index 1d8ff5f..b4ffa2b 100644
--- a/builtin/clean.c
+++ b/builtin/clean.c
@@ -100,7 +100,9 @@ int cmd_clean(int argc, const char **argv, const char *prefix)
 		add_exclude(exclude_list.items[i].string, "", 0,
 			    &dir.exclude_list[EXC_CMDL]);
 
-	parse_pathspec(&pathspec, PATHSPEC_FROMTOP, 0, prefix, argv);
+	parse_pathspec(&pathspec,
+		       PATHSPEC_FROMTOP | PATHSPEC_LITERAL,
+		       0, prefix, argv);
 
 	fill_directory(&dir, &pathspec);
 
diff --git a/builtin/commit.c b/builtin/commit.c
index 2011f98..433fdb9 100644
--- a/builtin/commit.c
+++ b/builtin/commit.c
@@ -281,7 +281,8 @@ static char *prepare_index(int argc, const char **argv, const char *prefix,
 
 	if (is_status)
 		refresh_flags |= REFRESH_UNMERGED;
-	parse_pathspec(&pathspec, PATHSPEC_FROMTOP,
+	parse_pathspec(&pathspec,
+		       PATHSPEC_FROMTOP | PATHSPEC_LITERAL,
 		       PATHSPEC_EMPTY_MATCH_ALL,
 		       prefix, argv);
 
@@ -1203,7 +1204,8 @@ int cmd_status(int argc, const char **argv, const char *prefix)
 	handle_untracked_files_arg(&s);
 	if (show_ignored_in_status)
 		s.show_ignored_files = 1;
-	parse_pathspec(&s.pathspec, PATHSPEC_FROMTOP,
+	parse_pathspec(&s.pathspec,
+		       PATHSPEC_FROMTOP | PATHSPEC_LITERAL,
 		       PATHSPEC_EMPTY_MATCH_ALL,
 		       prefix, argv);
 
diff --git a/builtin/diff.c b/builtin/diff.c
index 6b4e3f9..b78435f 100644
--- a/builtin/diff.c
+++ b/builtin/diff.c
@@ -372,7 +372,7 @@ int cmd_diff(int argc, const char **argv, const char *prefix)
 	}
 	if (rev.prune_data.nr) {
 		/* builtin_diff_b_f() */
-		GUARD_PATHSPEC(&rev.prune_data, PATHSPEC_FROMTOP);
+		GUARD_PATHSPEC(&rev.prune_data, PATHSPEC_FROMTOP | PATHSPEC_LITERAL);
 		if (!path)
 			path = rev.prune_data.items[0].match;
 		paths += rev.prune_data.nr;
diff --git a/builtin/grep.c b/builtin/grep.c
index f370bad..4d8e82c 100644
--- a/builtin/grep.c
+++ b/builtin/grep.c
@@ -856,7 +856,9 @@ int cmd_grep(int argc, const char **argv, const char *prefix)
 			verify_filename(prefix, argv[j], j == i);
 	}
 
-	parse_pathspec(&pathspec, PATHSPEC_FROMTOP, 0, prefix, argv + i);
+	parse_pathspec(&pathspec,
+		       PATHSPEC_FROMTOP | PATHSPEC_LITERAL,
+		       0, prefix, argv + i);
 	pathspec.max_depth = opt.max_depth;
 	pathspec.recursive = 1;
 
diff --git a/builtin/ls-files.c b/builtin/ls-files.c
index e3ccf50..1cbd211 100644
--- a/builtin/ls-files.c
+++ b/builtin/ls-files.c
@@ -535,7 +535,8 @@ int cmd_ls_files(int argc, const char **argv, const char *cmd_prefix)
 	if (require_work_tree && !is_inside_work_tree())
 		setup_work_tree();
 
-	parse_pathspec(&pathspec, PATHSPEC_FROMTOP,
+	parse_pathspec(&pathspec,
+		       PATHSPEC_FROMTOP | PATHSPEC_LITERAL,
 		       PATHSPEC_STRIP_SUBMODULE_SLASH_CHEAP,
 		       prefix, argv);
 
diff --git a/builtin/ls-tree.c b/builtin/ls-tree.c
index 58899a5..4764683 100644
--- a/builtin/ls-tree.c
+++ b/builtin/ls-tree.c
@@ -172,7 +172,9 @@ int cmd_ls_tree(int argc, const char **argv, const char *prefix)
 	 * cannot be lifted until it is converted to use
 	 * match_pathspec_depth() or tree_entry_interesting()
 	 */
-	parse_pathspec(&pathspec, PATHSPEC_FROMTOP, 0, prefix, argv + 1);
+	parse_pathspec(&pathspec,
+		       PATHSPEC_FROMTOP | PATHSPEC_LITERAL,
+		       0, prefix, argv + 1);
 	for (i = 0; i < pathspec.nr; i++)
 		pathspec.items[i].nowildcard_len = pathspec.items[i].len;
 	pathspec.has_wildcard = 0;
diff --git a/builtin/rerere.c b/builtin/rerere.c
index a573c4a..779fc77 100644
--- a/builtin/rerere.c
+++ b/builtin/rerere.c
@@ -71,7 +71,9 @@ int cmd_rerere(int argc, const char **argv, const char *prefix)
 		struct pathspec pathspec;
 		if (argc < 2)
 			warning("'git rerere forget' without paths is deprecated");
-		parse_pathspec(&pathspec, PATHSPEC_FROMTOP, 0, prefix, argv + 1);
+		parse_pathspec(&pathspec,
+			       PATHSPEC_FROMTOP | PATHSPEC_LITERAL,
+			       0, prefix, argv + 1);
 		return rerere_forget(&pathspec);
 	}
 
diff --git a/builtin/reset.c b/builtin/reset.c
index 0185bb0..69ce164 100644
--- a/builtin/reset.c
+++ b/builtin/reset.c
@@ -183,7 +183,9 @@ static int read_from_tree(const char *prefix, const char **argv,
 	struct diff_options opt;
 
 	memset(&opt, 0, sizeof(opt));
-	parse_pathspec(&opt.pathspec, PATHSPEC_FROMTOP, 0, prefix, argv);
+	parse_pathspec(&opt.pathspec,
+		       PATHSPEC_FROMTOP | PATHSPEC_LITERAL,
+		       0, prefix, argv);
 	opt.output_format = DIFF_FORMAT_CALLBACK;
 	opt.format_callback = update_index_from_diff;
 	opt.format_callback_data = &index_was_discarded;
diff --git a/builtin/rm.c b/builtin/rm.c
index b2a99c2..d46f1aa 100644
--- a/builtin/rm.c
+++ b/builtin/rm.c
@@ -249,7 +249,9 @@ int cmd_rm(int argc, const char **argv, const char *prefix)
 		}
 	}
 
-	parse_pathspec(&pathspec, PATHSPEC_FROMTOP, 0, prefix, argv);
+	parse_pathspec(&pathspec,
+		       PATHSPEC_FROMTOP | PATHSPEC_LITERAL,
+		       0, prefix, argv);
 	refresh_index(&the_index, REFRESH_QUIET, &pathspec, NULL, NULL);
 
 	seen = NULL;
diff --git a/builtin/update-index.c b/builtin/update-index.c
index 6728e59..1dd6178 100644
--- a/builtin/update-index.c
+++ b/builtin/update-index.c
@@ -548,7 +548,9 @@ static int do_reupdate(int ac, const char **av,
 	int has_head = 1;
 	struct pathspec pathspec;
 
-	parse_pathspec(&pathspec, PATHSPEC_FROMTOP, 0, prefix, av + 1);
+	parse_pathspec(&pathspec,
+		       PATHSPEC_FROMTOP | PATHSPEC_LITERAL,
+		       0, prefix, av + 1);
 
 	if (read_ref("HEAD", head_sha1))
 		/* If there is no HEAD, that means it is an initial
diff --git a/cache.h b/cache.h
index f3de28d..900b81a 100644
--- a/cache.h
+++ b/cache.h
@@ -478,6 +478,7 @@ extern int ie_modified(const struct index_state *, struct cache_entry *, struct
 
 /* Pathspec magic */
 #define PATHSPEC_FROMTOP    (1<<0)
+#define PATHSPEC_LITERAL    (1<<1)
 
 #define PATHSPEC_ONESTAR 1	/* the pathspec pattern sastisfies GFNM_ONESTAR */
 
diff --git a/dir.c b/dir.c
index efc676c..63d07cd 100644
--- a/dir.c
+++ b/dir.c
@@ -64,7 +64,7 @@ static size_t common_prefix_len(const struct pathspec *pathspec)
 	int n;
 	size_t max = 0;
 
-	GUARD_PATHSPEC(pathspec, PATHSPEC_FROMTOP);
+	GUARD_PATHSPEC(pathspec, PATHSPEC_FROMTOP | PATHSPEC_LITERAL);
 
 	for (n = 0; n < pathspec->nr; n++) {
 		size_t i = 0, len = 0;
@@ -181,7 +181,7 @@ int match_pathspec_depth(const struct pathspec *ps,
 {
 	int i, retval = 0;
 
-	GUARD_PATHSPEC(ps, PATHSPEC_FROMTOP);
+	GUARD_PATHSPEC(ps, PATHSPEC_FROMTOP | PATHSPEC_LITERAL);
 
 	if (!ps->nr) {
 		if (!ps->recursive || ps->max_depth == -1)
@@ -1230,7 +1230,7 @@ int read_directory(struct dir_struct *dir, const char *path, int len, const stru
 	 * Check out create_simplify()
 	 */
 	if (pathspec)
-		GUARD_PATHSPEC(pathspec, PATHSPEC_FROMTOP);
+		GUARD_PATHSPEC(pathspec, PATHSPEC_FROMTOP | PATHSPEC_LITERAL);
 
 	if (has_symlink_leading_path(path, len))
 		return dir->nr;
diff --git a/revision.c b/revision.c
index 231f53b..079955f 100644
--- a/revision.c
+++ b/revision.c
@@ -1851,8 +1851,9 @@ int setup_revisions(int argc, const char **argv, struct rev_info *revs, struct s
 		 */
 		ALLOC_GROW(prune_data.path, prune_data.nr+1, prune_data.alloc);
 		prune_data.path[prune_data.nr++] = NULL;
-		parse_pathspec(&revs->prune_data, PATHSPEC_FROMTOP, 0,
-			       revs->prefix, prune_data.path);
+		parse_pathspec(&revs->prune_data,
+			       PATHSPEC_FROMTOP | PATHSPEC_LITERAL,
+			       0, revs->prefix, prune_data.path);
 	}
 
 	if (revs->def == NULL)
diff --git a/setup.c b/setup.c
index c5e97c9..6b48f1b 100644
--- a/setup.c
+++ b/setup.c
@@ -157,7 +157,6 @@ void verify_non_filename(const char *prefix, const char *arg)
  *
  * Possible future magic semantics include stuff like:
  *
- *	{ PATHSPEC_NOGLOB, '!', "noglob" },
  *	{ PATHSPEC_ICASE, '\0', "icase" },
  *	{ PATHSPEC_RECURSIVE, '*', "recursive" },
  *	{ PATHSPEC_REGEXP, '\0', "regexp" },
@@ -170,6 +169,7 @@ static struct pathspec_magic {
 	const char *name;
 } pathspec_magic[] = {
 	{ PATHSPEC_FROMTOP, '/', "top" },
+	{ PATHSPEC_LITERAL,   0, "literal" },
 };
 
 /*
@@ -303,7 +303,7 @@ static unsigned prefix_pathspec(struct pathspec_item *item,
 		}
 
 	item->flags = 0;
-	if (limit_pathspec_to_literal())
+	if (limit_pathspec_to_literal() || (magic & PATHSPEC_LITERAL))
 		item->nowildcard_len = item->len;
 	else {
 		item->nowildcard_len = simple_length(item->match);
@@ -397,7 +397,8 @@ void parse_pathspec(struct pathspec *pathspec,
 const char **get_pathspec(const char *prefix, const char **pathspec)
 {
 	struct pathspec ps;
-	parse_pathspec(&ps, PATHSPEC_FROMTOP, 0, prefix, pathspec);
+	parse_pathspec(&ps, PATHSPEC_FROMTOP | PATHSPEC_LITERAL,
+		       0, prefix, pathspec);
 	return ps._raw;
 }
 
diff --git a/t/t6130-pathspec-noglob.sh b/t/t6130-pathspec-noglob.sh
index 39ef619..49c148e 100755
--- a/t/t6130-pathspec-noglob.sh
+++ b/t/t6130-pathspec-noglob.sh
@@ -47,18 +47,36 @@ test_expect_success 'no-glob option matches literally (vanilla)' '
 	test_cmp expect actual
 '
 
+test_expect_success 'no-glob option matches literally (vanilla)' '
+	echo vanilla >expect &&
+	git log --format=%s -- ":(literal)foo" >actual &&
+	test_cmp expect actual
+'
+
 test_expect_success 'no-glob option matches literally (star)' '
 	echo star >expect &&
 	git --literal-pathspecs log --format=%s -- "f*" >actual &&
 	test_cmp expect actual
 '
 
+test_expect_success 'no-glob option matches literally (star)' '
+	echo star >expect &&
+	git log --format=%s -- ":(literal)f*" >actual &&
+	test_cmp expect actual
+'
+
 test_expect_success 'no-glob option matches literally (bracket)' '
 	echo bracket >expect &&
 	git --literal-pathspecs log --format=%s -- "f[o][o]" >actual &&
 	test_cmp expect actual
 '
 
+test_expect_success 'no-glob option matches literally (bracket)' '
+	echo bracket >expect &&
+	git log --format=%s -- ":(literal)f[o][o]" >actual &&
+	test_cmp expect actual
+'
+
 test_expect_success 'no-glob environment variable works' '
 	echo star >expect &&
 	GIT_LITERAL_PATHSPECS=1 git log --format=%s -- "f*" >actual &&
diff --git a/tree-diff.c b/tree-diff.c
index 718f938..0e2e138 100644
--- a/tree-diff.c
+++ b/tree-diff.c
@@ -203,7 +203,7 @@ static void try_to_follow_renames(struct tree_desc *t1, struct tree_desc *t2, co
 	 * path. Magic that matches more than one path is not
 	 * supported.
 	 */
-	GUARD_PATHSPEC(&opt->pathspec, PATHSPEC_FROMTOP);
+	GUARD_PATHSPEC(&opt->pathspec, PATHSPEC_FROMTOP | PATHSPEC_LITERAL);
 #if 0
 	/*
 	 * We should reject wildcards as well. Unfortunately we
diff --git a/tree-walk.c b/tree-walk.c
index dd03750..d4ed51f 100644
--- a/tree-walk.c
+++ b/tree-walk.c
@@ -635,7 +635,7 @@ enum interesting tree_entry_interesting(const struct name_entry *entry,
 	enum interesting never_interesting = ps->has_wildcard ?
 		entry_not_interesting : all_entries_not_interesting;
 
-	GUARD_PATHSPEC(ps, PATHSPEC_FROMTOP);
+	GUARD_PATHSPEC(ps, PATHSPEC_FROMTOP | PATHSPEC_LITERAL);
 
 	if (!ps->nr) {
 		if (!ps->recursive || ps->max_depth == -1)
-- 
1.8.0.rc2.23.g1fb49df