Subject: [PATCH] ls-tree: default <tree-ish> to HEAD

From: "Ryan Williams via GitGitGadget" <[email-masked]>
Date: Mon, 07 Aug 2023 12:49:17 +0000

From: Ryan Williams <[email-masked]>

When no positional arguments are passed to `git ls-tree`, it currently
prints "usage" info to stderr and exits with code 129. A more intuitive
default would be to operate on the `HEAD` commit's tree (similarly to
`git show`, `git log`, and possibly others).

This patch updates `git ls-tree [options...]` to operate identically to
`git ls-tree [options...] HEAD`, updates the docs to reflect that
`<tree-ish>` is optional (and `[path...]` args can only be provided if a
`<tree-ish>` is explicitly provided first), and duplicates some existing
test cases to omit the `HEAD` argument to `ls-tree` (verifying that
`ls-tree` behaves identically whether `HEAD` is provided or not).

Signed-off-by: Ryan Williams <[email-masked]>
---
    ls-tree: default to HEAD

Published-As: https://github.com/gitgitgadget/git/releases/tag/pr-1566%2Frunsascoded%2Fls-tree-head-v1
Fetch-It-Via: git fetch https://github.com/gitgitgadget/git pr-1566/runsascoded/ls-tree-head-v1
Pull-Request: https://github.com/gitgitgadget/git/pull/1566

 Documentation/git-ls-tree.txt |  6 +++---
 builtin/ls-tree.c             | 11 ++++++++---
 t/t3105-ls-tree-output.sh     |  7 ++++++-
 3 files changed, 17 insertions(+), 7 deletions(-)

diff --git a/Documentation/git-ls-tree.txt b/Documentation/git-ls-tree.txt
index 6572095d8d6..6211d630974 100644
--- a/Documentation/git-ls-tree.txt
+++ b/Documentation/git-ls-tree.txt
@@ -11,7 +11,7 @@ SYNOPSIS
 [verse]
 'git ls-tree' [-d] [-r] [-t] [-l] [-z]
 	    [--name-only] [--name-status] [--object-only] [--full-name] [--full-tree] [--abbrev[=<n>]] [--format=<format>]
-	    <tree-ish> [<path>...]
+	    [<tree-ish> [<path>...]]
 
 DESCRIPTION
 -----------
@@ -36,7 +36,7 @@ in the current working directory.  Note that:
 OPTIONS
 -------
 <tree-ish>::
-	Id of a tree-ish.
+	Id of a tree-ish. If omitted, defaults to "HEAD".
 
 -d::
 	Show only the named tree entry itself, not its children.
@@ -139,7 +139,7 @@ which is able to interpolate different fields using a `%(fieldname)` notation.
 For example, if you only care about the "objectname" and "path" fields, you
 can execute with a specific "--format" like
 
-	git ls-tree --format='%(objectname) %(path)' <tree-ish>
+	git ls-tree --format='%(objectname) %(path)' [<tree-ish>]
 
 FIELD NAMES
 -----------
diff --git a/builtin/ls-tree.c b/builtin/ls-tree.c
index f558db5f3b8..b1e337ccde9 100644
--- a/builtin/ls-tree.c
+++ b/builtin/ls-tree.c
@@ -18,7 +18,7 @@
 #include "pathspec.h"
 
 static const char * const ls_tree_usage[] = {
-	N_("git ls-tree [<options>] <tree-ish> [<path>...]"),
+	N_("git ls-tree [<options>] [<tree-ish> [<path>...]]"),
 	NULL
 };
 
@@ -377,6 +377,8 @@ int cmd_ls_tree(int argc, const char **argv, const char *prefix)
 	};
 	struct ls_tree_cmdmode_to_fmt *m2f = ls_tree_cmdmode_format;
 	int ret;
+	/* If no positional args were passed, default <tree-ish> to HEAD. */
+	const char *fallback_args[] = { "HEAD", NULL };
 
 	git_config(git_default_config, NULL);
 
@@ -405,8 +407,11 @@ int cmd_ls_tree(int argc, const char **argv, const char *prefix)
 		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 (argc < 1) {
+		/* `git ls-tree [flags...]` -> `git ls-tree [flags...] HEAD`. */
+		argv = fallback_args;
+		argc = 1;
+	}
 	if (repo_get_oid(the_repository, argv[0], &oid))
 		die("Not a valid object name %s", argv[0]);
 
diff --git a/t/t3105-ls-tree-output.sh b/t/t3105-ls-tree-output.sh
index ce2391e28be..cb05529c0ad 100755
--- a/t/t3105-ls-tree-output.sh
+++ b/t/t3105-ls-tree-output.sh
@@ -26,11 +26,16 @@ test_ls_tree_format_mode_output () {
 		local mode="$1" &&
 		shift &&
 
-		test_expect_success "'ls-tree $opts${mode:+ $mode}' output" '
+		test_expect_success "'ls-tree ${mode:+$mode }$opts' output" '
 			git ls-tree ${mode:+$mode }$opts HEAD >actual &&
 			test_cmp expect actual
 		'
 
+		test_expect_success "'ls-tree ${mode:+$mode }$opts' (default HEAD) output" '
+			git ls-tree ${mode:+$mode }$opts >actual &&
+			test_cmp expect actual
+		'
+
 		case "$opts" in
 		--full-tree)
 			test_expect_success "'ls-tree $opts${mode:+ $mode}' output (via subdir, fails)" '

base-commit: ac83bc5054c2ac489166072334b4147ce6d0fccb
-- 
gitgitgadget