In 036876a106 (config: hide functions using `the_repository` by
default, 2024-08-13) we have moved around a bunch of functions in the
config subsystem that depend on `the_repository`. Those function have
been converted into mere wrappers around their equivalent function that
takes in a repository as parameter, and the intent was that we'll
eventually remove those wrappers to make the dependency on the global
repository variable explicit at the callsite.
Follow through with that intent and remove `git_config()`. All callsites
are adjusted so that they use `repo_config(the_repository, ...)`
instead. While some callsites might already have a repository available,
this mechanical conversion is the exact same as the current situation
and thus cannot cause any regression. Those sites should eventually be
cleaned up in a later patch series.
Signed-off-by: Patrick Steinhardt <ps@pks.im>
Signed-off-by: Junio C Hamano <gitster@pobox.com>
452 lines
12 KiB
C
452 lines
12 KiB
C
/*
|
|
* GIT - The information manager from hell
|
|
*
|
|
* Copyright (C) Linus Torvalds, 2005
|
|
*/
|
|
#define USE_THE_REPOSITORY_VARIABLE
|
|
#include "builtin.h"
|
|
|
|
#include "config.h"
|
|
#include "gettext.h"
|
|
#include "hex.h"
|
|
#include "object-name.h"
|
|
#include "odb.h"
|
|
#include "tree.h"
|
|
#include "path.h"
|
|
#include "quote.h"
|
|
#include "parse-options.h"
|
|
#include "pathspec.h"
|
|
|
|
static const char * const ls_tree_usage[] = {
|
|
N_("git ls-tree [<options>] <tree-ish> [<path>...]"),
|
|
NULL
|
|
};
|
|
|
|
static void expand_objectsize(struct strbuf *line, const struct object_id *oid,
|
|
const enum object_type type, unsigned int padded)
|
|
{
|
|
if (type == OBJ_BLOB) {
|
|
unsigned long size;
|
|
if (odb_read_object_info(the_repository->objects, oid, &size) < 0)
|
|
die(_("could not get object info about '%s'"),
|
|
oid_to_hex(oid));
|
|
if (padded)
|
|
strbuf_addf(line, "%7"PRIuMAX, (uintmax_t)size);
|
|
else
|
|
strbuf_addf(line, "%"PRIuMAX, (uintmax_t)size);
|
|
} else if (padded) {
|
|
strbuf_addf(line, "%7s", "-");
|
|
} else {
|
|
strbuf_addstr(line, "-");
|
|
}
|
|
}
|
|
|
|
struct ls_tree_options {
|
|
unsigned null_termination:1;
|
|
int abbrev;
|
|
enum ls_tree_path_options {
|
|
LS_RECURSIVE = 1 << 0,
|
|
LS_TREE_ONLY = 1 << 1,
|
|
LS_SHOW_TREES = 1 << 2,
|
|
} ls_options;
|
|
struct pathspec pathspec;
|
|
const char *prefix;
|
|
const char *format;
|
|
};
|
|
|
|
static int show_recursive(struct ls_tree_options *options, const char *base,
|
|
size_t baselen, const char *pathname)
|
|
{
|
|
int i;
|
|
|
|
if (options->ls_options & LS_RECURSIVE)
|
|
return 1;
|
|
|
|
if (!options->pathspec.nr)
|
|
return 0;
|
|
|
|
for (i = 0; i < options->pathspec.nr; i++) {
|
|
const char *spec = options->pathspec.items[i].match;
|
|
size_t len, speclen;
|
|
|
|
if (strncmp(base, spec, baselen))
|
|
continue;
|
|
len = strlen(pathname);
|
|
spec += baselen;
|
|
speclen = strlen(spec);
|
|
if (speclen <= len)
|
|
continue;
|
|
if (spec[len] && spec[len] != '/')
|
|
continue;
|
|
if (memcmp(pathname, spec, len))
|
|
continue;
|
|
return 1;
|
|
}
|
|
return 0;
|
|
}
|
|
|
|
static int show_tree_fmt(const struct object_id *oid, struct strbuf *base,
|
|
const char *pathname, unsigned mode, void *context)
|
|
{
|
|
struct ls_tree_options *options = context;
|
|
int recurse = 0;
|
|
struct strbuf sb = STRBUF_INIT;
|
|
enum object_type type = object_type(mode);
|
|
const char *format = options->format;
|
|
|
|
if (type == OBJ_TREE && show_recursive(options, base->buf, base->len, pathname))
|
|
recurse = READ_TREE_RECURSIVE;
|
|
if (type == OBJ_TREE && recurse && !(options->ls_options & LS_SHOW_TREES))
|
|
return recurse;
|
|
if (type == OBJ_BLOB && (options->ls_options & LS_TREE_ONLY))
|
|
return 0;
|
|
|
|
while (strbuf_expand_step(&sb, &format)) {
|
|
size_t len;
|
|
|
|
if (skip_prefix(format, "%", &format))
|
|
strbuf_addch(&sb, '%');
|
|
else if ((len = strbuf_expand_literal(&sb, format)))
|
|
format += len;
|
|
else if (skip_prefix(format, "(objectmode)", &format))
|
|
strbuf_addf(&sb, "%06o", mode);
|
|
else if (skip_prefix(format, "(objecttype)", &format))
|
|
strbuf_addstr(&sb, type_name(type));
|
|
else if (skip_prefix(format, "(objectsize:padded)", &format))
|
|
expand_objectsize(&sb, oid, type, 1);
|
|
else if (skip_prefix(format, "(objectsize)", &format))
|
|
expand_objectsize(&sb, oid, type, 0);
|
|
else if (skip_prefix(format, "(objectname)", &format))
|
|
strbuf_add_unique_abbrev(&sb, oid, options->abbrev);
|
|
else if (skip_prefix(format, "(path)", &format)) {
|
|
const char *name;
|
|
const char *prefix = options->prefix;
|
|
struct strbuf sbuf = STRBUF_INIT;
|
|
size_t baselen = base->len;
|
|
|
|
strbuf_addstr(base, pathname);
|
|
name = relative_path(base->buf, prefix, &sbuf);
|
|
quote_c_style(name, &sb, NULL, 0);
|
|
strbuf_setlen(base, baselen);
|
|
strbuf_release(&sbuf);
|
|
} else
|
|
strbuf_expand_bad_format(format, "ls-tree");
|
|
}
|
|
strbuf_addch(&sb, options->null_termination ? '\0' : '\n');
|
|
fwrite(sb.buf, sb.len, 1, stdout);
|
|
strbuf_release(&sb);
|
|
return recurse;
|
|
}
|
|
|
|
static int show_tree_common(struct ls_tree_options *options, int *recurse,
|
|
struct strbuf *base, const char *pathname,
|
|
enum object_type type)
|
|
{
|
|
int ret = -1;
|
|
*recurse = 0;
|
|
|
|
if (type == OBJ_BLOB) {
|
|
if (options->ls_options & LS_TREE_ONLY)
|
|
ret = 0;
|
|
} else if (type == OBJ_TREE &&
|
|
show_recursive(options, base->buf, base->len, pathname)) {
|
|
*recurse = READ_TREE_RECURSIVE;
|
|
if (!(options->ls_options & LS_SHOW_TREES))
|
|
ret = *recurse;
|
|
}
|
|
|
|
return ret;
|
|
}
|
|
|
|
static void show_tree_common_default_long(struct ls_tree_options *options,
|
|
struct strbuf *base,
|
|
const char *pathname,
|
|
const size_t baselen)
|
|
{
|
|
const char *prefix = options->prefix;
|
|
|
|
strbuf_addstr(base, pathname);
|
|
|
|
if (options->null_termination) {
|
|
struct strbuf sb = STRBUF_INIT;
|
|
const char *name = relative_path(base->buf, prefix, &sb);
|
|
|
|
fputs(name, stdout);
|
|
fputc('\0', stdout);
|
|
|
|
strbuf_release(&sb);
|
|
} else {
|
|
write_name_quoted_relative(base->buf, prefix, stdout, '\n');
|
|
}
|
|
|
|
strbuf_setlen(base, baselen);
|
|
}
|
|
|
|
static int show_tree_default(const struct object_id *oid, struct strbuf *base,
|
|
const char *pathname, unsigned mode,
|
|
void *context)
|
|
{
|
|
struct ls_tree_options *options = context;
|
|
int early;
|
|
int recurse;
|
|
enum object_type type = object_type(mode);
|
|
|
|
early = show_tree_common(options, &recurse, base, pathname, type);
|
|
if (early >= 0)
|
|
return early;
|
|
|
|
printf("%06o %s %s\t", mode, type_name(object_type(mode)),
|
|
repo_find_unique_abbrev(the_repository, oid, options->abbrev));
|
|
show_tree_common_default_long(options, base, pathname, base->len);
|
|
return recurse;
|
|
}
|
|
|
|
static int show_tree_long(const struct object_id *oid, struct strbuf *base,
|
|
const char *pathname, unsigned mode,
|
|
void *context)
|
|
{
|
|
struct ls_tree_options *options = context;
|
|
int early;
|
|
int recurse;
|
|
char size_text[24];
|
|
enum object_type type = object_type(mode);
|
|
|
|
early = show_tree_common(options, &recurse, base, pathname, type);
|
|
if (early >= 0)
|
|
return early;
|
|
|
|
if (type == OBJ_BLOB) {
|
|
unsigned long size;
|
|
if (odb_read_object_info(the_repository->objects, oid, &size) == OBJ_BAD)
|
|
xsnprintf(size_text, sizeof(size_text), "BAD");
|
|
else
|
|
xsnprintf(size_text, sizeof(size_text),
|
|
"%" PRIuMAX, (uintmax_t)size);
|
|
} else {
|
|
xsnprintf(size_text, sizeof(size_text), "-");
|
|
}
|
|
|
|
printf("%06o %s %s %7s\t", mode, type_name(type),
|
|
repo_find_unique_abbrev(the_repository, oid, options->abbrev),
|
|
size_text);
|
|
show_tree_common_default_long(options, base, pathname, base->len);
|
|
return recurse;
|
|
}
|
|
|
|
static int show_tree_name_only(const struct object_id *oid UNUSED,
|
|
struct strbuf *base,
|
|
const char *pathname, unsigned mode,
|
|
void *context)
|
|
{
|
|
struct ls_tree_options *options = context;
|
|
int early;
|
|
int recurse;
|
|
const size_t baselen = base->len;
|
|
enum object_type type = object_type(mode);
|
|
const char *prefix;
|
|
|
|
early = show_tree_common(options, &recurse, base, pathname, type);
|
|
if (early >= 0)
|
|
return early;
|
|
|
|
prefix = options->prefix;
|
|
strbuf_addstr(base, pathname);
|
|
if (options->null_termination) {
|
|
struct strbuf sb = STRBUF_INIT;
|
|
const char *name = relative_path(base->buf, prefix, &sb);
|
|
|
|
fputs(name, stdout);
|
|
fputc('\0', stdout);
|
|
|
|
strbuf_release(&sb);
|
|
} else {
|
|
write_name_quoted_relative(base->buf, prefix, stdout, '\n');
|
|
}
|
|
strbuf_setlen(base, baselen);
|
|
return recurse;
|
|
}
|
|
|
|
static int show_tree_object(const struct object_id *oid, struct strbuf *base,
|
|
const char *pathname, unsigned mode,
|
|
void *context)
|
|
{
|
|
struct ls_tree_options *options = context;
|
|
int early;
|
|
int recurse;
|
|
enum object_type type = object_type(mode);
|
|
const char *str;
|
|
|
|
early = show_tree_common(options, &recurse, base, pathname, type);
|
|
if (early >= 0)
|
|
return early;
|
|
|
|
str = repo_find_unique_abbrev(the_repository, oid, options->abbrev);
|
|
if (options->null_termination) {
|
|
fputs(str, stdout);
|
|
fputc('\0', stdout);
|
|
} else {
|
|
puts(str);
|
|
}
|
|
return recurse;
|
|
}
|
|
|
|
enum ls_tree_cmdmode {
|
|
MODE_DEFAULT = 0,
|
|
MODE_LONG,
|
|
MODE_NAME_ONLY,
|
|
MODE_NAME_STATUS,
|
|
MODE_OBJECT_ONLY,
|
|
};
|
|
|
|
struct ls_tree_cmdmode_to_fmt {
|
|
enum ls_tree_cmdmode mode;
|
|
const char *const fmt;
|
|
read_tree_fn_t fn;
|
|
};
|
|
|
|
static struct ls_tree_cmdmode_to_fmt ls_tree_cmdmode_format[] = {
|
|
{
|
|
.mode = MODE_DEFAULT,
|
|
.fmt = "%(objectmode) %(objecttype) %(objectname)%x09%(path)",
|
|
.fn = show_tree_default,
|
|
},
|
|
{
|
|
.mode = MODE_LONG,
|
|
.fmt = "%(objectmode) %(objecttype) %(objectname) %(objectsize:padded)%x09%(path)",
|
|
.fn = show_tree_long,
|
|
},
|
|
{
|
|
.mode = MODE_NAME_ONLY, /* And MODE_NAME_STATUS */
|
|
.fmt = "%(path)",
|
|
.fn = show_tree_name_only,
|
|
},
|
|
{
|
|
.mode = MODE_OBJECT_ONLY,
|
|
.fmt = "%(objectname)",
|
|
.fn = show_tree_object
|
|
},
|
|
{
|
|
/* fallback */
|
|
.fn = show_tree_default,
|
|
},
|
|
};
|
|
|
|
int cmd_ls_tree(int argc,
|
|
const char **argv,
|
|
const char *prefix,
|
|
struct repository *repo UNUSED)
|
|
{
|
|
struct object_id oid;
|
|
struct tree *tree;
|
|
int i, full_tree = 0;
|
|
int full_name = !prefix || !*prefix;
|
|
read_tree_fn_t fn = NULL;
|
|
enum ls_tree_cmdmode cmdmode = MODE_DEFAULT;
|
|
int null_termination = 0;
|
|
struct ls_tree_options options = { 0 };
|
|
const struct option ls_tree_options[] = {
|
|
OPT_BIT('d', NULL, &options.ls_options, N_("only show trees"),
|
|
LS_TREE_ONLY),
|
|
OPT_BIT('r', NULL, &options.ls_options, N_("recurse into subtrees"),
|
|
LS_RECURSIVE),
|
|
OPT_BIT('t', NULL, &options.ls_options, N_("show trees when recursing"),
|
|
LS_SHOW_TREES),
|
|
OPT_BOOL('z', NULL, &null_termination,
|
|
N_("terminate entries with NUL byte")),
|
|
OPT_CMDMODE('l', "long", &cmdmode, N_("include object size"),
|
|
MODE_LONG),
|
|
OPT_CMDMODE(0, "name-only", &cmdmode, N_("list only filenames"),
|
|
MODE_NAME_ONLY),
|
|
OPT_CMDMODE(0, "name-status", &cmdmode, N_("list only filenames"),
|
|
MODE_NAME_STATUS),
|
|
OPT_CMDMODE(0, "object-only", &cmdmode, N_("list only objects"),
|
|
MODE_OBJECT_ONLY),
|
|
OPT_BOOL(0, "full-name", &full_name, N_("use full path names")),
|
|
OPT_BOOL(0, "full-tree", &full_tree,
|
|
N_("list entire tree; not just current directory "
|
|
"(implies --full-name)")),
|
|
OPT_STRING_F(0, "format", &options.format, N_("format"),
|
|
N_("format to use for the output"),
|
|
PARSE_OPT_NONEG),
|
|
OPT__ABBREV(&options.abbrev),
|
|
OPT_END()
|
|
};
|
|
struct ls_tree_cmdmode_to_fmt *m2f = ls_tree_cmdmode_format;
|
|
struct object_context obj_context = {0};
|
|
int ret;
|
|
|
|
repo_config(the_repository, git_default_config, NULL);
|
|
|
|
argc = parse_options(argc, argv, prefix, ls_tree_options,
|
|
ls_tree_usage, 0);
|
|
options.null_termination = null_termination;
|
|
|
|
if (full_tree)
|
|
prefix = NULL;
|
|
options.prefix = full_name ? NULL : prefix;
|
|
|
|
/*
|
|
* We wanted to detect conflicts between --name-only and
|
|
* --name-status, but once we're done with that subsequent
|
|
* code should only need to check the primary name.
|
|
*/
|
|
if (cmdmode == MODE_NAME_STATUS)
|
|
cmdmode = MODE_NAME_ONLY;
|
|
|
|
/* -d -r should imply -t, but -d by itself should not have to. */
|
|
if ( (LS_TREE_ONLY|LS_RECURSIVE) ==
|
|
((LS_TREE_ONLY|LS_RECURSIVE) & options.ls_options))
|
|
options.ls_options |= LS_SHOW_TREES;
|
|
|
|
if (options.format && cmdmode)
|
|
usage_msg_opt(
|
|
_("--format can't be combined with other format-altering options"),
|
|
ls_tree_usage, ls_tree_options);
|
|
if (argc < 1)
|
|
usage_with_options(ls_tree_usage, ls_tree_options);
|
|
if (get_oid_with_context(the_repository, argv[0],
|
|
GET_OID_HASH_ANY, &oid,
|
|
&obj_context))
|
|
die("Not a valid object name %s", argv[0]);
|
|
|
|
/*
|
|
* show_recursive() rolls its own matching code and is
|
|
* generally ignorant of 'struct pathspec'. The magic mask
|
|
* cannot be lifted until it is converted to use
|
|
* match_pathspec() or tree_entry_interesting()
|
|
*/
|
|
parse_pathspec(&options.pathspec, PATHSPEC_ALL_MAGIC &
|
|
~(PATHSPEC_FROMTOP | PATHSPEC_LITERAL),
|
|
PATHSPEC_PREFER_CWD,
|
|
prefix, argv + 1);
|
|
for (i = 0; i < options.pathspec.nr; i++)
|
|
options.pathspec.items[i].nowildcard_len = options.pathspec.items[i].len;
|
|
options.pathspec.has_wildcard = 0;
|
|
tree = parse_tree_indirect(&oid);
|
|
if (!tree)
|
|
die("not a tree object");
|
|
/*
|
|
* The generic show_tree_fmt() is slower than show_tree(), so
|
|
* take the fast path if possible.
|
|
*/
|
|
while (m2f) {
|
|
if (!m2f->fmt) {
|
|
fn = options.format ? show_tree_fmt : show_tree_default;
|
|
} else if (options.format && !strcmp(options.format, m2f->fmt)) {
|
|
cmdmode = m2f->mode;
|
|
fn = m2f->fn;
|
|
} else if (!options.format && cmdmode == m2f->mode) {
|
|
fn = m2f->fn;
|
|
} else {
|
|
m2f++;
|
|
continue;
|
|
}
|
|
break;
|
|
}
|
|
|
|
ret = !!read_tree(the_repository, tree, &options.pathspec, fn, &options);
|
|
clear_pathspec(&options.pathspec);
|
|
object_context_release(&obj_context);
|
|
return ret;
|
|
}
|