diff --git a/Documentation/git.txt b/Documentation/git.txt
index 1d797f2..7008b4d 100644
--- a/Documentation/git.txt
+++ b/Documentation/git.txt
@@ -422,6 +422,10 @@ help ...`.
Do not use replacement refs to replace git objects. See
linkgit:git-replace[1] for more information.
+--no-pathspec-glob::
+ Do not treat pathspecs as glob patterns. See the section on
+ the `GIT_PATHSPEC_GLOB` environment variable below.
+
GIT COMMANDS
------------
@@ -790,6 +794,17 @@ for further details.
as a file path and will try to write the trace messages
into it.
+GIT_PATHSPEC_GLOB::
+ Setting this variable to `0` will turn off the globbing features
+ of git's pathspecs. For example, running `GIT_PATHSPEC_GLOB=0 git
+ log -- '*.c'` will search for commits literally touching the
+ path `*.c`, not any paths that the glob `*.c` matches. You might
+ want this if you are feeding literal paths to git (e.g., paths
+ previously given to you by `git ls-tree`, `--raw` diff output,
+ etc). Setting it to `1` enables pathspec globbing (which is also
+ the default).
+
+
Discussion[[Discussion]]
------------------------
diff --git a/cache.h b/cache.h
index 18fdd18..98af77c 100644
--- a/cache.h
+++ b/cache.h
@@ -362,6 +362,7 @@ static inline enum object_type object_type(unsigned int mode)
#define GIT_NOTES_DISPLAY_REF_ENVIRONMENT "GIT_NOTES_DISPLAY_REF"
#define GIT_NOTES_REWRITE_REF_ENVIRONMENT "GIT_NOTES_REWRITE_REF"
#define GIT_NOTES_REWRITE_MODE_ENVIRONMENT "GIT_NOTES_REWRITE_MODE"
+#define GIT_PATHSPEC_GLOB_ENVIRONMENT "GIT_PATHSPEC_GLOB"
/*
* Repository-local GIT_* environment variables
@@ -490,6 +491,8 @@ extern int ce_path_match(const struct cache_entry *ce, const struct pathspec *pa
extern void free_pathspec(struct pathspec *);
extern int ce_path_match(const struct cache_entry *ce, const struct pathspec *pathspec);
+extern int allow_pathspec_glob(void);
+
#define HASH_WRITE_OBJECT 1
#define HASH_FORMAT_CHECK 2
extern int index_fd(unsigned char *sha1, int fd, struct stat *st, enum object_type type, const char *path, unsigned flags);
diff --git a/dir.c b/dir.c
index 5a83aa7..a4d4321 100644
--- a/dir.c
+++ b/dir.c
@@ -38,6 +38,7 @@ static size_t common_prefix_len(const char **pathspec)
{
const char *n, *first;
size_t max = 0;
+ int glob = allow_pathspec_glob();
if (!pathspec)
return max;@@ -47,7 +48,7 @@ static size_t common_prefix_len(const char **pathspec)
size_t i, len = 0;
for (i = 0; first == n || i < max; i++) {
char c = n[i];
- if (!c || c != first[i] || is_glob_special(c))
+ if (!c || c != first[i] || (glob && is_glob_special(c)))
break;
if (c == '/')
len = i + 1;@@ -117,6 +118,7 @@ static int match_one(const char *match, const char *name, int namelen)
static int match_one(const char *match, const char *name, int namelen)
{
int matchlen;
+ int glob = allow_pathspec_glob();
/* If the match was just the prefix, we matched */
if (!*match)@@ -126,7 +128,7 @@ static int match_one(const char *match, const char *name, int namelen)
for (;;) {
unsigned char c1 = tolower(*match);
unsigned char c2 = tolower(*name);
- if (c1 == '\0' || is_glob_special(c1))
+ if (c1 == '\0' || (glob && is_glob_special(c1)))
break;
if (c1 != c2)
return 0;@@ -138,7 +140,7 @@ static int match_one(const char *match, const char *name, int namelen)
for (;;) {
unsigned char c1 = *match;
unsigned char c2 = *name;
- if (c1 == '\0' || is_glob_special(c1))
+ if (c1 == '\0' || (glob && is_glob_special(c1)))
break;
if (c1 != c2)
return 0;@@ -148,14 +150,16 @@ static int match_one(const char *match, const char *name, int namelen)
}
}
-
/*
* If we don't match the matchstring exactly,
* we need to match by fnmatch
*/
matchlen = strlen(match);
- if (strncmp_icase(match, name, matchlen))
+ if (strncmp_icase(match, name, matchlen)) {
+ if (!glob)
+ return 0;
return !fnmatch_icase(match, name, 0) ? MATCHED_FNMATCH : 0;
+ }
if (namelen == matchlen)
return MATCHED_EXACTLY;@@ -1429,7 +1433,7 @@ int init_pathspec(struct pathspec *pathspec, const char **paths)
item->match = path;
item->len = strlen(path);
- item->use_wildcard = !no_wildcard(path);
+ item->use_wildcard = allow_pathspec_glob() && !no_wildcard(path);
if (item->use_wildcard)
pathspec->has_wildcard = 1;
}
@@ -1445,3 +1449,11 @@ void free_pathspec(struct pathspec *pathspec)
free(pathspec->items);
pathspec->items = NULL;
}
+
+int allow_pathspec_glob(void)
+{
+ static int flag = -1;
+ if (flag < 0)
+ flag = git_env_bool(GIT_PATHSPEC_GLOB_ENVIRONMENT, 1);
+ return flag;
+}diff --git a/git.c b/git.c
index d33f9b3..db0496f 100644
--- a/git.c
+++ b/git.c
@@ -135,6 +135,14 @@ static int handle_options(const char ***argv, int *argc, int *envchanged)
git_config_push_parameter((*argv)[1]);
(*argv)++;
(*argc)--;
+ } else if (!strcmp(cmd, "--pathspec-glob")) {
+ setenv(GIT_PATHSPEC_GLOB_ENVIRONMENT, "1", 1);
+ if (envchanged)
+ *envchanged = 1;
+ } else if (!strcmp(cmd, "--no-pathspec-glob")) {
+ setenv(GIT_PATHSPEC_GLOB_ENVIRONMENT, "0", 1);
+ if (envchanged)
+ *envchanged = 1;
} else {
fprintf(stderr, "Unknown option: %s\n", cmd);
usage(git_usage_string);diff --git a/t/t6130-pathspec-noglob.sh b/t/t6130-pathspec-noglob.sh
new file mode 100755
index 0000000..835e730
--- /dev/null
+++ b/t/t6130-pathspec-noglob.sh
@@ -0,0 +1,62 @@
+#!/bin/sh
+
+test_description='test globbing (and noglob) of pathspec limiting'
+. ./test-lib.sh
+
+test_expect_success 'create commits with glob characters' '
+ test_commit unrelated bar &&
+ test_commit vanilla foo &&
+ test_commit star "f*" &&
+ test_commit bracket "f[o][o]"
+'
+
+test_expect_success 'vanilla pathspec matches literally' '
+ echo vanilla >expect &&
+ git log --format=%s -- foo >actual &&
+ test_cmp expect actual
+'
+
+test_expect_success 'star pathspec globs' '
+ cat >expect <<-\EOF &&
+ bracket
+ star
+ vanilla
+ EOF
+ git log --format=%s -- "f*" >actual &&
+ test_cmp expect actual
+'
+
+test_expect_success 'bracket pathspec globs and matches literal brackets' '
+ cat >expect <<-\EOF &&
+ bracket
+ vanilla
+ EOF
+ git log --format=%s -- "f[o][o]" >actual &&
+ test_cmp expect actual
+'
+
+test_expect_success 'no-glob option matches literally (vanilla)' '
+ echo vanilla >expect &&
+ git --no-pathspec-glob log --format=%s -- foo >actual &&
+ test_cmp expect actual
+'
+
+test_expect_success 'no-glob option matches literally (star)' '
+ echo star >expect &&
+ git --no-pathspec-glob log --format=%s -- "f*" >actual &&
+ test_cmp expect actual
+'
+
+test_expect_success 'no-glob option matches literally (bracket)' '
+ echo bracket >expect &&
+ git --no-pathspec-glob log --format=%s -- "f[o][o]" >actual &&
+ test_cmp expect actual
+'
+
+test_expect_success 'no-glob environment variable works' '
+ echo star >expect &&
+ GIT_PATHSPEC_GLOB=0 git log --format=%s -- "f*" >actual &&
+ test_cmp expect actual
+'
+
+test_done
--
1.8.1.rc2.24.gf3bad77