mirror of
https://github.com/git/git.git
synced 2026-03-15 03:00:07 +01:00
Merge branch 'jc/magic-pathspec' into next
* jc/magic-pathspec: 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"
This commit is contained in:
@@ -319,14 +319,14 @@ 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.
|
||||
icase;;
|
||||
The magic word `icase` (there is no mnemonic for it) makes the
|
||||
pattern match case insensitively. E.g. `:(icase)makefile` matches
|
||||
both `Makefile` and `makefile`.
|
||||
--
|
||||
+
|
||||
It is envisioned that we will support more types of magic in later
|
||||
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::
|
||||
A <<def_commit_object,commit object>> contains a (possibly empty) list
|
||||
|
||||
@@ -969,13 +969,7 @@ int cmd_grep(int argc, const char **argv, const char *prefix)
|
||||
verify_filename(prefix, argv[j]);
|
||||
}
|
||||
|
||||
if (i < argc)
|
||||
paths = get_pathspec(prefix, argv + i);
|
||||
else if (prefix) {
|
||||
paths = xcalloc(2, sizeof(const char *));
|
||||
paths[0] = prefix;
|
||||
paths[1] = NULL;
|
||||
}
|
||||
paths = get_pathspec(prefix, argv + i);
|
||||
init_pathspec(&pathspec, paths);
|
||||
pathspec.max_depth = opt.max_depth;
|
||||
pathspec.recursive = 1;
|
||||
|
||||
8
cache.h
8
cache.h
@@ -800,15 +800,15 @@ struct object_context {
|
||||
};
|
||||
|
||||
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)
|
||||
{
|
||||
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)
|
||||
{
|
||||
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 char *sha1_to_hex(const unsigned char *sha1); /* static buffer result! */
|
||||
|
||||
47
setup.c
47
setup.c
@@ -85,8 +85,17 @@ static void NORETURN die_verify_filename(const char *prefix, const char *arg)
|
||||
{
|
||||
unsigned char sha1[20];
|
||||
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. */
|
||||
die("ambiguous argument '%s': unknown revision or path not in the working tree.\n"
|
||||
"Use '--' to separate paths from revisions", arg);
|
||||
@@ -136,12 +145,12 @@ 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" },
|
||||
*
|
||||
*/
|
||||
#define PATHSPEC_FROMTOP (1<<0)
|
||||
#define PATHSPEC_ICASE (1<<1)
|
||||
|
||||
struct pathspec_magic {
|
||||
unsigned bit;
|
||||
@@ -149,7 +158,6 @@ struct pathspec_magic {
|
||||
const char *name;
|
||||
} pathspec_magic[] = {
|
||||
{ PATHSPEC_FROMTOP, '/', "top" },
|
||||
{ PATHSPEC_ICASE, '\0', "icase" },
|
||||
};
|
||||
|
||||
/*
|
||||
@@ -169,8 +177,7 @@ const char *prefix_pathspec(const char *prefix, int prefixlen, const char *elt)
|
||||
{
|
||||
unsigned magic = 0;
|
||||
const char *copyfrom = elt;
|
||||
const char *retval;
|
||||
int i, free_source = 0;
|
||||
int i;
|
||||
|
||||
if (elt[0] != ':') {
|
||||
; /* nothing to do */
|
||||
@@ -199,9 +206,6 @@ const char *prefix_pathspec(const char *prefix, int prefixlen, const char *elt)
|
||||
}
|
||||
if (*copyfrom == ')')
|
||||
copyfrom++;
|
||||
} else if (!elt[1]) {
|
||||
/* Just ':' -- no element! */
|
||||
return NULL;
|
||||
} else {
|
||||
/* shorthand */
|
||||
for (copyfrom = elt + 1;
|
||||
@@ -224,31 +228,10 @@ const char *prefix_pathspec(const char *prefix, int prefixlen, const char *elt)
|
||||
copyfrom++;
|
||||
}
|
||||
|
||||
if (magic & PATHSPEC_ICASE) {
|
||||
struct strbuf sb = STRBUF_INIT;
|
||||
for (i = 0; copyfrom[i]; i++) {
|
||||
int ch = copyfrom[i];
|
||||
if (('a' <= ch && ch <= 'z') ||
|
||||
('A' <= ch && ch <= 'Z')) {
|
||||
strbuf_addf(&sb, "[%c%c]",
|
||||
tolower(ch), toupper(ch));
|
||||
} else {
|
||||
strbuf_addch(&sb, ch);
|
||||
}
|
||||
}
|
||||
if (sb.len) {
|
||||
free_source = 1;
|
||||
copyfrom = strbuf_detach(&sb, NULL);
|
||||
}
|
||||
}
|
||||
|
||||
if (magic & PATHSPEC_FROMTOP)
|
||||
retval = xstrdup(copyfrom);
|
||||
return xstrdup(copyfrom);
|
||||
else
|
||||
retval = prefix_path(prefix, prefixlen, copyfrom);
|
||||
if (free_source)
|
||||
free((char *)copyfrom);
|
||||
return retval;
|
||||
return prefix_path(prefix, prefixlen, copyfrom);
|
||||
}
|
||||
|
||||
const char **get_pathspec(const char *prefix, const char **pathspec)
|
||||
|
||||
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;
|
||||
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;
|
||||
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,
|
||||
struct object_context *oc,
|
||||
int gently, const char *prefix)
|
||||
int only_to_die, const char *prefix)
|
||||
{
|
||||
int ret, bracket_depth;
|
||||
int namelen = strlen(name);
|
||||
@@ -1133,7 +1134,7 @@ int get_sha1_with_context_1(const char *name, unsigned char *sha1,
|
||||
struct cache_entry *ce;
|
||||
char *new_path = NULL;
|
||||
int pos;
|
||||
if (namelen > 2 && name[1] == '/') {
|
||||
if (!only_to_die && namelen > 2 && name[1] == '/') {
|
||||
struct commit_list *list = NULL;
|
||||
for_each_ref(handle_one_ref, &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++;
|
||||
}
|
||||
if (!gently)
|
||||
if (only_to_die && name[1] && name[1] != '/')
|
||||
diagnose_invalid_index_path(stage, prefix, cp);
|
||||
free(new_path);
|
||||
return -1;
|
||||
@@ -1192,7 +1193,7 @@ int get_sha1_with_context_1(const char *name, unsigned char *sha1,
|
||||
if (*cp == ':') {
|
||||
unsigned char tree_sha1[20];
|
||||
char *object_name = NULL;
|
||||
if (!gently) {
|
||||
if (only_to_die) {
|
||||
object_name = xmalloc(cp-name+1);
|
||||
strncpy(object_name, name, cp-name);
|
||||
object_name[cp-name] = '\0';
|
||||
@@ -1205,7 +1206,7 @@ int get_sha1_with_context_1(const char *name, unsigned char *sha1,
|
||||
if (new_filename)
|
||||
filename = new_filename;
|
||||
ret = get_tree_entry(tree_sha1, filename, sha1, &oc->mode);
|
||||
if (!gently) {
|
||||
if (only_to_die) {
|
||||
diagnose_invalid_sha1_path(prefix, filename,
|
||||
tree_sha1, object_name);
|
||||
free(object_name);
|
||||
@@ -1218,7 +1219,7 @@ int get_sha1_with_context_1(const char *name, unsigned char *sha1,
|
||||
free(new_filename);
|
||||
return ret;
|
||||
} else {
|
||||
if (!gently)
|
||||
if (only_to_die)
|
||||
die("Invalid object name '%s'.", object_name);
|
||||
}
|
||||
}
|
||||
|
||||
54
t/t3703-add-magic-pathspec.sh
Executable file
54
t/t3703-add-magic-pathspec.sh
Executable file
@@ -0,0 +1,54 @@
|
||||
#!/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"
|
||||
'
|
||||
|
||||
test_expect_success 'a file with the same (short) magic name exists' '
|
||||
mkdir ":" &&
|
||||
: >":/bar" &&
|
||||
test_must_fail git add -n :/bar &&
|
||||
git add -n "./:/bar"
|
||||
'
|
||||
|
||||
test_done
|
||||
36
t/t4208-log-magic-pathspec.sh
Executable file
36
t/t4208-log-magic-pathspec.sh
Executable file
@@ -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
|
||||
Reference in New Issue
Block a user