@@ -10,7 +10,7 @@ SYNOPSIS
--------
[verse]
'git ls-tree' [-d] [-r] [-t] [-l] [-z]
- [--name-only] [--name-status] [--full-name] [--full-tree] [--abbrev[=<n>]]
+ [--name-only] [--name-status] [--object-only] [--full-name] [--full-tree] [--abbrev[=<n>]]
<tree-ish> [<path>...]
DESCRIPTION
@@ -59,6 +59,11 @@ OPTIONS
--name-only::
--name-status::
List only filenames (instead of the "long" output), one per line.
+ Cannot be combined with `--object-only`.
+
+--object-only::
+ List only names of the objects, one per line. Cannot be combined
+ with `--name-only` or `--name-status`.
--abbrev[=<n>]::
Instead of showing the full 40-byte hexadecimal object
@@ -16,21 +16,38 @@
static int line_termination = '\n';
#define LS_RECURSIVE 1
-#define LS_TREE_ONLY 2
-#define LS_SHOW_TREES 4
-#define LS_NAME_ONLY 8
-#define LS_SHOW_SIZE 16
+#define LS_TREE_ONLY (1 << 1)
+#define LS_SHOW_TREES (1 << 2)
+#define LS_NAME_ONLY (1 << 3)
+#define LS_SHOW_SIZE (1 << 4)
+#define LS_OBJECT_ONLY (1 << 5)
static int abbrev;
static int ls_options;
static struct pathspec pathspec;
static int chomp_prefix;
static const char *ls_tree_prefix;
+static unsigned int shown_bits;
+#define SHOW_FILE_NAME 1
+#define SHOW_SIZE (1 << 1)
+#define SHOW_OBJECT_NAME (1 << 2)
+#define SHOW_TYPE (1 << 3)
+#define SHOW_MODE (1 << 4)
+#define SHOW_DEFAULT 29 /* 11101 size is not shown to output by default */
static const char * const ls_tree_usage[] = {
N_("git ls-tree [<options>] <tree-ish> [<path>...]"),
NULL
};
+enum {
+ MODE_UNSPECIFIED = 0,
+ MODE_NAME_ONLY,
+ MODE_OBJECT_ONLY,
+ MODE_LONG,
+};
+
+static int cmdmode = MODE_UNSPECIFIED;
+
static int show_recursive(const char *base, int baselen, const char *pathname)
{
int i;
@@ -66,6 +83,7 @@ static int show_tree(const struct object_id *oid, struct strbuf *base,
{
int retval = 0;
int baselen;
+ int interspace = 0;
const char *type = blob_type;
if (S_ISGITLINK(mode)) {
@@ -74,8 +92,8 @@ static int show_tree(const struct object_id *oid, struct strbuf *base,
*
* Something similar to this incomplete example:
*
- if (show_subprojects(base, baselen, pathname))
- retval = READ_TREE_RECURSIVE;
+ * if (show_subprojects(base, baselen, pathname))
+ * retval = READ_TREE_RECURSIVE;
*
*/
type = commit_type;
@@ -90,35 +108,73 @@ static int show_tree(const struct object_id *oid, struct strbuf *base,
else if (ls_options & LS_TREE_ONLY)
return 0;
- if (!(ls_options & LS_NAME_ONLY)) {
- if (ls_options & LS_SHOW_SIZE) {
- char size_text[24];
- if (!strcmp(type, blob_type)) {
- unsigned long size;
- if (oid_object_info(the_repository, 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,
- find_unique_abbrev(oid, abbrev),
- size_text);
+ if (shown_bits & SHOW_MODE) {
+ printf("%06o", mode);
+ interspace = 1;
+ }
+ if (shown_bits & SHOW_TYPE) {
+ printf("%s%s", interspace ? " " : "", type);
+ interspace = 1;
+ }
+ if (shown_bits & SHOW_OBJECT_NAME) {
+ printf("%s%s", interspace ? " " : "",
+ find_unique_abbrev(oid, abbrev));
+ if (!(shown_bits ^ SHOW_OBJECT_NAME))
+ goto LINE_FINISH;
+ interspace = 1;
+ }
+ if (shown_bits & SHOW_SIZE) {
+ char size_text[24];
+ if (!strcmp(type, blob_type)) {
+ unsigned long size;
+ if (oid_object_info(the_repository, oid, &size) == OBJ_BAD)
+ xsnprintf(size_text, sizeof(size_text), "BAD");
+ else
+ xsnprintf(size_text, sizeof(size_text),
+ "%"PRIuMAX, (uintmax_t)size);
} else
- printf("%06o %s %s\t", mode, type,
- find_unique_abbrev(oid, abbrev));
+ xsnprintf(size_text, sizeof(size_text), "-");
+ printf("%s%7s", interspace ? " " : "", size_text);
+ interspace = 1;
+ }
+ if (shown_bits & SHOW_FILE_NAME) {
+ if (interspace)
+ printf("\t");
+ baselen = base->len;
+ strbuf_addstr(base, pathname);
+ write_name_quoted_relative(base->buf,
+ chomp_prefix ? ls_tree_prefix : NULL,
+ stdout,
+ line_termination
+ ? CQ_NO_TERMINATOR_C_QUOTED
+ : CQ_NO_TERMINATOR_AS_IS);
+ strbuf_setlen(base, baselen);
}
- baselen = base->len;
- strbuf_addstr(base, pathname);
- write_name_quoted_relative(base->buf,
- chomp_prefix ? ls_tree_prefix : NULL,
- stdout, line_termination);
- strbuf_setlen(base, baselen);
+
+LINE_FINISH:
+ putchar(line_termination);
return retval;
}
+static int parse_shown_fields(void)
+{
+ if (cmdmode == MODE_NAME_ONLY) {
+ shown_bits = SHOW_FILE_NAME;
+ return 0;
+ }
+ if (cmdmode == MODE_OBJECT_ONLY) {
+ shown_bits = SHOW_OBJECT_NAME;
+ return 0;
+ }
+ if (!ls_options || (ls_options & LS_RECURSIVE)
+ || (ls_options & LS_SHOW_TREES)
+ || (ls_options & LS_TREE_ONLY))
+ shown_bits = SHOW_DEFAULT;
+ if (cmdmode == MODE_LONG)
+ shown_bits = SHOW_DEFAULT | SHOW_SIZE;
+ return 1;
+}
+
int cmd_ls_tree(int argc, const char **argv, const char *prefix)
{
struct object_id oid;
@@ -133,12 +189,14 @@ int cmd_ls_tree(int argc, const char **argv, const char *prefix)
LS_SHOW_TREES),
OPT_SET_INT('z', NULL, &line_termination,
N_("terminate entries with NUL byte"), 0),
- OPT_BIT('l', "long", &ls_options, N_("include object size"),
- LS_SHOW_SIZE),
- OPT_BIT(0, "name-only", &ls_options, N_("list only filenames"),
- LS_NAME_ONLY),
- OPT_BIT(0, "name-status", &ls_options, N_("list only filenames"),
- LS_NAME_ONLY),
+ 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_ONLY),
+ OPT_CMDMODE(0, "object-only", &cmdmode, N_("list only objects"),
+ MODE_OBJECT_ONLY),
OPT_SET_INT(0, "full-name", &chomp_prefix,
N_("use full path names"), 0),
OPT_BOOL(0, "full-tree", &full_tree,
@@ -169,6 +227,7 @@ int cmd_ls_tree(int argc, const char **argv, const char *prefix)
if (get_oid(argv[0], &oid))
die("Not a valid object name %s", argv[0]);
+ parse_shown_fields();
/*
* show_recursive() rolls its own matching code and is
* generally ignorant of 'struct pathspec'. The magic mask
@@ -340,12 +340,12 @@ void quote_two_c_style(struct strbuf *sb, const char *prefix, const char *path,
void write_name_quoted(const char *name, FILE *fp, int terminator)
{
- if (terminator) {
+ if (0 < terminator || terminator == CQ_NO_TERMINATOR_C_QUOTED)
quote_c_style(name, NULL, fp, 0);
- } else {
+ else
fputs(name, fp);
- }
- fputc(terminator, fp);
+ if (0 <= terminator)
+ fputc(terminator, fp);
}
void write_name_quoted_relative(const char *name, const char *prefix,
@@ -84,8 +84,27 @@ int unquote_c_style(struct strbuf *, const char *quoted, const char **endp);
#define CQUOTE_NODQ 01
size_t quote_c_style(const char *name, struct strbuf *, FILE *, unsigned);
void quote_two_c_style(struct strbuf *, const char *, const char *, unsigned);
+/*
+ * Write a name, typically a filename, followed by a terminator that
+ * separates it from what comes next.
+ * When terminator is NUL, the name is given as-is. Otherwise, the
+ * name is c-quoted, suitable for text output. HT and LF are typical
+ * values used for the terminator, but other positive values are possible.
+ *
+ * In addition to non-negative values two special values in terminator
+ * are possible.
+ *
+ * -1: show the name c-quoted, without adding any terminator.
+ * -2: show the name as-is, without adding any terminator.
+ */
+#define CQ_NO_TERMINATOR_C_QUOTED (-1)
+#define CQ_NO_TERMINATOR_AS_IS (-2)
void write_name_quoted(const char *name, FILE *, int terminator);
+/*
+ * Similar to the above, but the name is first made relative to the prefix
+ * before being shown.
+ */
void write_name_quoted_relative(const char *name, const char *prefix,
FILE *fp, int terminator);
@@ -22,4 +22,12 @@ test_expect_success 'ls-tree fails with non-zero exit code on broken tree' '
test_must_fail git ls-tree -r HEAD
'
+test_expect_success 'usage: incompatible options: --name-status with --long' '
+ test_expect_code 129 git ls-tree --long --name-status
+'
+
+test_expect_success 'usage: incompatible options: --name-only with --long' '
+ test_expect_code 129 git ls-tree --long --name-only
+'
+
test_done
new file mode 100755
@@ -0,0 +1,51 @@
+#!/bin/sh
+
+test_description='git ls-tree objects handling.'
+
+. ./test-lib.sh
+
+test_expect_success 'setup' '
+ test_commit A &&
+ test_commit B &&
+ mkdir -p C &&
+ test_commit C/D.txt &&
+ find *.txt path* \( -type f -o -type l \) -print |
+ xargs git update-index --add &&
+ tree=$(git write-tree) &&
+ echo $tree
+'
+
+test_expect_success 'usage: --object-only' '
+ git ls-tree --object-only $tree >current &&
+ git ls-tree $tree >result &&
+ cut -f1 result | cut -d " " -f3 >expected &&
+ test_cmp current expected
+'
+
+test_expect_success 'usage: --object-only with -r' '
+ git ls-tree --object-only -r $tree >current &&
+ git ls-tree -r $tree >result &&
+ cut -f1 result | cut -d " " -f3 >expected &&
+ test_cmp current expected
+'
+
+test_expect_success 'usage: --object-only with --abbrev' '
+ git ls-tree --object-only --abbrev=6 $tree >current &&
+ git ls-tree --abbrev=6 $tree >result &&
+ cut -f1 result | cut -d " " -f3 >expected &&
+ test_cmp current expected
+'
+
+test_expect_success 'usage: incompatible options: --name-only with --object-only' '
+ test_expect_code 129 git ls-tree --object-only --name-only
+'
+
+test_expect_success 'usage: incompatible options: --name-status with --object-only' '
+ test_expect_code 129 git ls-tree --object-only --name-status
+'
+
+test_expect_success 'usage: incompatible options: --long with --object-only' '
+ test_expect_code 129 git ls-tree --object-only --long
+'
+
+test_done
We usually pipe the output from `git ls-trees` to tools like `sed` or `cut` when we only want to extract some fields. When we want only the pathname component, we can pass `--name-only` option to omit such a pipeline, but there are no options for extracting other fields. Teach the "--object-only" option to the command to only show the object name. This option cannot be used together with "--name-only" or "--long" (mutually exclusive). Signed-off-by: Teng Long <dyroneteng@gmail.com> --- Documentation/git-ls-tree.txt | 7 +- builtin/ls-tree.c | 131 ++++++++++++++++++++++++---------- quote.c | 8 +-- quote.h | 19 +++++ t/t3103-ls-tree-misc.sh | 8 +++ t/t3104-ls-tree-oid.sh | 51 +++++++++++++ 6 files changed, 183 insertions(+), 41 deletions(-) create mode 100755 t/t3104-ls-tree-oid.sh