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:
Junio C Hamano
2011-05-11 11:24:27 -07:00
7 changed files with 124 additions and 56 deletions

View File

@@ -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

View File

@@ -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;

View File

@@ -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
View File

@@ -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)

View File

@@ -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
View 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
View 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