зеркало из https://github.com/microsoft/git.git
Merge branch 'jc/magic-pathspec'
* jc/magic-pathspec: setup.c: Fix some "symbol not declared" sparse warnings t3703: Skip tests using directory name ":" on Windows revision.c: leave a note for "a lone :" enhancement t3703, t4208: add test cases for magic pathspec rev/path disambiguation: further restrict "misspelled index entry" diag fix overslow :/no-such-string-ever-existed diagnostics fix overstrict :<path> diagnosis grep: use get_pathspec() correctly pathspec: drop "lone : means no pathspec" from get_pathspec() Revert "magic pathspec: add ":(icase)path" to match case insensitively" magic pathspec: add ":(icase)path" to match case insensitively magic pathspec: futureproof shorthand form magic pathspec: add tentative ":/path/from/top/level" pathspec support
This commit is contained in:
Коммит
be5ab43566
|
@ -277,7 +277,8 @@ This commit is referred to as a "merge commit", or sometimes just a
|
||||||
Pattern used to specify paths.
|
Pattern used to specify paths.
|
||||||
+
|
+
|
||||||
Pathspecs are used on the command line of "git ls-files", "git
|
Pathspecs are used on the command line of "git ls-files", "git
|
||||||
ls-tree", "git grep", "git checkout", and many other commands to
|
ls-tree", "git add", "git grep", "git diff", "git checkout",
|
||||||
|
and many other commands to
|
||||||
limit the scope of operations to some subset of the tree or
|
limit the scope of operations to some subset of the tree or
|
||||||
worktree. See the documentation of each command for whether
|
worktree. See the documentation of each command for whether
|
||||||
paths are relative to the current directory or toplevel. The
|
paths are relative to the current directory or toplevel. The
|
||||||
|
@ -296,6 +297,37 @@ For example, Documentation/*.jpg will match all .jpg files
|
||||||
in the Documentation subtree,
|
in the Documentation subtree,
|
||||||
including Documentation/chapter_1/figure_1.jpg.
|
including Documentation/chapter_1/figure_1.jpg.
|
||||||
|
|
||||||
|
+
|
||||||
|
A pathspec that begins with a colon `:` has special meaning. In the
|
||||||
|
short form, the leading colon `:` is followed by zero or more "magic
|
||||||
|
signature" letters (which optionally is terminated by another colon `:`),
|
||||||
|
and the remainder is the pattern to match against the path. The optional
|
||||||
|
colon that terminates the "magic signature" can be omitted if the pattern
|
||||||
|
begins with a character that cannot be a "magic signature" and is not a
|
||||||
|
colon.
|
||||||
|
+
|
||||||
|
In the long form, the leading colon `:` is followed by a open
|
||||||
|
parenthesis `(`, a comma-separated list of zero or more "magic words",
|
||||||
|
and a close parentheses `)`, and the remainder is the pattern to match
|
||||||
|
against the path.
|
||||||
|
+
|
||||||
|
The "magic signature" consists of an ASCII symbol that is not
|
||||||
|
alphanumeric.
|
||||||
|
+
|
||||||
|
--
|
||||||
|
top `/`;;
|
||||||
|
The magic word `top` (mnemonic: `/`) makes the pattern match
|
||||||
|
from the root of the working tree, even when you are running
|
||||||
|
the command from inside a subdirectory.
|
||||||
|
--
|
||||||
|
+
|
||||||
|
Currently only the slash `/` is recognized as the "magic signature",
|
||||||
|
but it is envisioned that we will support more types of magic in later
|
||||||
|
versions of git.
|
||||||
|
+
|
||||||
|
A pathspec with only a colon means "there is no pathspec". This form
|
||||||
|
should not be combined with other pathspec.
|
||||||
|
|
||||||
[[def_parent]]parent::
|
[[def_parent]]parent::
|
||||||
A <<def_commit_object,commit object>> contains a (possibly empty) list
|
A <<def_commit_object,commit object>> contains a (possibly empty) list
|
||||||
of the logical predecessor(s) in the line of development, i.e. its
|
of the logical predecessor(s) in the line of development, i.e. its
|
||||||
|
|
|
@ -969,13 +969,7 @@ int cmd_grep(int argc, const char **argv, const char *prefix)
|
||||||
verify_filename(prefix, argv[j]);
|
verify_filename(prefix, argv[j]);
|
||||||
}
|
}
|
||||||
|
|
||||||
if (i < argc)
|
paths = get_pathspec(prefix, argv + i);
|
||||||
paths = get_pathspec(prefix, argv + i);
|
|
||||||
else if (prefix) {
|
|
||||||
paths = xcalloc(2, sizeof(const char *));
|
|
||||||
paths[0] = prefix;
|
|
||||||
paths[1] = NULL;
|
|
||||||
}
|
|
||||||
init_pathspec(&pathspec, paths);
|
init_pathspec(&pathspec, paths);
|
||||||
pathspec.max_depth = opt.max_depth;
|
pathspec.max_depth = opt.max_depth;
|
||||||
pathspec.recursive = 1;
|
pathspec.recursive = 1;
|
||||||
|
|
8
cache.h
8
cache.h
|
@ -810,15 +810,15 @@ struct object_context {
|
||||||
};
|
};
|
||||||
|
|
||||||
extern int get_sha1(const char *str, unsigned char *sha1);
|
extern int get_sha1(const char *str, unsigned char *sha1);
|
||||||
extern int get_sha1_with_mode_1(const char *str, unsigned char *sha1, unsigned *mode, int gently, const char *prefix);
|
extern int get_sha1_with_mode_1(const char *str, unsigned char *sha1, unsigned *mode, int only_to_die, const char *prefix);
|
||||||
static inline int get_sha1_with_mode(const char *str, unsigned char *sha1, unsigned *mode)
|
static inline int get_sha1_with_mode(const char *str, unsigned char *sha1, unsigned *mode)
|
||||||
{
|
{
|
||||||
return get_sha1_with_mode_1(str, sha1, mode, 1, NULL);
|
return get_sha1_with_mode_1(str, sha1, mode, 0, NULL);
|
||||||
}
|
}
|
||||||
extern int get_sha1_with_context_1(const char *name, unsigned char *sha1, struct object_context *orc, int gently, const char *prefix);
|
extern int get_sha1_with_context_1(const char *name, unsigned char *sha1, struct object_context *orc, int only_to_die, const char *prefix);
|
||||||
static inline int get_sha1_with_context(const char *str, unsigned char *sha1, struct object_context *orc)
|
static inline int get_sha1_with_context(const char *str, unsigned char *sha1, struct object_context *orc)
|
||||||
{
|
{
|
||||||
return get_sha1_with_context_1(str, sha1, orc, 1, NULL);
|
return get_sha1_with_context_1(str, sha1, orc, 0, NULL);
|
||||||
}
|
}
|
||||||
extern int get_sha1_hex(const char *hex, unsigned char *sha1);
|
extern int get_sha1_hex(const char *hex, unsigned char *sha1);
|
||||||
extern char *sha1_to_hex(const unsigned char *sha1); /* static buffer result! */
|
extern char *sha1_to_hex(const unsigned char *sha1); /* static buffer result! */
|
||||||
|
|
15
ctype.c
15
ctype.c
|
@ -10,17 +10,18 @@ enum {
|
||||||
A = GIT_ALPHA,
|
A = GIT_ALPHA,
|
||||||
D = GIT_DIGIT,
|
D = GIT_DIGIT,
|
||||||
G = GIT_GLOB_SPECIAL, /* *, ?, [, \\ */
|
G = GIT_GLOB_SPECIAL, /* *, ?, [, \\ */
|
||||||
R = GIT_REGEX_SPECIAL /* $, (, ), +, ., ^, {, | */
|
R = GIT_REGEX_SPECIAL, /* $, (, ), +, ., ^, {, | */
|
||||||
|
P = GIT_PATHSPEC_MAGIC /* other non-alnum, except for ] and } */
|
||||||
};
|
};
|
||||||
|
|
||||||
unsigned char sane_ctype[256] = {
|
unsigned char sane_ctype[256] = {
|
||||||
0, 0, 0, 0, 0, 0, 0, 0, 0, S, S, 0, 0, S, 0, 0, /* 0.. 15 */
|
0, 0, 0, 0, 0, 0, 0, 0, 0, S, S, 0, 0, S, 0, 0, /* 0.. 15 */
|
||||||
0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, /* 16.. 31 */
|
0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, /* 16.. 31 */
|
||||||
S, 0, 0, 0, R, 0, 0, 0, R, R, G, R, 0, 0, R, 0, /* 32.. 47 */
|
S, P, P, P, R, P, P, P, R, R, G, R, P, P, R, P, /* 32.. 47 */
|
||||||
D, D, D, D, D, D, D, D, D, D, 0, 0, 0, 0, 0, G, /* 48.. 63 */
|
D, D, D, D, D, D, D, D, D, D, P, P, P, P, P, G, /* 48.. 63 */
|
||||||
0, A, A, A, A, A, A, A, A, A, A, A, A, A, A, A, /* 64.. 79 */
|
P, A, A, A, A, A, A, A, A, A, A, A, A, A, A, A, /* 64.. 79 */
|
||||||
A, A, A, A, A, A, A, A, A, A, A, G, G, 0, R, 0, /* 80.. 95 */
|
A, A, A, A, A, A, A, A, A, A, A, G, G, 0, R, P, /* 80.. 95 */
|
||||||
0, A, A, A, A, A, A, A, A, A, A, A, A, A, A, A, /* 96..111 */
|
P, A, A, A, A, A, A, A, A, A, A, A, A, A, A, A, /* 96..111 */
|
||||||
A, A, A, A, A, A, A, A, A, A, A, R, R, 0, 0, 0, /* 112..127 */
|
A, A, A, A, A, A, A, A, A, A, A, R, R, 0, P, 0, /* 112..127 */
|
||||||
/* Nothing in the 128.. range */
|
/* Nothing in the 128.. range */
|
||||||
};
|
};
|
||||||
|
|
|
@ -463,6 +463,7 @@ extern unsigned char sane_ctype[256];
|
||||||
#define GIT_ALPHA 0x04
|
#define GIT_ALPHA 0x04
|
||||||
#define GIT_GLOB_SPECIAL 0x08
|
#define GIT_GLOB_SPECIAL 0x08
|
||||||
#define GIT_REGEX_SPECIAL 0x10
|
#define GIT_REGEX_SPECIAL 0x10
|
||||||
|
#define GIT_PATHSPEC_MAGIC 0x20
|
||||||
#define sane_istest(x,mask) ((sane_ctype[(unsigned char)(x)] & (mask)) != 0)
|
#define sane_istest(x,mask) ((sane_ctype[(unsigned char)(x)] & (mask)) != 0)
|
||||||
#define isascii(x) (((x) & ~0x7f) == 0)
|
#define isascii(x) (((x) & ~0x7f) == 0)
|
||||||
#define isspace(x) sane_istest(x,GIT_SPACE)
|
#define isspace(x) sane_istest(x,GIT_SPACE)
|
||||||
|
@ -473,6 +474,7 @@ extern unsigned char sane_ctype[256];
|
||||||
#define is_regex_special(x) sane_istest(x,GIT_GLOB_SPECIAL | GIT_REGEX_SPECIAL)
|
#define is_regex_special(x) sane_istest(x,GIT_GLOB_SPECIAL | GIT_REGEX_SPECIAL)
|
||||||
#define tolower(x) sane_case((unsigned char)(x), 0x20)
|
#define tolower(x) sane_case((unsigned char)(x), 0x20)
|
||||||
#define toupper(x) sane_case((unsigned char)(x), 0)
|
#define toupper(x) sane_case((unsigned char)(x), 0)
|
||||||
|
#define is_pathspec_magic(x) sane_istest(x,GIT_PATHSPEC_MAGIC)
|
||||||
|
|
||||||
static inline int sane_case(int x, int high)
|
static inline int sane_case(int x, int high)
|
||||||
{
|
{
|
||||||
|
|
14
revision.c
14
revision.c
|
@ -1661,6 +1661,20 @@ int setup_revisions(int argc, const char **argv, struct rev_info *revs, struct s
|
||||||
}
|
}
|
||||||
|
|
||||||
if (prune_data.nr) {
|
if (prune_data.nr) {
|
||||||
|
/*
|
||||||
|
* If we need to introduce the magic "a lone ':' means no
|
||||||
|
* pathspec whatsoever", here is the place to do so.
|
||||||
|
*
|
||||||
|
* if (prune_data.nr == 1 && !strcmp(prune_data[0], ":")) {
|
||||||
|
* prune_data.nr = 0;
|
||||||
|
* prune_data.alloc = 0;
|
||||||
|
* free(prune_data.path);
|
||||||
|
* prune_data.path = NULL;
|
||||||
|
* } else {
|
||||||
|
* terminate prune_data.alloc with NULL and
|
||||||
|
* call init_pathspec() to set revs->prune_data here.
|
||||||
|
* }
|
||||||
|
*/
|
||||||
ALLOC_GROW(prune_data.path, prune_data.nr+1, prune_data.alloc);
|
ALLOC_GROW(prune_data.path, prune_data.nr+1, prune_data.alloc);
|
||||||
prune_data.path[prune_data.nr++] = NULL;
|
prune_data.path[prune_data.nr++] = NULL;
|
||||||
init_pathspec(&revs->prune_data,
|
init_pathspec(&revs->prune_data,
|
||||||
|
|
115
setup.c
115
setup.c
|
@ -85,8 +85,17 @@ static void NORETURN die_verify_filename(const char *prefix, const char *arg)
|
||||||
{
|
{
|
||||||
unsigned char sha1[20];
|
unsigned char sha1[20];
|
||||||
unsigned mode;
|
unsigned mode;
|
||||||
/* try a detailed diagnostic ... */
|
|
||||||
get_sha1_with_mode_1(arg, sha1, &mode, 0, prefix);
|
/*
|
||||||
|
* Saying "'(icase)foo' does not exist in the index" when the
|
||||||
|
* user gave us ":(icase)foo" is just stupid. A magic pathspec
|
||||||
|
* begins with a colon and is followed by a non-alnum; do not
|
||||||
|
* let get_sha1_with_mode_1(only_to_die=1) to even trigger.
|
||||||
|
*/
|
||||||
|
if (!(arg[0] == ':' && !isalnum(arg[1])))
|
||||||
|
/* try a detailed diagnostic ... */
|
||||||
|
get_sha1_with_mode_1(arg, sha1, &mode, 1, prefix);
|
||||||
|
|
||||||
/* ... or fall back the most general message. */
|
/* ... or fall back the most general message. */
|
||||||
die("ambiguous argument '%s': unknown revision or path not in the working tree.\n"
|
die("ambiguous argument '%s': unknown revision or path not in the working tree.\n"
|
||||||
"Use '--' to separate paths from revisions", arg);
|
"Use '--' to separate paths from revisions", arg);
|
||||||
|
@ -126,6 +135,105 @@ void verify_non_filename(const char *prefix, const char *arg)
|
||||||
"Use '--' to separate filenames from revisions", arg);
|
"Use '--' to separate filenames from revisions", arg);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/*
|
||||||
|
* Magic pathspec
|
||||||
|
*
|
||||||
|
* NEEDSWORK: These need to be moved to dir.h or even to a new
|
||||||
|
* pathspec.h when we restructure get_pathspec() users to use the
|
||||||
|
* "struct pathspec" interface.
|
||||||
|
*
|
||||||
|
* Possible future magic semantics include stuff like:
|
||||||
|
*
|
||||||
|
* { PATHSPEC_NOGLOB, '!', "noglob" },
|
||||||
|
* { PATHSPEC_ICASE, '\0', "icase" },
|
||||||
|
* { PATHSPEC_RECURSIVE, '*', "recursive" },
|
||||||
|
* { PATHSPEC_REGEXP, '\0', "regexp" },
|
||||||
|
*
|
||||||
|
*/
|
||||||
|
#define PATHSPEC_FROMTOP (1<<0)
|
||||||
|
|
||||||
|
static struct pathspec_magic {
|
||||||
|
unsigned bit;
|
||||||
|
char mnemonic; /* this cannot be ':'! */
|
||||||
|
const char *name;
|
||||||
|
} pathspec_magic[] = {
|
||||||
|
{ PATHSPEC_FROMTOP, '/', "top" },
|
||||||
|
};
|
||||||
|
|
||||||
|
/*
|
||||||
|
* Take an element of a pathspec and check for magic signatures.
|
||||||
|
* Append the result to the prefix.
|
||||||
|
*
|
||||||
|
* For now, we only parse the syntax and throw out anything other than
|
||||||
|
* "top" magic.
|
||||||
|
*
|
||||||
|
* NEEDSWORK: This needs to be rewritten when we start migrating
|
||||||
|
* get_pathspec() users to use the "struct pathspec" interface. For
|
||||||
|
* example, a pathspec element may be marked as case-insensitive, but
|
||||||
|
* the prefix part must always match literally, and a single stupid
|
||||||
|
* string cannot express such a case.
|
||||||
|
*/
|
||||||
|
static const char *prefix_pathspec(const char *prefix, int prefixlen, const char *elt)
|
||||||
|
{
|
||||||
|
unsigned magic = 0;
|
||||||
|
const char *copyfrom = elt;
|
||||||
|
int i;
|
||||||
|
|
||||||
|
if (elt[0] != ':') {
|
||||||
|
; /* nothing to do */
|
||||||
|
} else if (elt[1] == '(') {
|
||||||
|
/* longhand */
|
||||||
|
const char *nextat;
|
||||||
|
for (copyfrom = elt + 2;
|
||||||
|
*copyfrom && *copyfrom != ')';
|
||||||
|
copyfrom = nextat) {
|
||||||
|
size_t len = strcspn(copyfrom, ",)");
|
||||||
|
if (copyfrom[len] == ')')
|
||||||
|
nextat = copyfrom + len;
|
||||||
|
else
|
||||||
|
nextat = copyfrom + len + 1;
|
||||||
|
if (!len)
|
||||||
|
continue;
|
||||||
|
for (i = 0; i < ARRAY_SIZE(pathspec_magic); i++)
|
||||||
|
if (strlen(pathspec_magic[i].name) == len &&
|
||||||
|
!strncmp(pathspec_magic[i].name, copyfrom, len)) {
|
||||||
|
magic |= pathspec_magic[i].bit;
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
if (ARRAY_SIZE(pathspec_magic) <= i)
|
||||||
|
die("Invalid pathspec magic '%.*s' in '%s'",
|
||||||
|
(int) len, copyfrom, elt);
|
||||||
|
}
|
||||||
|
if (*copyfrom == ')')
|
||||||
|
copyfrom++;
|
||||||
|
} else {
|
||||||
|
/* shorthand */
|
||||||
|
for (copyfrom = elt + 1;
|
||||||
|
*copyfrom && *copyfrom != ':';
|
||||||
|
copyfrom++) {
|
||||||
|
char ch = *copyfrom;
|
||||||
|
|
||||||
|
if (!is_pathspec_magic(ch))
|
||||||
|
break;
|
||||||
|
for (i = 0; i < ARRAY_SIZE(pathspec_magic); i++)
|
||||||
|
if (pathspec_magic[i].mnemonic == ch) {
|
||||||
|
magic |= pathspec_magic[i].bit;
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
if (ARRAY_SIZE(pathspec_magic) <= i)
|
||||||
|
die("Unimplemented pathspec magic '%c' in '%s'",
|
||||||
|
ch, elt);
|
||||||
|
}
|
||||||
|
if (*copyfrom == ':')
|
||||||
|
copyfrom++;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (magic & PATHSPEC_FROMTOP)
|
||||||
|
return xstrdup(copyfrom);
|
||||||
|
else
|
||||||
|
return prefix_path(prefix, prefixlen, copyfrom);
|
||||||
|
}
|
||||||
|
|
||||||
const char **get_pathspec(const char *prefix, const char **pathspec)
|
const char **get_pathspec(const char *prefix, const char **pathspec)
|
||||||
{
|
{
|
||||||
const char *entry = *pathspec;
|
const char *entry = *pathspec;
|
||||||
|
@ -147,8 +255,7 @@ const char **get_pathspec(const char *prefix, const char **pathspec)
|
||||||
dst = pathspec;
|
dst = pathspec;
|
||||||
prefixlen = prefix ? strlen(prefix) : 0;
|
prefixlen = prefix ? strlen(prefix) : 0;
|
||||||
while (*src) {
|
while (*src) {
|
||||||
const char *p = prefix_path(prefix, prefixlen, *src);
|
*(dst++) = prefix_pathspec(prefix, prefixlen, *src);
|
||||||
*(dst++) = p;
|
|
||||||
src++;
|
src++;
|
||||||
}
|
}
|
||||||
*dst = NULL;
|
*dst = NULL;
|
||||||
|
|
17
sha1_name.c
17
sha1_name.c
|
@ -1083,11 +1083,12 @@ static void diagnose_invalid_index_path(int stage,
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
int get_sha1_with_mode_1(const char *name, unsigned char *sha1, unsigned *mode, int gently, const char *prefix)
|
int get_sha1_with_mode_1(const char *name, unsigned char *sha1, unsigned *mode,
|
||||||
|
int only_to_die, const char *prefix)
|
||||||
{
|
{
|
||||||
struct object_context oc;
|
struct object_context oc;
|
||||||
int ret;
|
int ret;
|
||||||
ret = get_sha1_with_context_1(name, sha1, &oc, gently, prefix);
|
ret = get_sha1_with_context_1(name, sha1, &oc, only_to_die, prefix);
|
||||||
*mode = oc.mode;
|
*mode = oc.mode;
|
||||||
return ret;
|
return ret;
|
||||||
}
|
}
|
||||||
|
@ -1111,7 +1112,7 @@ static char *resolve_relative_path(const char *rel)
|
||||||
|
|
||||||
int get_sha1_with_context_1(const char *name, unsigned char *sha1,
|
int get_sha1_with_context_1(const char *name, unsigned char *sha1,
|
||||||
struct object_context *oc,
|
struct object_context *oc,
|
||||||
int gently, const char *prefix)
|
int only_to_die, const char *prefix)
|
||||||
{
|
{
|
||||||
int ret, bracket_depth;
|
int ret, bracket_depth;
|
||||||
int namelen = strlen(name);
|
int namelen = strlen(name);
|
||||||
|
@ -1133,7 +1134,7 @@ int get_sha1_with_context_1(const char *name, unsigned char *sha1,
|
||||||
struct cache_entry *ce;
|
struct cache_entry *ce;
|
||||||
char *new_path = NULL;
|
char *new_path = NULL;
|
||||||
int pos;
|
int pos;
|
||||||
if (namelen > 2 && name[1] == '/') {
|
if (!only_to_die && namelen > 2 && name[1] == '/') {
|
||||||
struct commit_list *list = NULL;
|
struct commit_list *list = NULL;
|
||||||
for_each_ref(handle_one_ref, &list);
|
for_each_ref(handle_one_ref, &list);
|
||||||
return get_sha1_oneline(name + 2, sha1, list);
|
return get_sha1_oneline(name + 2, sha1, list);
|
||||||
|
@ -1176,7 +1177,7 @@ int get_sha1_with_context_1(const char *name, unsigned char *sha1,
|
||||||
}
|
}
|
||||||
pos++;
|
pos++;
|
||||||
}
|
}
|
||||||
if (!gently)
|
if (only_to_die && name[1] && name[1] != '/')
|
||||||
diagnose_invalid_index_path(stage, prefix, cp);
|
diagnose_invalid_index_path(stage, prefix, cp);
|
||||||
free(new_path);
|
free(new_path);
|
||||||
return -1;
|
return -1;
|
||||||
|
@ -1192,7 +1193,7 @@ int get_sha1_with_context_1(const char *name, unsigned char *sha1,
|
||||||
if (*cp == ':') {
|
if (*cp == ':') {
|
||||||
unsigned char tree_sha1[20];
|
unsigned char tree_sha1[20];
|
||||||
char *object_name = NULL;
|
char *object_name = NULL;
|
||||||
if (!gently) {
|
if (only_to_die) {
|
||||||
object_name = xmalloc(cp-name+1);
|
object_name = xmalloc(cp-name+1);
|
||||||
strncpy(object_name, name, cp-name);
|
strncpy(object_name, name, cp-name);
|
||||||
object_name[cp-name] = '\0';
|
object_name[cp-name] = '\0';
|
||||||
|
@ -1205,7 +1206,7 @@ int get_sha1_with_context_1(const char *name, unsigned char *sha1,
|
||||||
if (new_filename)
|
if (new_filename)
|
||||||
filename = new_filename;
|
filename = new_filename;
|
||||||
ret = get_tree_entry(tree_sha1, filename, sha1, &oc->mode);
|
ret = get_tree_entry(tree_sha1, filename, sha1, &oc->mode);
|
||||||
if (!gently) {
|
if (only_to_die) {
|
||||||
diagnose_invalid_sha1_path(prefix, filename,
|
diagnose_invalid_sha1_path(prefix, filename,
|
||||||
tree_sha1, object_name);
|
tree_sha1, object_name);
|
||||||
free(object_name);
|
free(object_name);
|
||||||
|
@ -1218,7 +1219,7 @@ int get_sha1_with_context_1(const char *name, unsigned char *sha1,
|
||||||
free(new_filename);
|
free(new_filename);
|
||||||
return ret;
|
return ret;
|
||||||
} else {
|
} else {
|
||||||
if (!gently)
|
if (only_to_die)
|
||||||
die("Invalid object name '%s'.", object_name);
|
die("Invalid object name '%s'.", object_name);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -0,0 +1,58 @@
|
||||||
|
#!/bin/sh
|
||||||
|
|
||||||
|
test_description='magic pathspec tests using git-add'
|
||||||
|
|
||||||
|
. ./test-lib.sh
|
||||||
|
|
||||||
|
test_expect_success 'setup' '
|
||||||
|
mkdir sub anothersub &&
|
||||||
|
: >sub/foo &&
|
||||||
|
: >anothersub/foo
|
||||||
|
'
|
||||||
|
|
||||||
|
test_expect_success 'add :/' "
|
||||||
|
cat >expected <<-EOF &&
|
||||||
|
add 'anothersub/foo'
|
||||||
|
add 'expected'
|
||||||
|
add 'sub/actual'
|
||||||
|
add 'sub/foo'
|
||||||
|
EOF
|
||||||
|
(cd sub && git add -n :/ >actual) &&
|
||||||
|
test_cmp expected sub/actual
|
||||||
|
"
|
||||||
|
|
||||||
|
cat >expected <<EOF
|
||||||
|
add 'anothersub/foo'
|
||||||
|
EOF
|
||||||
|
|
||||||
|
test_expect_success 'add :/anothersub' '
|
||||||
|
(cd sub && git add -n :/anothersub >actual) &&
|
||||||
|
test_cmp expected sub/actual
|
||||||
|
'
|
||||||
|
|
||||||
|
test_expect_success 'add :/non-existent' '
|
||||||
|
(cd sub && test_must_fail git add -n :/non-existent)
|
||||||
|
'
|
||||||
|
|
||||||
|
cat >expected <<EOF
|
||||||
|
add 'sub/foo'
|
||||||
|
EOF
|
||||||
|
|
||||||
|
test_expect_success 'a file with the same (long) magic name exists' '
|
||||||
|
: >":(icase)ha" &&
|
||||||
|
test_must_fail git add -n ":(icase)ha" &&
|
||||||
|
git add -n "./:(icase)ha"
|
||||||
|
'
|
||||||
|
|
||||||
|
if mkdir ":" 2>/dev/null
|
||||||
|
then
|
||||||
|
test_set_prereq COLON_DIR
|
||||||
|
fi
|
||||||
|
|
||||||
|
test_expect_success COLON_DIR 'a file with the same (short) magic name exists' '
|
||||||
|
: >":/bar" &&
|
||||||
|
test_must_fail git add -n :/bar &&
|
||||||
|
git add -n "./:/bar"
|
||||||
|
'
|
||||||
|
|
||||||
|
test_done
|
|
@ -0,0 +1,36 @@
|
||||||
|
#!/bin/sh
|
||||||
|
|
||||||
|
test_description='magic pathspec tests using git-log'
|
||||||
|
|
||||||
|
. ./test-lib.sh
|
||||||
|
|
||||||
|
test_expect_success 'setup' '
|
||||||
|
test_commit initial &&
|
||||||
|
test_tick &&
|
||||||
|
git commit --allow-empty -m empty &&
|
||||||
|
mkdir sub
|
||||||
|
'
|
||||||
|
|
||||||
|
test_expect_success '"git log :/" should be ambiguous' '
|
||||||
|
test_must_fail git log :/ 2>error &&
|
||||||
|
grep ambiguous error
|
||||||
|
'
|
||||||
|
|
||||||
|
test_expect_success '"git log :" should be ambiguous' '
|
||||||
|
test_must_fail git log : 2>error &&
|
||||||
|
grep ambiguous error
|
||||||
|
'
|
||||||
|
|
||||||
|
test_expect_success 'git log -- :' '
|
||||||
|
git log -- :
|
||||||
|
'
|
||||||
|
|
||||||
|
test_expect_success 'git log HEAD -- :/' '
|
||||||
|
cat >expected <<-EOF &&
|
||||||
|
24b24cf initial
|
||||||
|
EOF
|
||||||
|
(cd sub && git log --oneline HEAD -- :/ >../actual) &&
|
||||||
|
test_cmp expected actual
|
||||||
|
'
|
||||||
|
|
||||||
|
test_done
|
Загрузка…
Ссылка в новой задаче