Merge branch 'jk/pathspec-literal'

Allow scripts to feed literal paths to commands that take
pathspecs, by disabling wildcard globbing.

* jk/pathspec-literal:
  add global --literal-pathspecs option

Conflicts:
	dir.c
This commit is contained in:
Junio C Hamano 2013-01-05 23:42:07 -08:00
commit 971e829cd8
5 changed files with 115 additions and 11 deletions

View File

@ -428,6 +428,11 @@ help ...`.
Do not use replacement refs to replace git objects. See
linkgit:git-replace[1] for more information.
--literal-pathspecs::
Treat pathspecs literally, rather than as glob patterns. This is
equivalent to setting the `GIT_LITERAL_PATHSPECS` environment
variable to `1`.
GIT COMMANDS
------------
@ -796,6 +801,16 @@ for further details.
as a file path and will try to write the trace messages
into it.
GIT_LITERAL_PATHSPECS::
Setting this variable to `1` will cause git to treat all
pathspecs literally, rather than as glob patterns. For example,
running `GIT_LITERAL_PATHSPECS=1 git log -- '*.c'` will search
for commits that touch 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).
Discussion[[Discussion]]
------------------------

View File

@ -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_LITERAL_PATHSPECS_ENVIRONMENT "GIT_LITERAL_PATHSPECS"
/*
* Repository-local GIT_* environment variables
@ -493,6 +494,8 @@ extern int init_pathspec(struct pathspec *, const char **);
extern void free_pathspec(struct pathspec *);
extern int ce_path_match(const struct cache_entry *ce, const struct pathspec *pathspec);
extern int limit_pathspec_to_literal(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);

38
dir.c
View File

@ -60,6 +60,7 @@ static size_t common_prefix_len(const char **pathspec)
{
const char *n, *first;
size_t max = 0;
int literal = limit_pathspec_to_literal();
if (!pathspec)
return max;
@ -69,7 +70,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] || (!literal && is_glob_special(c)))
break;
if (c == '/')
len = i + 1;
@ -139,6 +140,7 @@ int within_depth(const char *name, int namelen,
static int match_one(const char *match, const char *name, int namelen)
{
int matchlen;
int literal = limit_pathspec_to_literal();
/* If the match was just the prefix, we matched */
if (!*match)
@ -148,7 +150,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' || (!literal && is_glob_special(c1)))
break;
if (c1 != c2)
return 0;
@ -160,7 +162,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' || (!literal && is_glob_special(c1)))
break;
if (c1 != c2)
return 0;
@ -170,14 +172,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 (literal)
return 0;
return !fnmatch_icase(match, name, 0) ? MATCHED_FNMATCH : 0;
}
if (namelen == matchlen)
return MATCHED_EXACTLY;
@ -1454,13 +1458,17 @@ int init_pathspec(struct pathspec *pathspec, const char **paths)
item->match = path;
item->len = strlen(path);
item->nowildcard_len = simple_length(path);
item->flags = 0;
if (item->nowildcard_len < item->len) {
pathspec->has_wildcard = 1;
if (path[item->nowildcard_len] == '*' &&
no_wildcard(path + item->nowildcard_len + 1))
item->flags |= PATHSPEC_ONESTAR;
if (limit_pathspec_to_literal()) {
item->nowildcard_len = item->len;
} else {
item->nowildcard_len = simple_length(path);
if (item->nowildcard_len < item->len) {
pathspec->has_wildcard = 1;
if (path[item->nowildcard_len] == '*' &&
no_wildcard(path + item->nowildcard_len + 1))
item->flags |= PATHSPEC_ONESTAR;
}
}
}
@ -1475,3 +1483,11 @@ void free_pathspec(struct pathspec *pathspec)
free(pathspec->items);
pathspec->items = NULL;
}
int limit_pathspec_to_literal(void)
{
static int flag = -1;
if (flag < 0)
flag = git_env_bool(GIT_LITERAL_PATHSPECS_ENVIRONMENT, 0);
return flag;
}

8
git.c
View File

@ -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, "--literal-pathspecs")) {
setenv(GIT_LITERAL_PATHSPECS_ENVIRONMENT, "1", 1);
if (envchanged)
*envchanged = 1;
} else if (!strcmp(cmd, "--no-literal-pathspecs")) {
setenv(GIT_LITERAL_PATHSPECS_ENVIRONMENT, "0", 1);
if (envchanged)
*envchanged = 1;
} else {
fprintf(stderr, "Unknown option: %s\n", cmd);
usage(git_usage_string);

62
t/t6130-pathspec-noglob.sh Executable file
View File

@ -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 --literal-pathspecs log --format=%s -- 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 (bracket)' '
echo bracket >expect &&
git --literal-pathspecs log --format=%s -- "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 &&
test_cmp expect actual
'
test_done