Merge branch 'cb/ls-files-cdup'
* cb/ls-files-cdup: ls-files: allow relative pathspec quote.c: separate quoting and relative path generation
This commit is contained in:
commit
e09151281d
@ -26,8 +26,9 @@ static int show_killed;
|
|||||||
static int show_valid_bit;
|
static int show_valid_bit;
|
||||||
static int line_terminator = '\n';
|
static int line_terminator = '\n';
|
||||||
|
|
||||||
|
static const char *prefix;
|
||||||
|
static int max_prefix_len;
|
||||||
static int prefix_len;
|
static int prefix_len;
|
||||||
static int prefix_offset;
|
|
||||||
static const char **pathspec;
|
static const char **pathspec;
|
||||||
static int error_unmatch;
|
static int error_unmatch;
|
||||||
static char *ps_matched;
|
static char *ps_matched;
|
||||||
@ -43,10 +44,15 @@ static const char *tag_modified = "";
|
|||||||
static const char *tag_skip_worktree = "";
|
static const char *tag_skip_worktree = "";
|
||||||
static const char *tag_resolve_undo = "";
|
static const char *tag_resolve_undo = "";
|
||||||
|
|
||||||
|
static void write_name(const char* name, size_t len)
|
||||||
|
{
|
||||||
|
write_name_quoted_relative(name, len, prefix, prefix_len, stdout,
|
||||||
|
line_terminator);
|
||||||
|
}
|
||||||
|
|
||||||
static void show_dir_entry(const char *tag, struct dir_entry *ent)
|
static void show_dir_entry(const char *tag, struct dir_entry *ent)
|
||||||
{
|
{
|
||||||
int len = prefix_len;
|
int len = max_prefix_len;
|
||||||
int offset = prefix_offset;
|
|
||||||
|
|
||||||
if (len >= ent->len)
|
if (len >= ent->len)
|
||||||
die("git ls-files: internal error - directory entry not superset of prefix");
|
die("git ls-files: internal error - directory entry not superset of prefix");
|
||||||
@ -55,7 +61,7 @@ static void show_dir_entry(const char *tag, struct dir_entry *ent)
|
|||||||
return;
|
return;
|
||||||
|
|
||||||
fputs(tag, stdout);
|
fputs(tag, stdout);
|
||||||
write_name_quoted(ent->name + offset, stdout, line_terminator);
|
write_name(ent->name, ent->len);
|
||||||
}
|
}
|
||||||
|
|
||||||
static void show_other_files(struct dir_struct *dir)
|
static void show_other_files(struct dir_struct *dir)
|
||||||
@ -121,8 +127,7 @@ static void show_killed_files(struct dir_struct *dir)
|
|||||||
|
|
||||||
static void show_ce_entry(const char *tag, struct cache_entry *ce)
|
static void show_ce_entry(const char *tag, struct cache_entry *ce)
|
||||||
{
|
{
|
||||||
int len = prefix_len;
|
int len = max_prefix_len;
|
||||||
int offset = prefix_offset;
|
|
||||||
|
|
||||||
if (len >= ce_namelen(ce))
|
if (len >= ce_namelen(ce))
|
||||||
die("git ls-files: internal error - cache entry not superset of prefix");
|
die("git ls-files: internal error - cache entry not superset of prefix");
|
||||||
@ -156,20 +161,19 @@ static void show_ce_entry(const char *tag, struct cache_entry *ce)
|
|||||||
find_unique_abbrev(ce->sha1,abbrev),
|
find_unique_abbrev(ce->sha1,abbrev),
|
||||||
ce_stage(ce));
|
ce_stage(ce));
|
||||||
}
|
}
|
||||||
write_name_quoted(ce->name + offset, stdout, line_terminator);
|
write_name(ce->name, ce_namelen(ce));
|
||||||
}
|
}
|
||||||
|
|
||||||
static int show_one_ru(struct string_list_item *item, void *cbdata)
|
static int show_one_ru(struct string_list_item *item, void *cbdata)
|
||||||
{
|
{
|
||||||
int offset = prefix_offset;
|
|
||||||
const char *path = item->string;
|
const char *path = item->string;
|
||||||
struct resolve_undo_info *ui = item->util;
|
struct resolve_undo_info *ui = item->util;
|
||||||
int i, len;
|
int i, len;
|
||||||
|
|
||||||
len = strlen(path);
|
len = strlen(path);
|
||||||
if (len < prefix_len)
|
if (len < max_prefix_len)
|
||||||
return 0; /* outside of the prefix */
|
return 0; /* outside of the prefix */
|
||||||
if (!match_pathspec(pathspec, path, len, prefix_len, ps_matched))
|
if (!match_pathspec(pathspec, path, len, max_prefix_len, ps_matched))
|
||||||
return 0; /* uninterested */
|
return 0; /* uninterested */
|
||||||
for (i = 0; i < 3; i++) {
|
for (i = 0; i < 3; i++) {
|
||||||
if (!ui->mode[i])
|
if (!ui->mode[i])
|
||||||
@ -177,19 +181,19 @@ static int show_one_ru(struct string_list_item *item, void *cbdata)
|
|||||||
printf("%s%06o %s %d\t", tag_resolve_undo, ui->mode[i],
|
printf("%s%06o %s %d\t", tag_resolve_undo, ui->mode[i],
|
||||||
find_unique_abbrev(ui->sha1[i], abbrev),
|
find_unique_abbrev(ui->sha1[i], abbrev),
|
||||||
i + 1);
|
i + 1);
|
||||||
write_name_quoted(path + offset, stdout, line_terminator);
|
write_name(path, len);
|
||||||
}
|
}
|
||||||
return 0;
|
return 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
static void show_ru_info(const char *prefix)
|
static void show_ru_info(void)
|
||||||
{
|
{
|
||||||
if (!the_index.resolve_undo)
|
if (!the_index.resolve_undo)
|
||||||
return;
|
return;
|
||||||
for_each_string_list(show_one_ru, the_index.resolve_undo, NULL);
|
for_each_string_list(show_one_ru, the_index.resolve_undo, NULL);
|
||||||
}
|
}
|
||||||
|
|
||||||
static void show_files(struct dir_struct *dir, const char *prefix)
|
static void show_files(struct dir_struct *dir)
|
||||||
{
|
{
|
||||||
int i;
|
int i;
|
||||||
|
|
||||||
@ -243,7 +247,7 @@ static void show_files(struct dir_struct *dir, const char *prefix)
|
|||||||
*/
|
*/
|
||||||
static void prune_cache(const char *prefix)
|
static void prune_cache(const char *prefix)
|
||||||
{
|
{
|
||||||
int pos = cache_name_pos(prefix, prefix_len);
|
int pos = cache_name_pos(prefix, max_prefix_len);
|
||||||
unsigned int first, last;
|
unsigned int first, last;
|
||||||
|
|
||||||
if (pos < 0)
|
if (pos < 0)
|
||||||
@ -256,7 +260,7 @@ static void prune_cache(const char *prefix)
|
|||||||
while (last > first) {
|
while (last > first) {
|
||||||
int next = (last + first) >> 1;
|
int next = (last + first) >> 1;
|
||||||
struct cache_entry *ce = active_cache[next];
|
struct cache_entry *ce = active_cache[next];
|
||||||
if (!strncmp(ce->name, prefix, prefix_len)) {
|
if (!strncmp(ce->name, prefix, max_prefix_len)) {
|
||||||
first = next+1;
|
first = next+1;
|
||||||
continue;
|
continue;
|
||||||
}
|
}
|
||||||
@ -265,11 +269,16 @@ static void prune_cache(const char *prefix)
|
|||||||
active_nr = last;
|
active_nr = last;
|
||||||
}
|
}
|
||||||
|
|
||||||
static const char *verify_pathspec(const char *prefix)
|
static const char *pathspec_prefix(const char *prefix)
|
||||||
{
|
{
|
||||||
const char **p, *n, *prev;
|
const char **p, *n, *prev;
|
||||||
unsigned long max;
|
unsigned long max;
|
||||||
|
|
||||||
|
if (!pathspec) {
|
||||||
|
max_prefix_len = prefix ? strlen(prefix) : 0;
|
||||||
|
return prefix;
|
||||||
|
}
|
||||||
|
|
||||||
prev = NULL;
|
prev = NULL;
|
||||||
max = PATH_MAX;
|
max = PATH_MAX;
|
||||||
for (p = pathspec; (n = *p) != NULL; p++) {
|
for (p = pathspec; (n = *p) != NULL; p++) {
|
||||||
@ -291,10 +300,7 @@ static const char *verify_pathspec(const char *prefix)
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
if (prefix_offset > max || memcmp(prev, prefix, prefix_offset))
|
max_prefix_len = max;
|
||||||
die("git ls-files: cannot generate relative filenames containing '..'");
|
|
||||||
|
|
||||||
prefix_len = max;
|
|
||||||
return max ? xmemdupz(prev, max) : NULL;
|
return max ? xmemdupz(prev, max) : NULL;
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -374,7 +380,7 @@ void overlay_tree_on_cache(const char *tree_name, const char *prefix)
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
int report_path_error(const char *ps_matched, const char **pathspec, int prefix_offset)
|
int report_path_error(const char *ps_matched, const char **pathspec, int prefix_len)
|
||||||
{
|
{
|
||||||
/*
|
/*
|
||||||
* Make sure all pathspec matched; otherwise it is an error.
|
* Make sure all pathspec matched; otherwise it is an error.
|
||||||
@ -404,7 +410,7 @@ int report_path_error(const char *ps_matched, const char **pathspec, int prefix_
|
|||||||
continue;
|
continue;
|
||||||
|
|
||||||
error("pathspec '%s' did not match any file(s) known to git.",
|
error("pathspec '%s' did not match any file(s) known to git.",
|
||||||
pathspec[num] + prefix_offset);
|
pathspec[num] + prefix_len);
|
||||||
errors++;
|
errors++;
|
||||||
}
|
}
|
||||||
return errors;
|
return errors;
|
||||||
@ -456,9 +462,10 @@ static int option_parse_exclude_standard(const struct option *opt,
|
|||||||
return 0;
|
return 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
int cmd_ls_files(int argc, const char **argv, const char *prefix)
|
int cmd_ls_files(int argc, const char **argv, const char *cmd_prefix)
|
||||||
{
|
{
|
||||||
int require_work_tree = 0, show_tag = 0;
|
int require_work_tree = 0, show_tag = 0;
|
||||||
|
const char *max_prefix;
|
||||||
struct dir_struct dir;
|
struct dir_struct dir;
|
||||||
struct option builtin_ls_files_options[] = {
|
struct option builtin_ls_files_options[] = {
|
||||||
{ OPTION_CALLBACK, 'z', NULL, NULL, NULL,
|
{ OPTION_CALLBACK, 'z', NULL, NULL, NULL,
|
||||||
@ -504,7 +511,7 @@ int cmd_ls_files(int argc, const char **argv, const char *prefix)
|
|||||||
{ OPTION_CALLBACK, 0, "exclude-standard", &dir, NULL,
|
{ OPTION_CALLBACK, 0, "exclude-standard", &dir, NULL,
|
||||||
"add the standard git exclusions",
|
"add the standard git exclusions",
|
||||||
PARSE_OPT_NOARG, option_parse_exclude_standard },
|
PARSE_OPT_NOARG, option_parse_exclude_standard },
|
||||||
{ OPTION_SET_INT, 0, "full-name", &prefix_offset, NULL,
|
{ OPTION_SET_INT, 0, "full-name", &prefix_len, NULL,
|
||||||
"make the output relative to the project top directory",
|
"make the output relative to the project top directory",
|
||||||
PARSE_OPT_NOARG | PARSE_OPT_NONEG, NULL },
|
PARSE_OPT_NOARG | PARSE_OPT_NONEG, NULL },
|
||||||
OPT_BOOLEAN(0, "error-unmatch", &error_unmatch,
|
OPT_BOOLEAN(0, "error-unmatch", &error_unmatch,
|
||||||
@ -516,8 +523,9 @@ int cmd_ls_files(int argc, const char **argv, const char *prefix)
|
|||||||
};
|
};
|
||||||
|
|
||||||
memset(&dir, 0, sizeof(dir));
|
memset(&dir, 0, sizeof(dir));
|
||||||
|
prefix = cmd_prefix;
|
||||||
if (prefix)
|
if (prefix)
|
||||||
prefix_offset = strlen(prefix);
|
prefix_len = strlen(prefix);
|
||||||
git_config(git_default_config, NULL);
|
git_config(git_default_config, NULL);
|
||||||
|
|
||||||
if (read_cache() < 0)
|
if (read_cache() < 0)
|
||||||
@ -555,9 +563,8 @@ int cmd_ls_files(int argc, const char **argv, const char *prefix)
|
|||||||
if (pathspec)
|
if (pathspec)
|
||||||
strip_trailing_slash_from_submodules();
|
strip_trailing_slash_from_submodules();
|
||||||
|
|
||||||
/* Verify that the pathspec matches the prefix */
|
/* Find common prefix for all pathspec's */
|
||||||
if (pathspec)
|
max_prefix = pathspec_prefix(prefix);
|
||||||
prefix = verify_pathspec(prefix);
|
|
||||||
|
|
||||||
/* Treat unmatching pathspec elements as errors */
|
/* Treat unmatching pathspec elements as errors */
|
||||||
if (pathspec && error_unmatch) {
|
if (pathspec && error_unmatch) {
|
||||||
@ -575,8 +582,8 @@ int cmd_ls_files(int argc, const char **argv, const char *prefix)
|
|||||||
show_killed | show_modified | show_resolve_undo))
|
show_killed | show_modified | show_resolve_undo))
|
||||||
show_cached = 1;
|
show_cached = 1;
|
||||||
|
|
||||||
if (prefix)
|
if (max_prefix)
|
||||||
prune_cache(prefix);
|
prune_cache(max_prefix);
|
||||||
if (with_tree) {
|
if (with_tree) {
|
||||||
/*
|
/*
|
||||||
* Basic sanity check; show-stages and show-unmerged
|
* Basic sanity check; show-stages and show-unmerged
|
||||||
@ -584,15 +591,15 @@ int cmd_ls_files(int argc, const char **argv, const char *prefix)
|
|||||||
*/
|
*/
|
||||||
if (show_stage || show_unmerged)
|
if (show_stage || show_unmerged)
|
||||||
die("ls-files --with-tree is incompatible with -s or -u");
|
die("ls-files --with-tree is incompatible with -s or -u");
|
||||||
overlay_tree_on_cache(with_tree, prefix);
|
overlay_tree_on_cache(with_tree, max_prefix);
|
||||||
}
|
}
|
||||||
show_files(&dir, prefix);
|
show_files(&dir);
|
||||||
if (show_resolve_undo)
|
if (show_resolve_undo)
|
||||||
show_ru_info(prefix);
|
show_ru_info();
|
||||||
|
|
||||||
if (ps_matched) {
|
if (ps_matched) {
|
||||||
int bad;
|
int bad;
|
||||||
bad = report_path_error(ps_matched, pathspec, prefix_offset);
|
bad = report_path_error(ps_matched, pathspec, prefix_len);
|
||||||
if (bad)
|
if (bad)
|
||||||
fprintf(stderr, "Did you forget to 'git add'?\n");
|
fprintf(stderr, "Did you forget to 'git add'?\n");
|
||||||
|
|
||||||
|
95
quote.c
95
quote.c
@ -295,42 +295,75 @@ void write_name_quotedpfx(const char *pfx, size_t pfxlen,
|
|||||||
fputc(terminator, fp);
|
fputc(terminator, fp);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
static const char *path_relative(const char *in, int len,
|
||||||
|
struct strbuf *sb, const char *prefix,
|
||||||
|
int prefix_len);
|
||||||
|
|
||||||
|
void write_name_quoted_relative(const char *name, size_t len,
|
||||||
|
const char *prefix, size_t prefix_len,
|
||||||
|
FILE *fp, int terminator)
|
||||||
|
{
|
||||||
|
struct strbuf sb = STRBUF_INIT;
|
||||||
|
|
||||||
|
name = path_relative(name, len, &sb, prefix, prefix_len);
|
||||||
|
write_name_quoted(name, fp, terminator);
|
||||||
|
|
||||||
|
strbuf_release(&sb);
|
||||||
|
}
|
||||||
|
|
||||||
|
/*
|
||||||
|
* Give path as relative to prefix.
|
||||||
|
*
|
||||||
|
* The strbuf may or may not be used, so do not assume it contains the
|
||||||
|
* returned path.
|
||||||
|
*/
|
||||||
|
static const char *path_relative(const char *in, int len,
|
||||||
|
struct strbuf *sb, const char *prefix,
|
||||||
|
int prefix_len)
|
||||||
|
{
|
||||||
|
int off, i;
|
||||||
|
|
||||||
|
if (len < 0)
|
||||||
|
len = strlen(in);
|
||||||
|
if (prefix && prefix_len < 0)
|
||||||
|
prefix_len = strlen(prefix);
|
||||||
|
|
||||||
|
off = 0;
|
||||||
|
i = 0;
|
||||||
|
while (i < prefix_len && i < len && prefix[i] == in[i]) {
|
||||||
|
if (prefix[i] == '/')
|
||||||
|
off = i + 1;
|
||||||
|
i++;
|
||||||
|
}
|
||||||
|
in += off;
|
||||||
|
len -= off;
|
||||||
|
|
||||||
|
if (i >= prefix_len)
|
||||||
|
return in;
|
||||||
|
|
||||||
|
strbuf_reset(sb);
|
||||||
|
strbuf_grow(sb, len);
|
||||||
|
|
||||||
|
while (i < prefix_len) {
|
||||||
|
if (prefix[i] == '/')
|
||||||
|
strbuf_addstr(sb, "../");
|
||||||
|
i++;
|
||||||
|
}
|
||||||
|
strbuf_add(sb, in, len);
|
||||||
|
|
||||||
|
return sb->buf;
|
||||||
|
}
|
||||||
|
|
||||||
/* quote path as relative to the given prefix */
|
/* quote path as relative to the given prefix */
|
||||||
char *quote_path_relative(const char *in, int len,
|
char *quote_path_relative(const char *in, int len,
|
||||||
struct strbuf *out, const char *prefix)
|
struct strbuf *out, const char *prefix)
|
||||||
{
|
{
|
||||||
int needquote;
|
struct strbuf sb = STRBUF_INIT;
|
||||||
|
const char *rel = path_relative(in, len, &sb, prefix, -1);
|
||||||
|
strbuf_reset(out);
|
||||||
|
quote_c_style_counted(rel, strlen(rel), out, NULL, 0);
|
||||||
|
strbuf_release(&sb);
|
||||||
|
|
||||||
if (len < 0)
|
|
||||||
len = strlen(in);
|
|
||||||
|
|
||||||
/* "../" prefix itself does not need quoting, but "in" might. */
|
|
||||||
needquote = next_quote_pos(in, len) < len;
|
|
||||||
strbuf_setlen(out, 0);
|
|
||||||
strbuf_grow(out, len);
|
|
||||||
|
|
||||||
if (needquote)
|
|
||||||
strbuf_addch(out, '"');
|
|
||||||
if (prefix) {
|
|
||||||
int off = 0;
|
|
||||||
while (prefix[off] && off < len && prefix[off] == in[off])
|
|
||||||
if (prefix[off] == '/') {
|
|
||||||
prefix += off + 1;
|
|
||||||
in += off + 1;
|
|
||||||
len -= off + 1;
|
|
||||||
off = 0;
|
|
||||||
} else
|
|
||||||
off++;
|
|
||||||
|
|
||||||
for (; *prefix; prefix++)
|
|
||||||
if (*prefix == '/')
|
|
||||||
strbuf_addstr(out, "../");
|
|
||||||
}
|
|
||||||
|
|
||||||
quote_c_style_counted (in, len, out, NULL, 1);
|
|
||||||
|
|
||||||
if (needquote)
|
|
||||||
strbuf_addch(out, '"');
|
|
||||||
if (!out->len)
|
if (!out->len)
|
||||||
strbuf_addstr(out, "./");
|
strbuf_addstr(out, "./");
|
||||||
|
|
||||||
|
5
quote.h
5
quote.h
@ -54,9 +54,12 @@ extern void quote_two_c_style(struct strbuf *, const char *, const char *, int);
|
|||||||
extern void write_name_quoted(const char *name, FILE *, int terminator);
|
extern void write_name_quoted(const char *name, FILE *, int terminator);
|
||||||
extern void write_name_quotedpfx(const char *pfx, size_t pfxlen,
|
extern void write_name_quotedpfx(const char *pfx, size_t pfxlen,
|
||||||
const char *name, FILE *, int terminator);
|
const char *name, FILE *, int terminator);
|
||||||
|
extern void write_name_quoted_relative(const char *name, size_t len,
|
||||||
|
const char *prefix, size_t prefix_len,
|
||||||
|
FILE *fp, int terminator);
|
||||||
|
|
||||||
/* quote path as relative to the given prefix */
|
/* quote path as relative to the given prefix */
|
||||||
char *quote_path_relative(const char *in, int len,
|
extern char *quote_path_relative(const char *in, int len,
|
||||||
struct strbuf *out, const char *prefix);
|
struct strbuf *out, const char *prefix);
|
||||||
|
|
||||||
/* quoting as a string literal for other languages */
|
/* quoting as a string literal for other languages */
|
||||||
|
@ -103,14 +103,10 @@ test_expect_success 'git ls-files (relative #3)' '
|
|||||||
git add a &&
|
git add a &&
|
||||||
(
|
(
|
||||||
cd a/b &&
|
cd a/b &&
|
||||||
if git ls-files "../e/f"
|
git ls-files "../e/f"
|
||||||
then
|
) >current &&
|
||||||
echo Gaah, should have failed
|
echo ../e/f >expect &&
|
||||||
exit 1
|
test_cmp expect current
|
||||||
else
|
|
||||||
: happy
|
|
||||||
fi
|
|
||||||
)
|
|
||||||
|
|
||||||
'
|
'
|
||||||
|
|
||||||
|
Loading…
Reference in New Issue
Block a user