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:
@ -228,6 +228,18 @@ int cmd_add(int argc, const char **argv, const char *prefix)
|
|||||||
goto finish;
|
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);
|
fill_directory(&dir, pathspec, ignored_too);
|
||||||
|
|
||||||
if (show_only) {
|
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)
|
static const char *add_prefix(const char *prefix, const char *path)
|
||||||
{
|
{
|
||||||
if (!prefix || !prefix[0])
|
return prefix_path(prefix, prefix ? strlen(prefix) : 0, path);
|
||||||
return path;
|
|
||||||
return prefix_path(prefix, strlen(prefix), path);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
/*
|
/*
|
||||||
|
@ -574,8 +574,17 @@ int cmd_ls_files(int argc, const char **argv, const char *prefix)
|
|||||||
pathspec = get_pathspec(prefix, argv + i);
|
pathspec = get_pathspec(prefix, argv + i);
|
||||||
|
|
||||||
/* Verify that the pathspec matches the prefix */
|
/* 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);
|
prefix = verify_pathspec(prefix);
|
||||||
|
} else if (argc != i)
|
||||||
|
exit(1); /* error message already given */
|
||||||
|
|
||||||
/* Treat unmatching pathspec elements as errors */
|
/* Treat unmatching pathspec elements as errors */
|
||||||
if (pathspec && error_unmatch) {
|
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 count, int base_name)
|
||||||
{
|
{
|
||||||
int i;
|
int i;
|
||||||
|
int len = prefix ? strlen(prefix) : 0;
|
||||||
const char **result = xmalloc((count + 1) * sizeof(const char *));
|
const char **result = xmalloc((count + 1) * sizeof(const char *));
|
||||||
memcpy(result, pathspec, count * sizeof(const char *));
|
memcpy(result, pathspec, count * sizeof(const char *));
|
||||||
result[count] = NULL;
|
result[count] = NULL;
|
||||||
@ -32,8 +33,11 @@ static const char **copy_pathspec(const char *prefix, const char **pathspec,
|
|||||||
if (last_slash)
|
if (last_slash)
|
||||||
result[i] = last_slash + 1;
|
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)
|
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 = add_slash(dst);
|
||||||
dst_len = strlen(dst) - 1;
|
dst_len = strlen(dst);
|
||||||
|
|
||||||
for (j = 0; j < last - first; j++) {
|
for (j = 0; j < last - first; j++) {
|
||||||
const char *path =
|
const char *path =
|
||||||
@ -172,7 +176,7 @@ int cmd_mv(int argc, const char **argv, const char *prefix)
|
|||||||
source[argc + j] = path;
|
source[argc + j] = path;
|
||||||
destination[argc + j] =
|
destination[argc + j] =
|
||||||
prefix_path(dst, dst_len,
|
prefix_path(dst, dst_len,
|
||||||
path + length);
|
path + length + 1);
|
||||||
modes[argc + j] = INDEX;
|
modes[argc + j] = INDEX;
|
||||||
}
|
}
|
||||||
argc += last - first;
|
argc += last - first;
|
||||||
|
164
setup.c
164
setup.c
@ -4,51 +4,118 @@
|
|||||||
static int inside_git_dir = -1;
|
static int inside_git_dir = -1;
|
||||||
static int inside_work_tree = -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 *prefix_path(const char *prefix, int len, const char *path)
|
||||||
{
|
{
|
||||||
const char *orig = path;
|
const char *orig = path;
|
||||||
for (;;) {
|
char *sanitized = xmalloc(len + strlen(path) + 1);
|
||||||
char c;
|
if (*orig == '/')
|
||||||
if (*path != '.')
|
strcpy(sanitized, path);
|
||||||
break;
|
else {
|
||||||
c = path[1];
|
if (len)
|
||||||
/* "." */
|
memcpy(sanitized, prefix, len);
|
||||||
if (!c) {
|
strcpy(sanitized + len, path);
|
||||||
path++;
|
|
||||||
break;
|
|
||||||
}
|
}
|
||||||
/* "./" */
|
if (sanitary_path_copy(sanitized, sanitized))
|
||||||
if (c == '/') {
|
goto error_out;
|
||||||
path += 2;
|
if (*orig == '/') {
|
||||||
continue;
|
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 (c != '.')
|
if (sanitized[len] == '/')
|
||||||
break;
|
len++;
|
||||||
c = path[2];
|
memmove(sanitized, sanitized + len, total - len);
|
||||||
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;
|
|
||||||
}
|
}
|
||||||
if (len) {
|
return sanitized;
|
||||||
int speclen = strlen(path);
|
|
||||||
char *n = xmalloc(speclen + len + 1);
|
|
||||||
|
|
||||||
memcpy(n, prefix, len);
|
|
||||||
memcpy(n + len, path, speclen+1);
|
|
||||||
path = n;
|
|
||||||
}
|
|
||||||
return path;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
/*
|
/*
|
||||||
@ -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 **get_pathspec(const char *prefix, const char **pathspec)
|
||||||
{
|
{
|
||||||
const char *entry = *pathspec;
|
const char *entry = *pathspec;
|
||||||
const char **p;
|
const char **src, **dst;
|
||||||
int prefixlen;
|
int prefixlen;
|
||||||
|
|
||||||
if (!prefix && !entry)
|
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.. */
|
/* Otherwise we have to re-write the entries.. */
|
||||||
p = pathspec;
|
src = pathspec;
|
||||||
|
dst = pathspec;
|
||||||
prefixlen = prefix ? strlen(prefix) : 0;
|
prefixlen = prefix ? strlen(prefix) : 0;
|
||||||
do {
|
while (*src) {
|
||||||
*p = prefix_path(prefix, prefixlen, entry);
|
const char *p = prefix_path(prefix, prefixlen, *src);
|
||||||
} while ((entry = *++p) != NULL);
|
if (p)
|
||||||
return (const char **) pathspec;
|
*(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
|
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
|
test_done
|
||||||
|
164
t/t7010-setup.sh
Executable file
164
t/t7010-setup.sh
Executable file
@ -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
|
Reference in New Issue
Block a user