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:
@ -428,6 +428,11 @@ help ...`.
|
|||||||
Do not use replacement refs to replace git objects. See
|
Do not use replacement refs to replace git objects. See
|
||||||
linkgit:git-replace[1] for more information.
|
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
|
GIT COMMANDS
|
||||||
------------
|
------------
|
||||||
@ -796,6 +801,16 @@ for further details.
|
|||||||
as a file path and will try to write the trace messages
|
as a file path and will try to write the trace messages
|
||||||
into it.
|
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]]
|
Discussion[[Discussion]]
|
||||||
------------------------
|
------------------------
|
||||||
|
|
||||||
|
3
cache.h
3
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_DISPLAY_REF_ENVIRONMENT "GIT_NOTES_DISPLAY_REF"
|
||||||
#define GIT_NOTES_REWRITE_REF_ENVIRONMENT "GIT_NOTES_REWRITE_REF"
|
#define GIT_NOTES_REWRITE_REF_ENVIRONMENT "GIT_NOTES_REWRITE_REF"
|
||||||
#define GIT_NOTES_REWRITE_MODE_ENVIRONMENT "GIT_NOTES_REWRITE_MODE"
|
#define GIT_NOTES_REWRITE_MODE_ENVIRONMENT "GIT_NOTES_REWRITE_MODE"
|
||||||
|
#define GIT_LITERAL_PATHSPECS_ENVIRONMENT "GIT_LITERAL_PATHSPECS"
|
||||||
|
|
||||||
/*
|
/*
|
||||||
* Repository-local GIT_* environment variables
|
* Repository-local GIT_* environment variables
|
||||||
@ -493,6 +494,8 @@ extern int init_pathspec(struct pathspec *, const char **);
|
|||||||
extern void free_pathspec(struct pathspec *);
|
extern void free_pathspec(struct pathspec *);
|
||||||
extern int ce_path_match(const struct cache_entry *ce, const struct pathspec *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_WRITE_OBJECT 1
|
||||||
#define HASH_FORMAT_CHECK 2
|
#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);
|
extern int index_fd(unsigned char *sha1, int fd, struct stat *st, enum object_type type, const char *path, unsigned flags);
|
||||||
|
38
dir.c
38
dir.c
@ -60,6 +60,7 @@ static size_t common_prefix_len(const char **pathspec)
|
|||||||
{
|
{
|
||||||
const char *n, *first;
|
const char *n, *first;
|
||||||
size_t max = 0;
|
size_t max = 0;
|
||||||
|
int literal = limit_pathspec_to_literal();
|
||||||
|
|
||||||
if (!pathspec)
|
if (!pathspec)
|
||||||
return max;
|
return max;
|
||||||
@ -69,7 +70,7 @@ static size_t common_prefix_len(const char **pathspec)
|
|||||||
size_t i, len = 0;
|
size_t i, len = 0;
|
||||||
for (i = 0; first == n || i < max; i++) {
|
for (i = 0; first == n || i < max; i++) {
|
||||||
char c = n[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;
|
break;
|
||||||
if (c == '/')
|
if (c == '/')
|
||||||
len = i + 1;
|
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)
|
static int match_one(const char *match, const char *name, int namelen)
|
||||||
{
|
{
|
||||||
int matchlen;
|
int matchlen;
|
||||||
|
int literal = limit_pathspec_to_literal();
|
||||||
|
|
||||||
/* If the match was just the prefix, we matched */
|
/* If the match was just the prefix, we matched */
|
||||||
if (!*match)
|
if (!*match)
|
||||||
@ -148,7 +150,7 @@ static int match_one(const char *match, const char *name, int namelen)
|
|||||||
for (;;) {
|
for (;;) {
|
||||||
unsigned char c1 = tolower(*match);
|
unsigned char c1 = tolower(*match);
|
||||||
unsigned char c2 = tolower(*name);
|
unsigned char c2 = tolower(*name);
|
||||||
if (c1 == '\0' || is_glob_special(c1))
|
if (c1 == '\0' || (!literal && is_glob_special(c1)))
|
||||||
break;
|
break;
|
||||||
if (c1 != c2)
|
if (c1 != c2)
|
||||||
return 0;
|
return 0;
|
||||||
@ -160,7 +162,7 @@ static int match_one(const char *match, const char *name, int namelen)
|
|||||||
for (;;) {
|
for (;;) {
|
||||||
unsigned char c1 = *match;
|
unsigned char c1 = *match;
|
||||||
unsigned char c2 = *name;
|
unsigned char c2 = *name;
|
||||||
if (c1 == '\0' || is_glob_special(c1))
|
if (c1 == '\0' || (!literal && is_glob_special(c1)))
|
||||||
break;
|
break;
|
||||||
if (c1 != c2)
|
if (c1 != c2)
|
||||||
return 0;
|
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,
|
* If we don't match the matchstring exactly,
|
||||||
* we need to match by fnmatch
|
* we need to match by fnmatch
|
||||||
*/
|
*/
|
||||||
matchlen = strlen(match);
|
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;
|
return !fnmatch_icase(match, name, 0) ? MATCHED_FNMATCH : 0;
|
||||||
|
}
|
||||||
|
|
||||||
if (namelen == matchlen)
|
if (namelen == matchlen)
|
||||||
return MATCHED_EXACTLY;
|
return MATCHED_EXACTLY;
|
||||||
@ -1454,13 +1458,17 @@ int init_pathspec(struct pathspec *pathspec, const char **paths)
|
|||||||
|
|
||||||
item->match = path;
|
item->match = path;
|
||||||
item->len = strlen(path);
|
item->len = strlen(path);
|
||||||
item->nowildcard_len = simple_length(path);
|
|
||||||
item->flags = 0;
|
item->flags = 0;
|
||||||
if (item->nowildcard_len < item->len) {
|
if (limit_pathspec_to_literal()) {
|
||||||
pathspec->has_wildcard = 1;
|
item->nowildcard_len = item->len;
|
||||||
if (path[item->nowildcard_len] == '*' &&
|
} else {
|
||||||
no_wildcard(path + item->nowildcard_len + 1))
|
item->nowildcard_len = simple_length(path);
|
||||||
item->flags |= PATHSPEC_ONESTAR;
|
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);
|
free(pathspec->items);
|
||||||
pathspec->items = NULL;
|
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
8
git.c
@ -135,6 +135,14 @@ static int handle_options(const char ***argv, int *argc, int *envchanged)
|
|||||||
git_config_push_parameter((*argv)[1]);
|
git_config_push_parameter((*argv)[1]);
|
||||||
(*argv)++;
|
(*argv)++;
|
||||||
(*argc)--;
|
(*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 {
|
} else {
|
||||||
fprintf(stderr, "Unknown option: %s\n", cmd);
|
fprintf(stderr, "Unknown option: %s\n", cmd);
|
||||||
usage(git_usage_string);
|
usage(git_usage_string);
|
||||||
|
62
t/t6130-pathspec-noglob.sh
Executable file
62
t/t6130-pathspec-noglob.sh
Executable 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
|
Reference in New Issue
Block a user