зеркало из https://github.com/microsoft/git.git
Merge branch 'jc/setup'
* jc/setup: builtin-mv: minimum fix to avoid losing files git-add: adjust to the get_pathspec() changes. Make blame accept absolute paths setup: sanitize absolute and funny paths in get_pathspec()
This commit is contained in:
Коммит
9e7bd0110b
|
@ -228,6 +228,18 @@ int cmd_add(int argc, const char **argv, const char *prefix)
|
|||
goto finish;
|
||||
}
|
||||
|
||||
if (*argv) {
|
||||
/* Was there an invalid path? */
|
||||
if (pathspec) {
|
||||
int num;
|
||||
for (num = 0; pathspec[num]; num++)
|
||||
; /* just counting */
|
||||
if (argc != num)
|
||||
exit(1); /* error message already given */
|
||||
} else
|
||||
exit(1); /* error message already given */
|
||||
}
|
||||
|
||||
fill_directory(&dir, pathspec, ignored_too);
|
||||
|
||||
if (show_only) {
|
||||
|
|
|
@ -1894,9 +1894,7 @@ static unsigned parse_score(const char *arg)
|
|||
|
||||
static const char *add_prefix(const char *prefix, const char *path)
|
||||
{
|
||||
if (!prefix || !prefix[0])
|
||||
return path;
|
||||
return prefix_path(prefix, strlen(prefix), path);
|
||||
return prefix_path(prefix, prefix ? strlen(prefix) : 0, path);
|
||||
}
|
||||
|
||||
/*
|
||||
|
|
|
@ -574,8 +574,17 @@ int cmd_ls_files(int argc, const char **argv, const char *prefix)
|
|||
pathspec = get_pathspec(prefix, argv + i);
|
||||
|
||||
/* Verify that the pathspec matches the prefix */
|
||||
if (pathspec)
|
||||
if (pathspec) {
|
||||
if (argc != i) {
|
||||
int cnt;
|
||||
for (cnt = 0; pathspec[cnt]; cnt++)
|
||||
;
|
||||
if (cnt != (argc - i))
|
||||
exit(1); /* error message already given */
|
||||
}
|
||||
prefix = verify_pathspec(prefix);
|
||||
} else if (argc != i)
|
||||
exit(1); /* error message already given */
|
||||
|
||||
/* Treat unmatching pathspec elements as errors */
|
||||
if (pathspec && error_unmatch) {
|
||||
|
|
10
builtin-mv.c
10
builtin-mv.c
|
@ -19,6 +19,7 @@ static const char **copy_pathspec(const char *prefix, const char **pathspec,
|
|||
int count, int base_name)
|
||||
{
|
||||
int i;
|
||||
int len = prefix ? strlen(prefix) : 0;
|
||||
const char **result = xmalloc((count + 1) * sizeof(const char *));
|
||||
memcpy(result, pathspec, count * sizeof(const char *));
|
||||
result[count] = NULL;
|
||||
|
@ -32,8 +33,11 @@ static const char **copy_pathspec(const char *prefix, const char **pathspec,
|
|||
if (last_slash)
|
||||
result[i] = last_slash + 1;
|
||||
}
|
||||
result[i] = prefix_path(prefix, len, result[i]);
|
||||
if (!result[i])
|
||||
exit(1); /* error already given */
|
||||
}
|
||||
return get_pathspec(prefix, result);
|
||||
return result;
|
||||
}
|
||||
|
||||
static void show_list(const char *label, struct path_list *list)
|
||||
|
@ -164,7 +168,7 @@ int cmd_mv(int argc, const char **argv, const char *prefix)
|
|||
}
|
||||
|
||||
dst = add_slash(dst);
|
||||
dst_len = strlen(dst) - 1;
|
||||
dst_len = strlen(dst);
|
||||
|
||||
for (j = 0; j < last - first; j++) {
|
||||
const char *path =
|
||||
|
@ -172,7 +176,7 @@ int cmd_mv(int argc, const char **argv, const char *prefix)
|
|||
source[argc + j] = path;
|
||||
destination[argc + j] =
|
||||
prefix_path(dst, dst_len,
|
||||
path + length);
|
||||
path + length + 1);
|
||||
modes[argc + j] = INDEX;
|
||||
}
|
||||
argc += last - first;
|
||||
|
|
166
setup.c
166
setup.c
|
@ -4,51 +4,118 @@
|
|||
static int inside_git_dir = -1;
|
||||
static int inside_work_tree = -1;
|
||||
|
||||
static int sanitary_path_copy(char *dst, const char *src)
|
||||
{
|
||||
char *dst0 = dst;
|
||||
|
||||
if (*src == '/') {
|
||||
*dst++ = '/';
|
||||
while (*src == '/')
|
||||
src++;
|
||||
}
|
||||
|
||||
for (;;) {
|
||||
char c = *src;
|
||||
|
||||
/*
|
||||
* A path component that begins with . could be
|
||||
* special:
|
||||
* (1) "." and ends -- ignore and terminate.
|
||||
* (2) "./" -- ignore them, eat slash and continue.
|
||||
* (3) ".." and ends -- strip one and terminate.
|
||||
* (4) "../" -- strip one, eat slash and continue.
|
||||
*/
|
||||
if (c == '.') {
|
||||
switch (src[1]) {
|
||||
case '\0':
|
||||
/* (1) */
|
||||
src++;
|
||||
break;
|
||||
case '/':
|
||||
/* (2) */
|
||||
src += 2;
|
||||
while (*src == '/')
|
||||
src++;
|
||||
continue;
|
||||
case '.':
|
||||
switch (src[2]) {
|
||||
case '\0':
|
||||
/* (3) */
|
||||
src += 2;
|
||||
goto up_one;
|
||||
case '/':
|
||||
/* (4) */
|
||||
src += 3;
|
||||
while (*src == '/')
|
||||
src++;
|
||||
goto up_one;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/* copy up to the next '/', and eat all '/' */
|
||||
while ((c = *src++) != '\0' && c != '/')
|
||||
*dst++ = c;
|
||||
if (c == '/') {
|
||||
*dst++ = c;
|
||||
while (c == '/')
|
||||
c = *src++;
|
||||
src--;
|
||||
} else if (!c)
|
||||
break;
|
||||
continue;
|
||||
|
||||
up_one:
|
||||
/*
|
||||
* dst0..dst is prefix portion, and dst[-1] is '/';
|
||||
* go up one level.
|
||||
*/
|
||||
dst -= 2; /* go past trailing '/' if any */
|
||||
if (dst < dst0)
|
||||
return -1;
|
||||
while (1) {
|
||||
if (dst <= dst0)
|
||||
break;
|
||||
c = *dst--;
|
||||
if (c == '/') {
|
||||
dst += 2;
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
*dst = '\0';
|
||||
return 0;
|
||||
}
|
||||
|
||||
const char *prefix_path(const char *prefix, int len, const char *path)
|
||||
{
|
||||
const char *orig = path;
|
||||
for (;;) {
|
||||
char c;
|
||||
if (*path != '.')
|
||||
break;
|
||||
c = path[1];
|
||||
/* "." */
|
||||
if (!c) {
|
||||
path++;
|
||||
break;
|
||||
}
|
||||
/* "./" */
|
||||
if (c == '/') {
|
||||
path += 2;
|
||||
continue;
|
||||
}
|
||||
if (c != '.')
|
||||
break;
|
||||
c = path[2];
|
||||
if (!c)
|
||||
path += 2;
|
||||
else if (c == '/')
|
||||
path += 3;
|
||||
else
|
||||
break;
|
||||
/* ".." and "../" */
|
||||
/* Remove last component of the prefix */
|
||||
do {
|
||||
if (!len)
|
||||
die("'%s' is outside repository", orig);
|
||||
len--;
|
||||
} while (len && prefix[len-1] != '/');
|
||||
continue;
|
||||
char *sanitized = xmalloc(len + strlen(path) + 1);
|
||||
if (*orig == '/')
|
||||
strcpy(sanitized, path);
|
||||
else {
|
||||
if (len)
|
||||
memcpy(sanitized, prefix, len);
|
||||
strcpy(sanitized + len, path);
|
||||
}
|
||||
if (len) {
|
||||
int speclen = strlen(path);
|
||||
char *n = xmalloc(speclen + len + 1);
|
||||
|
||||
memcpy(n, prefix, len);
|
||||
memcpy(n + len, path, speclen+1);
|
||||
path = n;
|
||||
if (sanitary_path_copy(sanitized, sanitized))
|
||||
goto error_out;
|
||||
if (*orig == '/') {
|
||||
const char *work_tree = get_git_work_tree();
|
||||
size_t len = strlen(work_tree);
|
||||
size_t total = strlen(sanitized) + 1;
|
||||
if (strncmp(sanitized, work_tree, len) ||
|
||||
(sanitized[len] != '\0' && sanitized[len] != '/')) {
|
||||
error_out:
|
||||
error("'%s' is outside repository", orig);
|
||||
free(sanitized);
|
||||
return NULL;
|
||||
}
|
||||
if (sanitized[len] == '/')
|
||||
len++;
|
||||
memmove(sanitized, sanitized + len, total - len);
|
||||
}
|
||||
return path;
|
||||
return sanitized;
|
||||
}
|
||||
|
||||
/*
|
||||
|
@ -114,7 +181,7 @@ void verify_non_filename(const char *prefix, const char *arg)
|
|||
const char **get_pathspec(const char *prefix, const char **pathspec)
|
||||
{
|
||||
const char *entry = *pathspec;
|
||||
const char **p;
|
||||
const char **src, **dst;
|
||||
int prefixlen;
|
||||
|
||||
if (!prefix && !entry)
|
||||
|
@ -128,12 +195,19 @@ const char **get_pathspec(const char *prefix, const char **pathspec)
|
|||
}
|
||||
|
||||
/* Otherwise we have to re-write the entries.. */
|
||||
p = pathspec;
|
||||
src = pathspec;
|
||||
dst = pathspec;
|
||||
prefixlen = prefix ? strlen(prefix) : 0;
|
||||
do {
|
||||
*p = prefix_path(prefix, prefixlen, entry);
|
||||
} while ((entry = *++p) != NULL);
|
||||
return (const char **) pathspec;
|
||||
while (*src) {
|
||||
const char *p = prefix_path(prefix, prefixlen, *src);
|
||||
if (p)
|
||||
*(dst++) = p;
|
||||
src++;
|
||||
}
|
||||
*dst = NULL;
|
||||
if (!*pathspec)
|
||||
return NULL;
|
||||
return pathspec;
|
||||
}
|
||||
|
||||
/*
|
||||
|
|
|
@ -118,4 +118,42 @@ test_expect_success "Sergey Vlasov's test case" '
|
|||
git mv ab a
|
||||
'
|
||||
|
||||
test_expect_success 'absolute pathname' '(
|
||||
|
||||
rm -fr mine &&
|
||||
mkdir mine &&
|
||||
cd mine &&
|
||||
test_create_repo one &&
|
||||
cd one &&
|
||||
mkdir sub &&
|
||||
>sub/file &&
|
||||
git add sub/file &&
|
||||
|
||||
git mv sub "$(pwd)/in" &&
|
||||
! test -d sub &&
|
||||
test -d in &&
|
||||
git ls-files --error-unmatch in/file
|
||||
|
||||
|
||||
)'
|
||||
|
||||
test_expect_success 'absolute pathname outside should fail' '(
|
||||
|
||||
rm -fr mine &&
|
||||
mkdir mine &&
|
||||
cd mine &&
|
||||
out=$(pwd) &&
|
||||
test_create_repo one &&
|
||||
cd one &&
|
||||
mkdir sub &&
|
||||
>sub/file &&
|
||||
git add sub/file &&
|
||||
|
||||
! git mv sub "$out/out" &&
|
||||
test -d sub &&
|
||||
! test -d ../in &&
|
||||
git ls-files --error-unmatch sub/file
|
||||
|
||||
)'
|
||||
|
||||
test_done
|
||||
|
|
|
@ -0,0 +1,164 @@
|
|||
#!/bin/sh
|
||||
|
||||
test_description='setup taking and sanitizing funny paths'
|
||||
|
||||
. ./test-lib.sh
|
||||
|
||||
test_expect_success setup '
|
||||
|
||||
mkdir -p a/b/c a/e &&
|
||||
D=$(pwd) &&
|
||||
>a/b/c/d &&
|
||||
>a/e/f
|
||||
|
||||
'
|
||||
|
||||
test_expect_success 'git add (absolute)' '
|
||||
|
||||
git add "$D/a/b/c/d" &&
|
||||
git ls-files >current &&
|
||||
echo a/b/c/d >expect &&
|
||||
diff -u expect current
|
||||
|
||||
'
|
||||
|
||||
|
||||
test_expect_success 'git add (funny relative)' '
|
||||
|
||||
rm -f .git/index &&
|
||||
(
|
||||
cd a/b &&
|
||||
git add "../e/./f"
|
||||
) &&
|
||||
git ls-files >current &&
|
||||
echo a/e/f >expect &&
|
||||
diff -u expect current
|
||||
|
||||
'
|
||||
|
||||
test_expect_success 'git rm (absolute)' '
|
||||
|
||||
rm -f .git/index &&
|
||||
git add a &&
|
||||
git rm -f --cached "$D/a/b/c/d" &&
|
||||
git ls-files >current &&
|
||||
echo a/e/f >expect &&
|
||||
diff -u expect current
|
||||
|
||||
'
|
||||
|
||||
test_expect_success 'git rm (funny relative)' '
|
||||
|
||||
rm -f .git/index &&
|
||||
git add a &&
|
||||
(
|
||||
cd a/b &&
|
||||
git rm -f --cached "../e/./f"
|
||||
) &&
|
||||
git ls-files >current &&
|
||||
echo a/b/c/d >expect &&
|
||||
diff -u expect current
|
||||
|
||||
'
|
||||
|
||||
test_expect_success 'git ls-files (absolute)' '
|
||||
|
||||
rm -f .git/index &&
|
||||
git add a &&
|
||||
git ls-files "$D/a/e/../b" >current &&
|
||||
echo a/b/c/d >expect &&
|
||||
diff -u expect current
|
||||
|
||||
'
|
||||
|
||||
test_expect_success 'git ls-files (relative #1)' '
|
||||
|
||||
rm -f .git/index &&
|
||||
git add a &&
|
||||
(
|
||||
cd a/b &&
|
||||
git ls-files "../b/c"
|
||||
) >current &&
|
||||
echo c/d >expect &&
|
||||
diff -u expect current
|
||||
|
||||
'
|
||||
|
||||
test_expect_success 'git ls-files (relative #2)' '
|
||||
|
||||
rm -f .git/index &&
|
||||
git add a &&
|
||||
(
|
||||
cd a/b &&
|
||||
git ls-files --full-name "../e/f"
|
||||
) >current &&
|
||||
echo a/e/f >expect &&
|
||||
diff -u expect current
|
||||
|
||||
'
|
||||
|
||||
test_expect_success 'git ls-files (relative #3)' '
|
||||
|
||||
rm -f .git/index &&
|
||||
git add a &&
|
||||
(
|
||||
cd a/b &&
|
||||
if git ls-files "../e/f"
|
||||
then
|
||||
echo Gaah, should have failed
|
||||
exit 1
|
||||
else
|
||||
: happy
|
||||
fi
|
||||
)
|
||||
|
||||
'
|
||||
|
||||
test_expect_success 'commit using absolute path names' '
|
||||
git commit -m "foo" &&
|
||||
echo aa >>a/b/c/d &&
|
||||
git commit -m "aa" "$(pwd)/a/b/c/d"
|
||||
'
|
||||
|
||||
test_expect_success 'log using absolute path names' '
|
||||
echo bb >>a/b/c/d &&
|
||||
git commit -m "bb" $(pwd)/a/b/c/d &&
|
||||
|
||||
git log a/b/c/d >f1.txt &&
|
||||
git log "$(pwd)/a/b/c/d" >f2.txt &&
|
||||
diff -u f1.txt f2.txt
|
||||
'
|
||||
|
||||
test_expect_success 'blame using absolute path names' '
|
||||
git blame a/b/c/d >f1.txt &&
|
||||
git blame "$(pwd)/a/b/c/d" >f2.txt &&
|
||||
diff -u f1.txt f2.txt
|
||||
'
|
||||
|
||||
test_expect_success 'setup deeper work tree' '
|
||||
test_create_repo tester
|
||||
'
|
||||
|
||||
test_expect_success 'add a directory outside the work tree' '(
|
||||
cd tester &&
|
||||
d1="$(cd .. ; pwd)" &&
|
||||
git add "$d1"
|
||||
)'
|
||||
|
||||
test_expect_success 'add a file outside the work tree, nasty case 1' '(
|
||||
cd tester &&
|
||||
f="$(pwd)x" &&
|
||||
echo "$f" &&
|
||||
touch "$f" &&
|
||||
git add "$f"
|
||||
)'
|
||||
|
||||
test_expect_success 'add a file outside the work tree, nasty case 2' '(
|
||||
cd tester &&
|
||||
f="$(pwd | sed "s/.$//")x" &&
|
||||
echo "$f" &&
|
||||
touch "$f" &&
|
||||
git add "$f"
|
||||
)'
|
||||
|
||||
test_done
|
Загрузка…
Ссылка в новой задаче