public inbox for git@vger.kernel.org
 help / color / mirror / Atom feed
* [PATCH 0/3] Expand and enhance git-last-modified(1) documentation
@ 2025-11-26  6:09 Toon Claes
  2025-11-26  6:09 ` [PATCH 1/3] last-modified: handle and document NUL termination Toon Claes
                   ` (3 more replies)
  0 siblings, 4 replies; 36+ messages in thread
From: Toon Claes @ 2025-11-26  6:09 UTC (permalink / raw)
  To: git; +Cc: Toon Claes

Option `-z` and `--max-depth` are not documented in git-last-modified(1)
while they are pretty crucial. In these patches documentation is added
in the man page and the `-h` output.

---
Toon Claes (3):
      last-modified: handle and document NUL termination
      last-modified: document option --max-depth
      last-modified: better document how depth in handled

 Documentation/git-last-modified.adoc | 71 +++++++++++++++++++++++++++++++++++-
 builtin/last-modified.c              | 23 ++++++++++--
 2 files changed, 90 insertions(+), 4 deletions(-)



---
base-commit: 6ab38b7e9cc7adafc304f3204616a4debd49c6e9
change-id: 20251114-toon-last-modified-zzzz-af9c1be74fc4


^ permalink raw reply	[flat|nested] 36+ messages in thread

* [PATCH 1/3] last-modified: handle and document NUL termination
  2025-11-26  6:09 [PATCH 0/3] Expand and enhance git-last-modified(1) documentation Toon Claes
@ 2025-11-26  6:09 ` Toon Claes
  2025-11-26 13:03   ` Karthik Nayak
  2025-11-26 16:57   ` Junio C Hamano
  2025-11-26  6:09 ` [PATCH 2/3] last-modified: document option --max-depth Toon Claes
                   ` (2 subsequent siblings)
  3 siblings, 2 replies; 36+ messages in thread
From: Toon Claes @ 2025-11-26  6:09 UTC (permalink / raw)
  To: git; +Cc: Toon Claes

When option `-z` is provided to git-last-modified(1), each line is
separated with a NUL instead of a newline. Document this properly and
handle parsing of the option in the builtin itself.

Signed-off-by: Toon Claes <toon@iotcl.com>
---
 Documentation/git-last-modified.adoc | 21 ++++++++++++++++++++-
 builtin/last-modified.c              | 13 ++++++++++---
 2 files changed, 30 insertions(+), 4 deletions(-)

diff --git a/Documentation/git-last-modified.adoc b/Documentation/git-last-modified.adoc
index 602843e095..cd4a5040b0 100644
--- a/Documentation/git-last-modified.adoc
+++ b/Documentation/git-last-modified.adoc
@@ -9,7 +9,7 @@ git-last-modified - EXPERIMENTAL: Show when files were last modified
 SYNOPSIS
 --------
 [synopsis]
-git last-modified [--recursive] [--show-trees] [<revision-range>] [[--] <path>...]
+git last-modified [--recursive] [--show-trees] [-z] [<revision-range>] [[--] <path>...]
 
 DESCRIPTION
 -----------
@@ -32,6 +32,9 @@ OPTIONS
 	Show tree entries even when recursing into them. It has no effect
 	without `--recursive`.
 
+`-z`::
+	Terminate each line with a _NUL_ rather than a newline.
+
 `<revision-range>`::
 	Only traverse commits in the specified revision range. When no
 	`<revision-range>` is specified, it defaults to `HEAD` (i.e. the whole
@@ -44,6 +47,22 @@ OPTIONS
 	Without an optional path parameter, all files and subdirectories
 	in path traversal the are included in the output.
 
+OUTPUT
+------
+
+The output is in the format:
+
+------------
+ <oid> TAB <path> LF
+------------
+
+If a path contains any special characters, the path is C-style quoted. To
+avoid quoting, pass option `-z` to terminate each line with a NUL.
+
+------------
+ <oid> TAB <path> NUL
+------------
+
 SEE ALSO
 --------
 linkgit:git-blame[1],
diff --git a/builtin/last-modified.c b/builtin/last-modified.c
index b0ecbdc540..9206bbdc1d 100644
--- a/builtin/last-modified.c
+++ b/builtin/last-modified.c
@@ -23,6 +23,10 @@
 #define PARENT1 (1u<<16) /* used instead of SEEN */
 #define PARENT2 (1u<<17) /* used instead of BOTTOM, BOUNDARY */
 
+#define LAST_MODIFIED_INIT { \
+	.line_termination = '\n', \
+}
+
 struct last_modified_entry {
 	struct hashmap_entry hashent;
 	struct object_id oid;
@@ -55,6 +59,7 @@ struct last_modified {
 	struct rev_info rev;
 	bool recursive;
 	bool show_trees;
+	int line_termination;
 
 	const char **all_paths;
 	size_t all_paths_nr;
@@ -165,7 +170,7 @@ static void last_modified_emit(struct last_modified *lm,
 		putchar('^');
 	printf("%s\t", oid_to_hex(&commit->object.oid));
 
-	if (lm->rev.diffopt.line_termination)
+	if (lm->line_termination)
 		write_name_quoted(path, stdout, '\n');
 	else
 		printf("%s%c", path, '\0');
@@ -507,10 +512,10 @@ int cmd_last_modified(int argc, const char **argv, const char *prefix,
 		      struct repository *repo)
 {
 	int ret;
-	struct last_modified lm = { 0 };
+	struct last_modified lm = LAST_MODIFIED_INIT;
 
 	const char * const last_modified_usage[] = {
-		N_("git last-modified [--recursive] [--show-trees] "
+		N_("git last-modified [--recursive] [--show-trees] [-z] "
 		   "[<revision-range>] [[--] <path>...]"),
 		NULL
 	};
@@ -520,6 +525,8 @@ int cmd_last_modified(int argc, const char **argv, const char *prefix,
 			 N_("recurse into subtrees")),
 		OPT_BOOL('t', "show-trees", &lm.show_trees,
 			 N_("show tree entries when recursing into subtrees")),
+		OPT_SET_INT('z', NULL, &lm.line_termination,
+			N_("lines are separated with NUL character"), '\0'),
 		OPT_END()
 	};
 

-- 
2.51.2


^ permalink raw reply related	[flat|nested] 36+ messages in thread

* [PATCH 2/3] last-modified: document option --max-depth
  2025-11-26  6:09 [PATCH 0/3] Expand and enhance git-last-modified(1) documentation Toon Claes
  2025-11-26  6:09 ` [PATCH 1/3] last-modified: handle and document NUL termination Toon Claes
@ 2025-11-26  6:09 ` Toon Claes
  2025-11-26 13:31   ` Karthik Nayak
  2025-11-26 19:49   ` Junio C Hamano
  2025-11-26  6:09 ` [PATCH 3/3] last-modified: better document how depth in handled Toon Claes
  2026-01-16 13:22 ` [PATCH v2 0/5] Change git-last-modified(1) default behavior and add documentation Toon Claes
  3 siblings, 2 replies; 36+ messages in thread
From: Toon Claes @ 2025-11-26  6:09 UTC (permalink / raw)
  To: git; +Cc: Toon Claes

Option --max-depth is supported by git-last-modified(1), because it was
added to the diff machinery in a1dfa5448d (diff: teach tree-diff a
max-depth parameter, 2025-08-07).

This option is useful for everyday use of the git-last-modified(1)
command, so document it's existence in the man page and `-h` output.

Signed-off-by: Toon Claes <toon@iotcl.com>
---
 Documentation/git-last-modified.adoc |  9 ++++++++-
 builtin/last-modified.c              | 12 +++++++++++-
 2 files changed, 19 insertions(+), 2 deletions(-)

diff --git a/Documentation/git-last-modified.adoc b/Documentation/git-last-modified.adoc
index cd4a5040b0..8409daebe9 100644
--- a/Documentation/git-last-modified.adoc
+++ b/Documentation/git-last-modified.adoc
@@ -9,7 +9,8 @@ git-last-modified - EXPERIMENTAL: Show when files were last modified
 SYNOPSIS
 --------
 [synopsis]
-git last-modified [--recursive] [--show-trees] [-z] [<revision-range>] [[--] <path>...]
+git last-modified [--recursive] [--show-trees] [--max-depth=<depth>] [-z]
+	[<revision-range>] [[--] <path>...]
 
 DESCRIPTION
 -----------
@@ -32,6 +33,12 @@ OPTIONS
 	Show tree entries even when recursing into them. It has no effect
 	without `--recursive`.
 
+`--max-depth=<depth>`::
+	For each pathspec given on the command line, descend at most `<depth>`
+	levels of directories. A negative value means no limit.
+	Setting a positive value implies `--recursive`.
+	Cannot be combined with wildcards in the pathspec.
+
 `-z`::
 	Terminate each line with a _NUL_ rather than a newline.
 
diff --git a/builtin/last-modified.c b/builtin/last-modified.c
index 9206bbdc1d..ccb7ff66d4 100644
--- a/builtin/last-modified.c
+++ b/builtin/last-modified.c
@@ -25,6 +25,7 @@
 
 #define LAST_MODIFIED_INIT { \
 	.line_termination = '\n', \
+	.max_depth = -1, \
 }
 
 struct last_modified_entry {
@@ -60,6 +61,7 @@ struct last_modified {
 	bool recursive;
 	bool show_trees;
 	int line_termination;
+	int max_depth;
 
 	const char **all_paths;
 	size_t all_paths_nr;
@@ -487,6 +489,12 @@ static int last_modified_init(struct last_modified *lm, struct repository *r,
 	lm->rev.diffopt.flags.recursive = lm->recursive;
 	lm->rev.diffopt.flags.tree_in_recursive = lm->show_trees;
 
+	if (lm->max_depth >= 0) {
+		lm->rev.diffopt.flags.recursive = 1;
+		lm->rev.diffopt.max_depth = lm->max_depth;
+		lm->rev.diffopt.max_depth_valid = 1;
+	}
+
 	argc = setup_revisions(argc, argv, &lm->rev, NULL);
 	if (argc > 1) {
 		error(_("unknown last-modified argument: %s"), argv[1]);
@@ -515,7 +523,7 @@ int cmd_last_modified(int argc, const char **argv, const char *prefix,
 	struct last_modified lm = LAST_MODIFIED_INIT;
 
 	const char * const last_modified_usage[] = {
-		N_("git last-modified [--recursive] [--show-trees] [-z] "
+		N_("git last-modified [--recursive] [--show-trees] [--max-depth=<depth>] [-z] "
 		   "[<revision-range>] [[--] <path>...]"),
 		NULL
 	};
@@ -525,6 +533,8 @@ int cmd_last_modified(int argc, const char **argv, const char *prefix,
 			 N_("recurse into subtrees")),
 		OPT_BOOL('t', "show-trees", &lm.show_trees,
 			 N_("show tree entries when recursing into subtrees")),
+		OPT_INTEGER_F(0, "max-depth", &lm.max_depth,
+			N_("maximum tree depth to recurse"), PARSE_OPT_NONEG),
 		OPT_SET_INT('z', NULL, &lm.line_termination,
 			N_("lines are separated with NUL character"), '\0'),
 		OPT_END()

-- 
2.51.2


^ permalink raw reply related	[flat|nested] 36+ messages in thread

* [PATCH 3/3] last-modified: better document how depth in handled
  2025-11-26  6:09 [PATCH 0/3] Expand and enhance git-last-modified(1) documentation Toon Claes
  2025-11-26  6:09 ` [PATCH 1/3] last-modified: handle and document NUL termination Toon Claes
  2025-11-26  6:09 ` [PATCH 2/3] last-modified: document option --max-depth Toon Claes
@ 2025-11-26  6:09 ` Toon Claes
  2025-11-26 17:38   ` Eric Sunshine
  2025-12-01 10:32   ` Patrick Steinhardt
  2026-01-16 13:22 ` [PATCH v2 0/5] Change git-last-modified(1) default behavior and add documentation Toon Claes
  3 siblings, 2 replies; 36+ messages in thread
From: Toon Claes @ 2025-11-26  6:09 UTC (permalink / raw)
  To: git; +Cc: Toon Claes

By default git-last-modified(1) only shows information about paths at
the root level. This can be confusing. Clarify the command's behavior in
the documentation.

Signed-off-by: Toon Claes <toon@iotcl.com>
---
 Documentation/git-last-modified.adoc | 43 ++++++++++++++++++++++++++++++++++++
 1 file changed, 43 insertions(+)

diff --git a/Documentation/git-last-modified.adoc b/Documentation/git-last-modified.adoc
index 8409daebe9..36f72954a5 100644
--- a/Documentation/git-last-modified.adoc
+++ b/Documentation/git-last-modified.adoc
@@ -27,6 +27,7 @@ OPTIONS
 `--recursive`::
 	Instead of showing tree entries, step into subtrees and show all entries
 	inside them recursively.
+	See the section "NOTES ABOUT DEPTH" below for more details.
 
 `-t`::
 `--show-trees`::
@@ -38,6 +39,7 @@ OPTIONS
 	levels of directories. A negative value means no limit.
 	Setting a positive value implies `--recursive`.
 	Cannot be combined with wildcards in the pathspec.
+	See the section "NOTES ABOUT DEPTH" below for more details.
 
 `-z`::
 	Terminate each line with a _NUL_ rather than a newline.
@@ -70,6 +72,47 @@ avoid quoting, pass option `-z` to terminate each line with a NUL.
  <oid> TAB <path> NUL
 ------------
 
+NOTES ABOUT DEPTH
+-----------------
+
+By default this command only shows information about paths at the root level.
+When a path that lives in a subtree is provided, information about the top-level
+subtree is printed. For example:
+
+------------
+$ git last-modified -- sub/file
+
+abcd1234abcd1234abcd1234abcd1234abcd1234 sub
+------------
+
+To get details about the exact path in a subtree, add option `--recursive`:
+
+------------
+$ git last-modified --recursive -- sub/file
+
+5678abca5678abca5678abca5678abca5678abca sub/file
+------------
+
+This comes with a downside. When the path provided is a tree itself, with
+option `--recursive` all paths in that subtree are printed too:
+
+------------
+$ git last-modified --recursive -- sub/subsub
+
+1234cdef1234cdef1234cdef1234cdef1234cdef sub/subsub/a
+3456cdef3456cdef3456cdef3456cdef3456cdef sub/subsub/b
+5678abcd5678abcd5678abcd5678abcd5678abcd sub/subsub/c
+------------
+
+To stop this command from traversing deeper into trees, add option
+`--max-depth=0`:
+
+------------
+$ git last-modified --recursive --max-depth=0 -- sub/subsub
+
+3456def3456def3456def3456def3456def3456b sub/subsub
+------------
+
 SEE ALSO
 --------
 linkgit:git-blame[1],

-- 
2.51.2


^ permalink raw reply related	[flat|nested] 36+ messages in thread

* Re: [PATCH 1/3] last-modified: handle and document NUL termination
  2025-11-26  6:09 ` [PATCH 1/3] last-modified: handle and document NUL termination Toon Claes
@ 2025-11-26 13:03   ` Karthik Nayak
  2025-11-26 16:57   ` Junio C Hamano
  1 sibling, 0 replies; 36+ messages in thread
From: Karthik Nayak @ 2025-11-26 13:03 UTC (permalink / raw)
  To: Toon Claes, git

[-- Attachment #1: Type: text/plain, Size: 4173 bytes --]

Toon Claes <toon@iotcl.com> writes:

> When option `-z` is provided to git-last-modified(1), each line is
> separated with a NUL instead of a newline.

This line make it seem like the option already exists..

> Document this properly and
> handle parsing of the option in the builtin itself.
>

But this line says we add the option now. Perhaps this should be
clearer.

> Signed-off-by: Toon Claes <toon@iotcl.com>
> ---
>  Documentation/git-last-modified.adoc | 21 ++++++++++++++++++++-
>  builtin/last-modified.c              | 13 ++++++++++---
>  2 files changed, 30 insertions(+), 4 deletions(-)
>
> diff --git a/Documentation/git-last-modified.adoc b/Documentation/git-last-modified.adoc
> index 602843e095..cd4a5040b0 100644
> --- a/Documentation/git-last-modified.adoc
> +++ b/Documentation/git-last-modified.adoc
> @@ -9,7 +9,7 @@ git-last-modified - EXPERIMENTAL: Show when files were last modified
>  SYNOPSIS
>  --------
>  [synopsis]
> -git last-modified [--recursive] [--show-trees] [<revision-range>] [[--] <path>...]
> +git last-modified [--recursive] [--show-trees] [-z] [<revision-range>] [[--] <path>...]
>

This is a bit long now, let's wrap it.

>  DESCRIPTION
>  -----------
> @@ -32,6 +32,9 @@ OPTIONS
>  	Show tree entries even when recursing into them. It has no effect
>  	without `--recursive`.
>
> +`-z`::
> +	Terminate each line with a _NUL_ rather than a newline.
> +

Nit: perhaps it is just me, but it would nicer to read if it said 'a
_NUL_ character'

>  `<revision-range>`::
>  	Only traverse commits in the specified revision range. When no
>  	`<revision-range>` is specified, it defaults to `HEAD` (i.e. the whole
> @@ -44,6 +47,22 @@ OPTIONS
>  	Without an optional path parameter, all files and subdirectories
>  	in path traversal the are included in the output.
>
> +OUTPUT
> +------
> +
> +The output is in the format:
> +
> +------------
> + <oid> TAB <path> LF
> +------------
> +
> +If a path contains any special characters, the path is C-style quoted. To
> +avoid quoting, pass option `-z` to terminate each line with a NUL.
> +
> +------------
> + <oid> TAB <path> NUL
> +------------
> +
>  SEE ALSO
>  --------
>  linkgit:git-blame[1],
> diff --git a/builtin/last-modified.c b/builtin/last-modified.c
> index b0ecbdc540..9206bbdc1d 100644
> --- a/builtin/last-modified.c
> +++ b/builtin/last-modified.c
> @@ -23,6 +23,10 @@
>  #define PARENT1 (1u<<16) /* used instead of SEEN */
>  #define PARENT2 (1u<<17) /* used instead of BOTTOM, BOUNDARY */
>
> +#define LAST_MODIFIED_INIT { \
> +	.line_termination = '\n', \
> +}
> +
>  struct last_modified_entry {
>  	struct hashmap_entry hashent;
>  	struct object_id oid;
> @@ -55,6 +59,7 @@ struct last_modified {
>  	struct rev_info rev;
>  	bool recursive;
>  	bool show_trees;
> +	int line_termination;
>

Wouldn't 'line_terminator' be a better name?

>  	const char **all_paths;
>  	size_t all_paths_nr;
> @@ -165,7 +170,7 @@ static void last_modified_emit(struct last_modified *lm,
>  		putchar('^');
>  	printf("%s\t", oid_to_hex(&commit->object.oid));
>
> -	if (lm->rev.diffopt.line_termination)
> +	if (lm->line_termination)

So it did exist before. But this was parsed as part of diff_options, why
make the change than?

>  		write_name_quoted(path, stdout, '\n');
>  	else
>  		printf("%s%c", path, '\0');
> @@ -507,10 +512,10 @@ int cmd_last_modified(int argc, const char **argv, const char *prefix,
>  		      struct repository *repo)
>  {
>  	int ret;
> -	struct last_modified lm = { 0 };
> +	struct last_modified lm = LAST_MODIFIED_INIT;
>
>  	const char * const last_modified_usage[] = {
> -		N_("git last-modified [--recursive] [--show-trees] "
> +		N_("git last-modified [--recursive] [--show-trees] [-z] "
>  		   "[<revision-range>] [[--] <path>...]"),
>  		NULL
>  	};
> @@ -520,6 +525,8 @@ int cmd_last_modified(int argc, const char **argv, const char *prefix,
>  			 N_("recurse into subtrees")),
>  		OPT_BOOL('t', "show-trees", &lm.show_trees,
>  			 N_("show tree entries when recursing into subtrees")),
> +		OPT_SET_INT('z', NULL, &lm.line_termination,
> +			N_("lines are separated with NUL character"), '\0'),
>  		OPT_END()
>  	};
>
>
> --
> 2.51.2

[-- Attachment #2: signature.asc --]
[-- Type: application/pgp-signature, Size: 690 bytes --]

^ permalink raw reply	[flat|nested] 36+ messages in thread

* Re: [PATCH 2/3] last-modified: document option --max-depth
  2025-11-26  6:09 ` [PATCH 2/3] last-modified: document option --max-depth Toon Claes
@ 2025-11-26 13:31   ` Karthik Nayak
  2026-01-16 12:13     ` Toon Claes
  2025-11-26 19:49   ` Junio C Hamano
  1 sibling, 1 reply; 36+ messages in thread
From: Karthik Nayak @ 2025-11-26 13:31 UTC (permalink / raw)
  To: Toon Claes, git

[-- Attachment #1: Type: text/plain, Size: 4159 bytes --]

Toon Claes <toon@iotcl.com> writes:

> Option --max-depth is supported by git-last-modified(1), because it was
> added to the diff machinery in a1dfa5448d (diff: teach tree-diff a
> max-depth parameter, 2025-08-07).
>

At this point, does it make more sense to link the respective sections
within 'Documentation/diff-options.adoc' as done by many other commands?
This would ensure that we don't have to repeat the documentation.

> This option is useful for everyday use of the git-last-modified(1)
> command, so document it's existence in the man page and `-h` output.
>
> Signed-off-by: Toon Claes <toon@iotcl.com>
> ---
>  Documentation/git-last-modified.adoc |  9 ++++++++-
>  builtin/last-modified.c              | 12 +++++++++++-
>  2 files changed, 19 insertions(+), 2 deletions(-)
>
> diff --git a/Documentation/git-last-modified.adoc b/Documentation/git-last-modified.adoc
> index cd4a5040b0..8409daebe9 100644
> --- a/Documentation/git-last-modified.adoc
> +++ b/Documentation/git-last-modified.adoc
> @@ -9,7 +9,8 @@ git-last-modified - EXPERIMENTAL: Show when files were last modified
>  SYNOPSIS
>  --------
>  [synopsis]
> -git last-modified [--recursive] [--show-trees] [-z] [<revision-range>] [[--] <path>...]
> +git last-modified [--recursive] [--show-trees] [--max-depth=<depth>] [-z]
> +	[<revision-range>] [[--] <path>...]
>
>  DESCRIPTION
>  -----------
> @@ -32,6 +33,12 @@ OPTIONS
>  	Show tree entries even when recursing into them. It has no effect
>  	without `--recursive`.
>
> +`--max-depth=<depth>`::
> +	For each pathspec given on the command line, descend at most `<depth>`
> +	levels of directories. A negative value means no limit.
> +	Setting a positive value implies `--recursive`.
> +	Cannot be combined with wildcards in the pathspec.
> +
>  `-z`::
>  	Terminate each line with a _NUL_ rather than a newline.
>
> diff --git a/builtin/last-modified.c b/builtin/last-modified.c
> index 9206bbdc1d..ccb7ff66d4 100644
> --- a/builtin/last-modified.c
> +++ b/builtin/last-modified.c
> @@ -25,6 +25,7 @@
>
>  #define LAST_MODIFIED_INIT { \
>  	.line_termination = '\n', \
> +	.max_depth = -1, \
>  }
>
>  struct last_modified_entry {
> @@ -60,6 +61,7 @@ struct last_modified {
>  	bool recursive;
>  	bool show_trees;
>  	int line_termination;
> +	int max_depth;
>

Should this be signed?

>  	const char **all_paths;
>  	size_t all_paths_nr;
> @@ -487,6 +489,12 @@ static int last_modified_init(struct last_modified *lm, struct repository *r,
>  	lm->rev.diffopt.flags.recursive = lm->recursive;
>  	lm->rev.diffopt.flags.tree_in_recursive = lm->show_trees;
>
> +	if (lm->max_depth >= 0) {
> +		lm->rev.diffopt.flags.recursive = 1;
> +		lm->rev.diffopt.max_depth = lm->max_depth;
> +		lm->rev.diffopt.max_depth_valid = 1;
> +	}
> +

Or if our goal is to actually handle them within the
'git-last-modified(1)' command, shouldn't we ensure we don't allow any
additional flags from being parsed as diffopt?

Currently other diffopts flags such as '--no-prefix', '--cc' and so on,
are parsed even if they don't affect the output of
'git-last-modified(1)'. Shouldn't we disallow such behavior?

>  	argc = setup_revisions(argc, argv, &lm->rev, NULL);
>  	if (argc > 1) {
>  		error(_("unknown last-modified argument: %s"), argv[1]);
> @@ -515,7 +523,7 @@ int cmd_last_modified(int argc, const char **argv, const char *prefix,
>  	struct last_modified lm = LAST_MODIFIED_INIT;
>
>  	const char * const last_modified_usage[] = {
> -		N_("git last-modified [--recursive] [--show-trees] [-z] "
> +		N_("git last-modified [--recursive] [--show-trees] [--max-depth=<depth>] [-z] "
>  		   "[<revision-range>] [[--] <path>...]"),
>  		NULL
>  	};
> @@ -525,6 +533,8 @@ int cmd_last_modified(int argc, const char **argv, const char *prefix,
>  			 N_("recurse into subtrees")),
>  		OPT_BOOL('t', "show-trees", &lm.show_trees,
>  			 N_("show tree entries when recursing into subtrees")),
> +		OPT_INTEGER_F(0, "max-depth", &lm.max_depth,
> +			N_("maximum tree depth to recurse"), PARSE_OPT_NONEG),
>  		OPT_SET_INT('z', NULL, &lm.line_termination,
>  			N_("lines are separated with NUL character"), '\0'),
>  		OPT_END()
>
> --
> 2.51.2

[-- Attachment #2: signature.asc --]
[-- Type: application/pgp-signature, Size: 690 bytes --]

^ permalink raw reply	[flat|nested] 36+ messages in thread

* Re: [PATCH 1/3] last-modified: handle and document NUL termination
  2025-11-26  6:09 ` [PATCH 1/3] last-modified: handle and document NUL termination Toon Claes
  2025-11-26 13:03   ` Karthik Nayak
@ 2025-11-26 16:57   ` Junio C Hamano
  2025-11-28 18:50     ` Toon Claes
  1 sibling, 1 reply; 36+ messages in thread
From: Junio C Hamano @ 2025-11-26 16:57 UTC (permalink / raw)
  To: Toon Claes; +Cc: git

Toon Claes <toon@iotcl.com> writes:

> When option `-z` is provided to git-last-modified(1), each line is
> separated with a NUL instead of a newline. Document this properly and
> handle parsing of the option in the builtin itself.

I think documenting does make sense, but it is not clear from the
description why it is better to handle the option in the builtin
itself, instead of letting the setup_revisions() take care of it.

Is it because after the command lets setup_revisions() to parse out
the revision range, the command does not really let the revision
machinery drive diffs and let it output anything (hence, even though
rev.diffopt.line_termination is set from the command line, the calling
builtin is the only one that pays attention, and the revision machinery
and the diff machinery called from there does not pay attention to it?

And assuming that this new division of labor between the revision
machinery and the subcommand makes sense (it needs to be explained
better), the updated code does make sense to me.

But it looks suboptimal.  See below.

> +#define LAST_MODIFIED_INIT { \
> +	.line_termination = '\n', \
> +}

You have to introduce such a non-zero initialization, only because
you pretend to accept _any_ byte here, and use it as the line
termination character.  If you were porting Git to ancient Macintosh,
you could set this to '\r' and it would follow their text file
convention there ;-)

But ...

>  struct last_modified_entry {
>  	struct hashmap_entry hashent;
>  	struct object_id oid;
> @@ -55,6 +59,7 @@ struct last_modified {
>  	struct rev_info rev;
>  	bool recursive;
>  	bool show_trees;
> +	int line_termination;
>  
>  	const char **all_paths;
>  	size_t all_paths_nr;
> @@ -165,7 +170,7 @@ static void last_modified_emit(struct last_modified *lm,
>  		putchar('^');
>  	printf("%s\t", oid_to_hex(&commit->object.oid));
>  
> -	if (lm->rev.diffopt.line_termination)
> +	if (lm->line_termination)
>  		write_name_quoted(path, stdout, '\n');
>  	else
>  		printf("%s%c", path, '\0');

... you use hardcoded '\n' here, without allowing the value of
line_termination to affect the termination character.

This is way suboptimal.  Instead, would it work if you add

	bool null_termination;

to the last_modified structure, and do

	if (!lm->null_termination)
		write_name_quoted(path, stdout, '\n');
	else
		printf("%s%c", path, '\0');

here?   Then

> @@ -507,10 +512,10 @@ int cmd_last_modified(int argc, const char **argv, const char *prefix,
>  		      struct repository *repo)
>  {
>  	int ret;
> -	struct last_modified lm = { 0 };
> +	struct last_modified lm = LAST_MODIFIED_INIT;

You do not need this change, and

>  	const char * const last_modified_usage[] = {
> -		N_("git last-modified [--recursive] [--show-trees] "
> +		N_("git last-modified [--recursive] [--show-trees] [-z] "
>  		   "[<revision-range>] [[--] <path>...]"),
>  		NULL
>  	};
> @@ -520,6 +525,8 @@ int cmd_last_modified(int argc, const char **argv, const char *prefix,
>  			 N_("recurse into subtrees")),
>  		OPT_BOOL('t', "show-trees", &lm.show_trees,
>  			 N_("show tree entries when recursing into subtrees")),
> +		OPT_SET_INT('z', NULL, &lm.line_termination,
> +			N_("lines are separated with NUL character"), '\0'),

This will become OPT_BOOL() to set the &lm.null_termination.

>  		OPT_END()
>  	};

^ permalink raw reply	[flat|nested] 36+ messages in thread

* Re: [PATCH 3/3] last-modified: better document how depth in handled
  2025-11-26  6:09 ` [PATCH 3/3] last-modified: better document how depth in handled Toon Claes
@ 2025-11-26 17:38   ` Eric Sunshine
  2025-12-01 10:32   ` Patrick Steinhardt
  1 sibling, 0 replies; 36+ messages in thread
From: Eric Sunshine @ 2025-11-26 17:38 UTC (permalink / raw)
  To: Toon Claes; +Cc: git

On Wed, Nov 26, 2025 at 1:10 AM Toon Claes <toon@iotcl.com> wrote:
> last-modified: better document how depth in handled

s/in/is/

> By default git-last-modified(1) only shows information about paths at
> the root level. This can be confusing. Clarify the command's behavior in
> the documentation.
>
> Signed-off-by: Toon Claes <toon@iotcl.com>

^ permalink raw reply	[flat|nested] 36+ messages in thread

* Re: [PATCH 2/3] last-modified: document option --max-depth
  2025-11-26  6:09 ` [PATCH 2/3] last-modified: document option --max-depth Toon Claes
  2025-11-26 13:31   ` Karthik Nayak
@ 2025-11-26 19:49   ` Junio C Hamano
  2025-11-28 18:51     ` Toon Claes
  1 sibling, 1 reply; 36+ messages in thread
From: Junio C Hamano @ 2025-11-26 19:49 UTC (permalink / raw)
  To: Toon Claes; +Cc: git

Toon Claes <toon@iotcl.com> writes:

> Option --max-depth is supported by git-last-modified(1), because it was
> added to the diff machinery in a1dfa5448d (diff: teach tree-diff a
> max-depth parameter, 2025-08-07).
>
> This option is useful for everyday use of the git-last-modified(1)
> command, so document it's existence in the man page and `-h` output.
>
> Signed-off-by: Toon Claes <toon@iotcl.com>
> ---
>  Documentation/git-last-modified.adoc |  9 ++++++++-
>  builtin/last-modified.c              | 12 +++++++++++-
>  2 files changed, 19 insertions(+), 2 deletions(-)

Does this step pass t0450?

    fixup! last-modified: document option --max-depth

diff --git a/builtin/last-modified.c b/builtin/last-modified.c
index ccb7ff66d4..857554e70d 100644
--- a/builtin/last-modified.c
+++ b/builtin/last-modified.c
@@ -523,8 +523,8 @@ int cmd_last_modified(int argc, const char **argv, const char *prefix,
 	struct last_modified lm = LAST_MODIFIED_INIT;
 
 	const char * const last_modified_usage[] = {
-		N_("git last-modified [--recursive] [--show-trees] [--max-depth=<depth>] [-z] "
-		   "[<revision-range>] [[--] <path>...]"),
+		N_("git last-modified [--recursive] [--show-trees] [--max-depth=<depth>] [-z]\n"
+		   "    [<revision-range>] [[--] <path>...]"),
 		NULL
 	};
 

^ permalink raw reply related	[flat|nested] 36+ messages in thread

* Re: [PATCH 1/3] last-modified: handle and document NUL termination
  2025-11-26 16:57   ` Junio C Hamano
@ 2025-11-28 18:50     ` Toon Claes
  2025-12-01 10:32       ` Patrick Steinhardt
  0 siblings, 1 reply; 36+ messages in thread
From: Toon Claes @ 2025-11-28 18:50 UTC (permalink / raw)
  To: Junio C Hamano; +Cc: git

Junio C Hamano <gitster@pobox.com> writes:

> Toon Claes <toon@iotcl.com> writes:
>
>> When option `-z` is provided to git-last-modified(1), each line is
>> separated with a NUL instead of a newline. Document this properly and
>> handle parsing of the option in the builtin itself.
>
> I think documenting does make sense, but it is not clear from the
> description why it is better to handle the option in the builtin
> itself, instead of letting the setup_revisions() take care of it.

I know it's silly, but I wanted to feed these options to
parse_options(). Doing this would make them show up in `git
last-modified -h`.

> Is it because after the command lets setup_revisions() to parse out
> the revision range, the command does not really let the revision
> machinery drive diffs and let it output anything (hence, even though
> rev.diffopt.line_termination is set from the command line, the calling
> builtin is the only one that pays attention, and the revision machinery
> and the diff machinery called from there does not pay attention to it?

That too, at least for this option. Not the other patch.

> And assuming that this new division of labor between the revision
> machinery and the subcommand makes sense (it needs to be explained
> better), the updated code does make sense to me.
>
> But it looks suboptimal.  See below.
>
>> +#define LAST_MODIFIED_INIT { \
>> +	.line_termination = '\n', \
>> +}
>
> You have to introduce such a non-zero initialization, only because
> you pretend to accept _any_ byte here, and use it as the line
> termination character.  If you were porting Git to ancient Macintosh,
> you could set this to '\r' and it would follow their text file
> convention there ;-)
>
> But ...
>
>>  struct last_modified_entry {
>>  	struct hashmap_entry hashent;
>>  	struct object_id oid;
>> @@ -55,6 +59,7 @@ struct last_modified {
>>  	struct rev_info rev;
>>  	bool recursive;
>>  	bool show_trees;
>> +	int line_termination;
>>  
>>  	const char **all_paths;
>>  	size_t all_paths_nr;
>> @@ -165,7 +170,7 @@ static void last_modified_emit(struct last_modified *lm,
>>  		putchar('^');
>>  	printf("%s\t", oid_to_hex(&commit->object.oid));
>>  
>> -	if (lm->rev.diffopt.line_termination)
>> +	if (lm->line_termination)
>>  		write_name_quoted(path, stdout, '\n');
>>  	else
>>  		printf("%s%c", path, '\0');
>
> ... you use hardcoded '\n' here, without allowing the value of
> line_termination to affect the termination character.
>
> This is way suboptimal.  Instead, would it work if you add
>
> 	bool null_termination;
>
> to the last_modified structure, and do
>
> 	if (!lm->null_termination)
> 		write_name_quoted(path, stdout, '\n');
> 	else
> 		printf("%s%c", path, '\0');
>
> here?   Then
>
>> @@ -507,10 +512,10 @@ int cmd_last_modified(int argc, const char **argv, const char *prefix,
>>  		      struct repository *repo)
>>  {
>>  	int ret;
>> -	struct last_modified lm = { 0 };
>> +	struct last_modified lm = LAST_MODIFIED_INIT;
>
> You do not need this change, and
>
>>  	const char * const last_modified_usage[] = {
>> -		N_("git last-modified [--recursive] [--show-trees] "
>> +		N_("git last-modified [--recursive] [--show-trees] [-z] "
>>  		   "[<revision-range>] [[--] <path>...]"),
>>  		NULL
>>  	};
>> @@ -520,6 +525,8 @@ int cmd_last_modified(int argc, const char **argv, const char *prefix,
>>  			 N_("recurse into subtrees")),
>>  		OPT_BOOL('t', "show-trees", &lm.show_trees,
>>  			 N_("show tree entries when recursing into subtrees")),
>> +		OPT_SET_INT('z', NULL, &lm.line_termination,
>> +			N_("lines are separated with NUL character"), '\0'),
>
> This will become OPT_BOOL() to set the &lm.null_termination.
>
>>  		OPT_END()
>>  	};
>

I actually agree this is better. But I was following the pattern of
`line_termination` other in various other places. I'll adapt it to use a
bool instead.

-- 
Cheers,
Toon

^ permalink raw reply	[flat|nested] 36+ messages in thread

* Re: [PATCH 2/3] last-modified: document option --max-depth
  2025-11-26 19:49   ` Junio C Hamano
@ 2025-11-28 18:51     ` Toon Claes
  0 siblings, 0 replies; 36+ messages in thread
From: Toon Claes @ 2025-11-28 18:51 UTC (permalink / raw)
  To: Junio C Hamano; +Cc: git

Junio C Hamano <gitster@pobox.com> writes:

> Toon Claes <toon@iotcl.com> writes:
>
>> Option --max-depth is supported by git-last-modified(1), because it was
>> added to the diff machinery in a1dfa5448d (diff: teach tree-diff a
>> max-depth parameter, 2025-08-07).
>>
>> This option is useful for everyday use of the git-last-modified(1)
>> command, so document it's existence in the man page and `-h` output.
>>
>> Signed-off-by: Toon Claes <toon@iotcl.com>
>> ---
>>  Documentation/git-last-modified.adoc |  9 ++++++++-
>>  builtin/last-modified.c              | 12 +++++++++++-
>>  2 files changed, 19 insertions(+), 2 deletions(-)
>
> Does this step pass t0450?
>
>     fixup! last-modified: document option --max-depth
>
> diff --git a/builtin/last-modified.c b/builtin/last-modified.c
> index ccb7ff66d4..857554e70d 100644
> --- a/builtin/last-modified.c
> +++ b/builtin/last-modified.c
> @@ -523,8 +523,8 @@ int cmd_last_modified(int argc, const char **argv, const char *prefix,
>  	struct last_modified lm = LAST_MODIFIED_INIT;
>  
>  	const char * const last_modified_usage[] = {
> -		N_("git last-modified [--recursive] [--show-trees] [--max-depth=<depth>] [-z] "
> -		   "[<revision-range>] [[--] <path>...]"),
> +		N_("git last-modified [--recursive] [--show-trees] [--max-depth=<depth>] [-z]\n"
> +		   "    [<revision-range>] [[--] <path>...]"),
>  		NULL
>  	};

Derp, I should have checked that. I knew it could be a problem, but by
the time I was about to send the patch I forgot. Sorry about that.

-- 
Cheers,
Toon

^ permalink raw reply	[flat|nested] 36+ messages in thread

* Re: [PATCH 1/3] last-modified: handle and document NUL termination
  2025-11-28 18:50     ` Toon Claes
@ 2025-12-01 10:32       ` Patrick Steinhardt
  0 siblings, 0 replies; 36+ messages in thread
From: Patrick Steinhardt @ 2025-12-01 10:32 UTC (permalink / raw)
  To: Toon Claes; +Cc: Junio C Hamano, git

On Fri, Nov 28, 2025 at 07:50:30PM +0100, Toon Claes wrote:
> Junio C Hamano <gitster@pobox.com> writes:
> 
> > Toon Claes <toon@iotcl.com> writes:
> >
> >> When option `-z` is provided to git-last-modified(1), each line is
> >> separated with a NUL instead of a newline. Document this properly and
> >> handle parsing of the option in the builtin itself.
> >
> > I think documenting does make sense, but it is not clear from the
> > description why it is better to handle the option in the builtin
> > itself, instead of letting the setup_revisions() take care of it.
> 
> I know it's silly, but I wanted to feed these options to
> parse_options(). Doing this would make them show up in `git
> last-modified -h`.

I think that reasoning makes sense, but it's certainly non-obvious from
the commit message. So if you expand the commit message with an
explanation the change becomes much more sensible.

Patrick

^ permalink raw reply	[flat|nested] 36+ messages in thread

* Re: [PATCH 3/3] last-modified: better document how depth in handled
  2025-11-26  6:09 ` [PATCH 3/3] last-modified: better document how depth in handled Toon Claes
  2025-11-26 17:38   ` Eric Sunshine
@ 2025-12-01 10:32   ` Patrick Steinhardt
  2025-12-02 11:01     ` Toon Claes
  1 sibling, 1 reply; 36+ messages in thread
From: Patrick Steinhardt @ 2025-12-01 10:32 UTC (permalink / raw)
  To: Toon Claes; +Cc: git

On Wed, Nov 26, 2025 at 07:09:45AM +0100, Toon Claes wrote:
> By default git-last-modified(1) only shows information about paths at
> the root level. This can be confusing. Clarify the command's behavior in
> the documentation.

Hm, that's confusing indeed. Is it possible for git-last-modified(1) to
do the "right thing" automatically? That is, given "sub/file", show when
that specific file has been last modified? Or is there a good
(non-technical) reason it behaves the way it does?

Patrick

^ permalink raw reply	[flat|nested] 36+ messages in thread

* Re: [PATCH 3/3] last-modified: better document how depth in handled
  2025-12-01 10:32   ` Patrick Steinhardt
@ 2025-12-02 11:01     ` Toon Claes
  2025-12-02 17:14       ` Patrick Steinhardt
  0 siblings, 1 reply; 36+ messages in thread
From: Toon Claes @ 2025-12-02 11:01 UTC (permalink / raw)
  To: Patrick Steinhardt, Taylor Blau; +Cc: git

Patrick Steinhardt <ps@pks.im> writes:

> Hm, that's confusing indeed. Is it possible for git-last-modified(1) to
> do the "right thing" automatically? That is, given "sub/file", show when
> that specific file has been last modified? Or is there a good
> (non-technical) reason it behaves the way it does?

You bring up a good point. In the version of git-blame-tree(1) that
GitHub did share, the `--recursive` flag is enabled by default. So if
you pass a file path to the command, you'll get the "right thing". But
as I am pointing out in this patch, if you pass a subtree, everything in
that subtree is shown too. You could argue this is the "right thing".

Anyhow, in the version of git-last-modified(1) I submitted upstream,
recursive is not enabled by default. My reason, at the time option
--max-depth wasn't implemented yet. I submitted those changes in a
separate series (these patches also originate from GitHub by the way).
If those patches wouldn't land, I think always-on recursive behavior for
git-last-modified(1) would be quite annoying.

So long story short, as git-last-modified(1) is still marked as
"EXPERIMENTAL", shall we make recursive always-on?

-- 
Cheers,
Toon

^ permalink raw reply	[flat|nested] 36+ messages in thread

* Re: [PATCH 3/3] last-modified: better document how depth in handled
  2025-12-02 11:01     ` Toon Claes
@ 2025-12-02 17:14       ` Patrick Steinhardt
  0 siblings, 0 replies; 36+ messages in thread
From: Patrick Steinhardt @ 2025-12-02 17:14 UTC (permalink / raw)
  To: Toon Claes; +Cc: Taylor Blau, git

On Tue, Dec 02, 2025 at 12:01:18PM +0100, Toon Claes wrote:
> Patrick Steinhardt <ps@pks.im> writes:
> 
> > Hm, that's confusing indeed. Is it possible for git-last-modified(1) to
> > do the "right thing" automatically? That is, given "sub/file", show when
> > that specific file has been last modified? Or is there a good
> > (non-technical) reason it behaves the way it does?
> 
> You bring up a good point. In the version of git-blame-tree(1) that
> GitHub did share, the `--recursive` flag is enabled by default. So if
> you pass a file path to the command, you'll get the "right thing". But
> as I am pointing out in this patch, if you pass a subtree, everything in
> that subtree is shown too. You could argue this is the "right thing".
> 
> Anyhow, in the version of git-last-modified(1) I submitted upstream,
> recursive is not enabled by default. My reason, at the time option
> --max-depth wasn't implemented yet. I submitted those changes in a
> separate series (these patches also originate from GitHub by the way).
> If those patches wouldn't land, I think always-on recursive behavior for
> git-last-modified(1) would be quite annoying.
> 
> So long story short, as git-last-modified(1) is still marked as
> "EXPERIMENTAL", shall we make recursive always-on?

I think that is a good idea, as it sounds like it would make common use
cases way more intuitive.

An alternative would be to default to a max-depth of 0 and stick to the
non-recursive default. In that case, the command would work intuitively
to the user, right? At least it would work intuitively if the user
doesn't want a recursive listing.

So maybe we should combine these two improvements. That is, we use:

  - An infinite max-depth by default with the recursive bahviour.

  - A max-depth of 0 in the non-recursive case.

In this case, we'd always recursively list all entries by default, but
in case the user explicitly doesn't want recursive behaviour we'd know
to show the last modification date of all provided files.

I was briefly thinking that a max-depth of 1 might be a more obvious
default for the non-recursive case. In that case, doing e.g. `git
last-modified --no-recursive t` would list the contents of "t/", but
nothing more. I'm a bit torn there though whether that really is a
sufficient improvement over a max-depth of 0.

Patrick

^ permalink raw reply	[flat|nested] 36+ messages in thread

* Re: [PATCH 2/3] last-modified: document option --max-depth
  2025-11-26 13:31   ` Karthik Nayak
@ 2026-01-16 12:13     ` Toon Claes
  0 siblings, 0 replies; 36+ messages in thread
From: Toon Claes @ 2026-01-16 12:13 UTC (permalink / raw)
  To: Karthik Nayak, git

Karthik Nayak <karthik.188@gmail.com> writes:

> Toon Claes <toon@iotcl.com> writes:
>
>> Option --max-depth is supported by git-last-modified(1), because it was
>> added to the diff machinery in a1dfa5448d (diff: teach tree-diff a
>> max-depth parameter, 2025-08-07).
>>
>
> At this point, does it make more sense to link the respective sections
> within 'Documentation/diff-options.adoc' as done by many other commands?
> This would ensure that we don't have to repeat the documentation.

Nah, there are way too many options that are not relevant for this
command in there. I agree, it's annoying to duplicate things. But I
don't know there's an easy way around that right now.

>> This option is useful for everyday use of the git-last-modified(1)
>> command, so document it's existence in the man page and `-h` output.
>>
>> Signed-off-by: Toon Claes <toon@iotcl.com>
>> ---
>>  Documentation/git-last-modified.adoc |  9 ++++++++-
>>  builtin/last-modified.c              | 12 +++++++++++-
>>  2 files changed, 19 insertions(+), 2 deletions(-)
>>
>> diff --git a/Documentation/git-last-modified.adoc b/Documentation/git-last-modified.adoc
>> index cd4a5040b0..8409daebe9 100644
>> --- a/Documentation/git-last-modified.adoc
>> +++ b/Documentation/git-last-modified.adoc
>> @@ -9,7 +9,8 @@ git-last-modified - EXPERIMENTAL: Show when files were last modified
>>  SYNOPSIS
>>  --------
>>  [synopsis]
>> -git last-modified [--recursive] [--show-trees] [-z] [<revision-range>] [[--] <path>...]
>> +git last-modified [--recursive] [--show-trees] [--max-depth=<depth>] [-z]
>> +	[<revision-range>] [[--] <path>...]
>>
>>  DESCRIPTION
>>  -----------
>> @@ -32,6 +33,12 @@ OPTIONS
>>  	Show tree entries even when recursing into them. It has no effect
>>  	without `--recursive`.
>>
>> +`--max-depth=<depth>`::
>> +	For each pathspec given on the command line, descend at most `<depth>`
>> +	levels of directories. A negative value means no limit.
>> +	Setting a positive value implies `--recursive`.
>> +	Cannot be combined with wildcards in the pathspec.
>> +
>>  `-z`::
>>  	Terminate each line with a _NUL_ rather than a newline.
>>
>> diff --git a/builtin/last-modified.c b/builtin/last-modified.c
>> index 9206bbdc1d..ccb7ff66d4 100644
>> --- a/builtin/last-modified.c
>> +++ b/builtin/last-modified.c
>> @@ -25,6 +25,7 @@
>>
>>  #define LAST_MODIFIED_INIT { \
>>  	.line_termination = '\n', \
>> +	.max_depth = -1, \
>>  }
>>
>>  struct last_modified_entry {
>> @@ -60,6 +61,7 @@ struct last_modified {
>>  	bool recursive;
>>  	bool show_trees;
>>  	int line_termination;
>> +	int max_depth;
>>
>
> Should this be signed?

Yes, a negative number is allowed to set an unlimited max depth.

>
>>  	const char **all_paths;
>>  	size_t all_paths_nr;
>> @@ -487,6 +489,12 @@ static int last_modified_init(struct last_modified *lm, struct repository *r,
>>  	lm->rev.diffopt.flags.recursive = lm->recursive;
>>  	lm->rev.diffopt.flags.tree_in_recursive = lm->show_trees;
>>
>> +	if (lm->max_depth >= 0) {
>> +		lm->rev.diffopt.flags.recursive = 1;
>> +		lm->rev.diffopt.max_depth = lm->max_depth;
>> +		lm->rev.diffopt.max_depth_valid = 1;
>> +	}
>> +
>
> Or if our goal is to actually handle them within the
> 'git-last-modified(1)' command, shouldn't we ensure we don't allow any
> additional flags from being parsed as diffopt?

Is that possible?

All the magic that happens in setup_revision() is a little bit of a pain
if you ask me. 

> Currently other diffopts flags such as '--no-prefix', '--cc' and so on,
> are parsed even if they don't affect the output of
> 'git-last-modified(1)'. Shouldn't we disallow such behavior?

I think this would require a revamp of setup_revision(). But I'm happy
to be proven otherwise.

>>  	argc = setup_revisions(argc, argv, &lm->rev, NULL);
>>  	if (argc > 1) {
>>  		error(_("unknown last-modified argument: %s"), argv[1]);
>> @@ -515,7 +523,7 @@ int cmd_last_modified(int argc, const char **argv, const char *prefix,
>>  	struct last_modified lm = LAST_MODIFIED_INIT;
>>
>>  	const char * const last_modified_usage[] = {
>> -		N_("git last-modified [--recursive] [--show-trees] [-z] "
>> +		N_("git last-modified [--recursive] [--show-trees] [--max-depth=<depth>] [-z] "
>>  		   "[<revision-range>] [[--] <path>...]"),
>>  		NULL
>>  	};
>> @@ -525,6 +533,8 @@ int cmd_last_modified(int argc, const char **argv, const char *prefix,
>>  			 N_("recurse into subtrees")),
>>  		OPT_BOOL('t', "show-trees", &lm.show_trees,
>>  			 N_("show tree entries when recursing into subtrees")),
>> +		OPT_INTEGER_F(0, "max-depth", &lm.max_depth,
>> +			N_("maximum tree depth to recurse"), PARSE_OPT_NONEG),
>>  		OPT_SET_INT('z', NULL, &lm.line_termination,
>>  			N_("lines are separated with NUL character"), '\0'),
>>  		OPT_END()
>>
>> --
>> 2.51.2

-- 
Cheers,
Toon

^ permalink raw reply	[flat|nested] 36+ messages in thread

* [PATCH v2 0/5] Change git-last-modified(1) default behavior and add documentation
  2025-11-26  6:09 [PATCH 0/3] Expand and enhance git-last-modified(1) documentation Toon Claes
                   ` (2 preceding siblings ...)
  2025-11-26  6:09 ` [PATCH 3/3] last-modified: better document how depth in handled Toon Claes
@ 2026-01-16 13:22 ` Toon Claes
  2026-01-16 13:22   ` [PATCH v2 1/5] last-modified: document NUL termination Toon Claes
                     ` (6 more replies)
  3 siblings, 7 replies; 36+ messages in thread
From: Toon Claes @ 2026-01-16 13:22 UTC (permalink / raw)
  To: git; +Cc: Kristoffer Haugsbakk, Gusted, Toon Claes

We have had several reports[1][2] from users that have been thoroughly
confused by the default behaviour of git-last-modified(1). Most
importantly, when using the command on trees other than the root tree
the requests are seemingly ignoring that request and instead blame the
top-level subtree. For example:

    $ git last-modified t/lib-bash.sh
    acdfea4394db8e8b42f48e36f7726d64a909a89d t

This result is completely unexpected and feels buggy to anybody who is
not deeply familiar with git-last-modified(1). Furthermore, users
typically have a hard time to figure out the correct parameters.

This series changes the behavior to something that intuitively makes
more sense.

The change of the default behavior is done in the last commit, in the
lead-up commits changes in the documentation are made.

[1]: <f0c508cc-5c6b-4c4b-a3f3-0cdd8d1071e5@app.fastmail.com>
[2]: <03f96860-29fc-42a7-a220-c3ec65eb8516@codeberg.org>

---
Changes in v2:
- Change the default --max-depth.
- Split up commits in smaller pieces.
- Add more testss
- Link to v1: https://patch.msgid.link/20251126-toon-last-modified-zzzz-v1-0-608350df0caa@iotcl.com

Cc: "Kristoffer Haugsbakk" <kristofferhaugsbakk@fastmail.com>
Cc: Gusted <gusted@codeberg.org>

---
Toon Claes (5):
      last-modified: document NUL termination
      last-modified: add option '-z' to help output
      last-modified: document option --max-depth
      last-modified: add option '--max-depth' to help output
      last-modified: change default max-depth to 0

 Documentation/git-last-modified.adoc | 29 ++++++++++++++++++++++++++++-
 builtin/last-modified.c              | 20 ++++++++++++++------
 t/t8020-last-modified.sh             | 35 +++++++++++++++++++++++++++++++++++
 3 files changed, 77 insertions(+), 7 deletions(-)

Range-diff versus v1:

1:  7f599cf0d1 ! 1:  4a9b8170c3 last-modified: handle and document NUL termination
    @@ Metadata
     Author: Toon Claes <toon@iotcl.com>
     
      ## Commit message ##
    -    last-modified: handle and document NUL termination
    +    last-modified: document NUL termination
     
    -    When option `-z` is provided to git-last-modified(1), each line is
    -    separated with a NUL instead of a newline. Document this properly and
    -    handle parsing of the option in the builtin itself.
    +    The command git-last-modified(1) already recognizes the option '-z', and
    +    similar to many other commands this will make the output NUL-terminated
    +    instead of using newlines. Although, this option is missing from the
    +    documentation, so add it.
     
         Signed-off-by: Toon Claes <toon@iotcl.com>
     
    @@ Documentation/git-last-modified.adoc: git-last-modified - EXPERIMENTAL: Show whe
      --------
      [synopsis]
     -git last-modified [--recursive] [--show-trees] [<revision-range>] [[--] <path>...]
    -+git last-modified [--recursive] [--show-trees] [-z] [<revision-range>] [[--] <path>...]
    ++git last-modified [--recursive] [--show-trees] [-z]
    ++		  [<revision-range>] [[--] <path>...]
      
      DESCRIPTION
      -----------
    @@ Documentation/git-last-modified.adoc: OPTIONS
      	without `--recursive`.
      
     +`-z`::
    -+	Terminate each line with a _NUL_ rather than a newline.
    ++	Terminate each line with a _NUL_ character rather than a newline.
     +
      `<revision-range>`::
      	Only traverse commits in the specified revision range. When no
    @@ Documentation/git-last-modified.adoc: OPTIONS
      linkgit:git-blame[1],
     
      ## builtin/last-modified.c ##
    -@@
    - #define PARENT1 (1u<<16) /* used instead of SEEN */
    - #define PARENT2 (1u<<17) /* used instead of BOTTOM, BOUNDARY */
    - 
    -+#define LAST_MODIFIED_INIT { \
    -+	.line_termination = '\n', \
    -+}
    -+
    - struct last_modified_entry {
    - 	struct hashmap_entry hashent;
    - 	struct object_id oid;
    -@@ builtin/last-modified.c: struct last_modified {
    - 	struct rev_info rev;
    - 	bool recursive;
    - 	bool show_trees;
    -+	int line_termination;
    - 
    - 	const char **all_paths;
    - 	size_t all_paths_nr;
    -@@ builtin/last-modified.c: static void last_modified_emit(struct last_modified *lm,
    - 		putchar('^');
    - 	printf("%s\t", oid_to_hex(&commit->object.oid));
    - 
    --	if (lm->rev.diffopt.line_termination)
    -+	if (lm->line_termination)
    - 		write_name_quoted(path, stdout, '\n');
    - 	else
    - 		printf("%s%c", path, '\0');
     @@ builtin/last-modified.c: int cmd_last_modified(int argc, const char **argv, const char *prefix,
    - 		      struct repository *repo)
    - {
    - 	int ret;
    --	struct last_modified lm = { 0 };
    -+	struct last_modified lm = LAST_MODIFIED_INIT;
    + 	struct last_modified lm = { 0 };
      
      	const char * const last_modified_usage[] = {
     -		N_("git last-modified [--recursive] [--show-trees] "
    -+		N_("git last-modified [--recursive] [--show-trees] [-z] "
    - 		   "[<revision-range>] [[--] <path>...]"),
    +-		   "[<revision-range>] [[--] <path>...]"),
    ++		N_("git last-modified [--recursive] [--show-trees] [-z]\n"
    ++		   "                  [<revision-range>] [[--] <path>...]"),
      		NULL
      	};
    -@@ builtin/last-modified.c: int cmd_last_modified(int argc, const char **argv, const char *prefix,
    - 			 N_("recurse into subtrees")),
    - 		OPT_BOOL('t', "show-trees", &lm.show_trees,
    - 			 N_("show tree entries when recursing into subtrees")),
    -+		OPT_SET_INT('z', NULL, &lm.line_termination,
    -+			N_("lines are separated with NUL character"), '\0'),
    - 		OPT_END()
    - 	};
      
-:  ---------- > 2:  94efeb29b1 last-modified: add option '-z' to help output
-:  ---------- > 3:  5669ab25e8 last-modified: document option --max-depth
2:  f193241248 ! 4:  b3060f8b38 last-modified: document option --max-depth
    @@ Metadata
     Author: Toon Claes <toon@iotcl.com>
     
      ## Commit message ##
    -    last-modified: document option --max-depth
    +    last-modified: add option '--max-depth' to help output
     
    -    Option --max-depth is supported by git-last-modified(1), because it was
    -    added to the diff machinery in a1dfa5448d (diff: teach tree-diff a
    -    max-depth parameter, 2025-08-07).
    +    In previous commit option '--max-depth' was added to the documentation.
    +    To have it also appear in the help output of `git last-modified -h`,
    +    move the handling of '--max-depth' to parse_options() in
    +    builtin/last-modified.c itself.
     
    -    This option is useful for everyday use of the git-last-modified(1)
    -    command, so document it's existence in the man page and `-h` output.
    +    It enables us to change default behavior in a subsequent commit.
     
         Signed-off-by: Toon Claes <toon@iotcl.com>
     
    - ## Documentation/git-last-modified.adoc ##
    -@@ Documentation/git-last-modified.adoc: git-last-modified - EXPERIMENTAL: Show when files were last modified
    - SYNOPSIS
    - --------
    - [synopsis]
    --git last-modified [--recursive] [--show-trees] [-z] [<revision-range>] [[--] <path>...]
    -+git last-modified [--recursive] [--show-trees] [--max-depth=<depth>] [-z]
    -+	[<revision-range>] [[--] <path>...]
    - 
    - DESCRIPTION
    - -----------
    -@@ Documentation/git-last-modified.adoc: OPTIONS
    - 	Show tree entries even when recursing into them. It has no effect
    - 	without `--recursive`.
    - 
    -+`--max-depth=<depth>`::
    -+	For each pathspec given on the command line, descend at most `<depth>`
    -+	levels of directories. A negative value means no limit.
    -+	Setting a positive value implies `--recursive`.
    -+	Cannot be combined with wildcards in the pathspec.
    -+
    - `-z`::
    - 	Terminate each line with a _NUL_ rather than a newline.
    - 
    -
      ## builtin/last-modified.c ##
    -@@
    - 
    - #define LAST_MODIFIED_INIT { \
    - 	.line_termination = '\n', \
    -+	.max_depth = -1, \
    - }
    - 
    - struct last_modified_entry {
     @@ builtin/last-modified.c: struct last_modified {
      	bool recursive;
      	bool show_trees;
    - 	int line_termination;
    + 	bool null_termination;
     +	int max_depth;
      
      	const char **all_paths;
    @@ builtin/last-modified.c: static int last_modified_init(struct last_modified *lm,
      	argc = setup_revisions(argc, argv, &lm->rev, NULL);
      	if (argc > 1) {
      		error(_("unknown last-modified argument: %s"), argv[1]);
    -@@ builtin/last-modified.c: int cmd_last_modified(int argc, const char **argv, const char *prefix,
    - 	struct last_modified lm = LAST_MODIFIED_INIT;
    - 
    - 	const char * const last_modified_usage[] = {
    --		N_("git last-modified [--recursive] [--show-trees] [-z] "
    -+		N_("git last-modified [--recursive] [--show-trees] [--max-depth=<depth>] [-z] "
    - 		   "[<revision-range>] [[--] <path>...]"),
    - 		NULL
    - 	};
     @@ builtin/last-modified.c: int cmd_last_modified(int argc, const char **argv, const char *prefix,
      			 N_("recurse into subtrees")),
      		OPT_BOOL('t', "show-trees", &lm.show_trees,
      			 N_("show tree entries when recursing into subtrees")),
     +		OPT_INTEGER_F(0, "max-depth", &lm.max_depth,
     +			N_("maximum tree depth to recurse"), PARSE_OPT_NONEG),
    - 		OPT_SET_INT('z', NULL, &lm.line_termination,
    - 			N_("lines are separated with NUL character"), '\0'),
    + 		OPT_BOOL('z', NULL, &lm.null_termination,
    + 			N_("lines are separated with NUL character")),
      		OPT_END()
    + 	};
    + 
    ++	/*
    ++	 * Set the default of a max-depth to "unset". This will change in a
    ++	 * subsequent commit.
    ++	 */
    ++	lm.max_depth = -1;
    ++
    + 	argc = parse_options(argc, argv, prefix, last_modified_options,
    + 			     last_modified_usage,
    + 			     PARSE_OPT_KEEP_ARGV0 | PARSE_OPT_KEEP_UNKNOWN_OPT |
3:  6773f073d6 < -:  ---------- last-modified: better document how depth in handled
-:  ---------- > 5:  41819f8732 last-modified: change default max-depth to 0


---
base-commit: 7264e61d87e58b9d0f5e6424c47c11e9657dfb75
change-id: 20251114-toon-last-modified-zzzz-af9c1be74fc4


^ permalink raw reply	[flat|nested] 36+ messages in thread

* [PATCH v2 1/5] last-modified: document NUL termination
  2026-01-16 13:22 ` [PATCH v2 0/5] Change git-last-modified(1) default behavior and add documentation Toon Claes
@ 2026-01-16 13:22   ` Toon Claes
  2026-01-16 13:22   ` [PATCH v2 2/5] last-modified: add option '-z' to help output Toon Claes
                     ` (5 subsequent siblings)
  6 siblings, 0 replies; 36+ messages in thread
From: Toon Claes @ 2026-01-16 13:22 UTC (permalink / raw)
  To: git; +Cc: Kristoffer Haugsbakk, Gusted, Toon Claes

The command git-last-modified(1) already recognizes the option '-z', and
similar to many other commands this will make the output NUL-terminated
instead of using newlines. Although, this option is missing from the
documentation, so add it.

Signed-off-by: Toon Claes <toon@iotcl.com>
---
 Documentation/git-last-modified.adoc | 22 +++++++++++++++++++++-
 builtin/last-modified.c              |  4 ++--
 2 files changed, 23 insertions(+), 3 deletions(-)

diff --git a/Documentation/git-last-modified.adoc b/Documentation/git-last-modified.adoc
index 602843e095..2e5f370c15 100644
--- a/Documentation/git-last-modified.adoc
+++ b/Documentation/git-last-modified.adoc
@@ -9,7 +9,8 @@ git-last-modified - EXPERIMENTAL: Show when files were last modified
 SYNOPSIS
 --------
 [synopsis]
-git last-modified [--recursive] [--show-trees] [<revision-range>] [[--] <path>...]
+git last-modified [--recursive] [--show-trees] [-z]
+		  [<revision-range>] [[--] <path>...]
 
 DESCRIPTION
 -----------
@@ -32,6 +33,9 @@ OPTIONS
 	Show tree entries even when recursing into them. It has no effect
 	without `--recursive`.
 
+`-z`::
+	Terminate each line with a _NUL_ character rather than a newline.
+
 `<revision-range>`::
 	Only traverse commits in the specified revision range. When no
 	`<revision-range>` is specified, it defaults to `HEAD` (i.e. the whole
@@ -44,6 +48,22 @@ OPTIONS
 	Without an optional path parameter, all files and subdirectories
 	in path traversal the are included in the output.
 
+OUTPUT
+------
+
+The output is in the format:
+
+------------
+ <oid> TAB <path> LF
+------------
+
+If a path contains any special characters, the path is C-style quoted. To
+avoid quoting, pass option `-z` to terminate each line with a NUL.
+
+------------
+ <oid> TAB <path> NUL
+------------
+
 SEE ALSO
 --------
 linkgit:git-blame[1],
diff --git a/builtin/last-modified.c b/builtin/last-modified.c
index c80f0535f6..cac66d03fd 100644
--- a/builtin/last-modified.c
+++ b/builtin/last-modified.c
@@ -510,8 +510,8 @@ int cmd_last_modified(int argc, const char **argv, const char *prefix,
 	struct last_modified lm = { 0 };
 
 	const char * const last_modified_usage[] = {
-		N_("git last-modified [--recursive] [--show-trees] "
-		   "[<revision-range>] [[--] <path>...]"),
+		N_("git last-modified [--recursive] [--show-trees] [-z]\n"
+		   "                  [<revision-range>] [[--] <path>...]"),
 		NULL
 	};
 

-- 
2.52.0


^ permalink raw reply related	[flat|nested] 36+ messages in thread

* [PATCH v2 2/5] last-modified: add option '-z' to help output
  2026-01-16 13:22 ` [PATCH v2 0/5] Change git-last-modified(1) default behavior and add documentation Toon Claes
  2026-01-16 13:22   ` [PATCH v2 1/5] last-modified: document NUL termination Toon Claes
@ 2026-01-16 13:22   ` Toon Claes
  2026-01-16 18:31     ` Junio C Hamano
  2026-01-16 13:22   ` [PATCH v2 3/5] last-modified: document option --max-depth Toon Claes
                     ` (4 subsequent siblings)
  6 siblings, 1 reply; 36+ messages in thread
From: Toon Claes @ 2026-01-16 13:22 UTC (permalink / raw)
  To: git; +Cc: Kristoffer Haugsbakk, Gusted, Toon Claes

The parsing of option '-z' is done by diff_opt_parse(), which is called
by setup_revisions(), and ends up filling in
`struct diff_options::line_termination`. But that field isn't used by
the diff machinery itself, only by builtin/last-modified.c to format
the output.

To have '-z' also appear in the help output of `git last-modified -h`,
move the handling of '-z' to parse_options() in builtin/last-modified.c
itself.

Signed-off-by: Toon Claes <toon@iotcl.com>
---
 builtin/last-modified.c | 9 ++++++---
 1 file changed, 6 insertions(+), 3 deletions(-)

diff --git a/builtin/last-modified.c b/builtin/last-modified.c
index cac66d03fd..0d73384e45 100644
--- a/builtin/last-modified.c
+++ b/builtin/last-modified.c
@@ -55,6 +55,7 @@ struct last_modified {
 	struct rev_info rev;
 	bool recursive;
 	bool show_trees;
+	bool null_termination;
 
 	const char **all_paths;
 	size_t all_paths_nr;
@@ -165,10 +166,10 @@ static void last_modified_emit(struct last_modified *lm,
 		putchar('^');
 	printf("%s\t", oid_to_hex(&commit->object.oid));
 
-	if (lm->rev.diffopt.line_termination)
-		write_name_quoted(path, stdout, '\n');
-	else
+	if (lm->null_termination)
 		printf("%s%c", path, '\0');
+	else
+		write_name_quoted(path, stdout, '\n');
 }
 
 static void mark_path(const char *path, const struct object_id *oid,
@@ -520,6 +521,8 @@ int cmd_last_modified(int argc, const char **argv, const char *prefix,
 			 N_("recurse into subtrees")),
 		OPT_BOOL('t', "show-trees", &lm.show_trees,
 			 N_("show tree entries when recursing into subtrees")),
+		OPT_BOOL('z', NULL, &lm.null_termination,
+			N_("lines are separated with NUL character")),
 		OPT_END()
 	};
 

-- 
2.52.0


^ permalink raw reply related	[flat|nested] 36+ messages in thread

* [PATCH v2 3/5] last-modified: document option --max-depth
  2026-01-16 13:22 ` [PATCH v2 0/5] Change git-last-modified(1) default behavior and add documentation Toon Claes
  2026-01-16 13:22   ` [PATCH v2 1/5] last-modified: document NUL termination Toon Claes
  2026-01-16 13:22   ` [PATCH v2 2/5] last-modified: add option '-z' to help output Toon Claes
@ 2026-01-16 13:22   ` Toon Claes
  2026-01-16 13:22   ` [PATCH v2 4/5] last-modified: add option '--max-depth' to help output Toon Claes
                     ` (3 subsequent siblings)
  6 siblings, 0 replies; 36+ messages in thread
From: Toon Claes @ 2026-01-16 13:22 UTC (permalink / raw)
  To: git; +Cc: Kristoffer Haugsbakk, Gusted, Toon Claes

Option --max-depth is supported by git-last-modified(1), because it was
added to the diff machinery in a1dfa5448d (diff: teach tree-diff a
max-depth parameter, 2025-08-07).

This option is useful for everyday use of the git-last-modified(1)
command, so document it's existence in the man page.

Signed-off-by: Toon Claes <toon@iotcl.com>
---
 Documentation/git-last-modified.adoc | 8 +++++++-
 builtin/last-modified.c              | 2 +-
 2 files changed, 8 insertions(+), 2 deletions(-)

diff --git a/Documentation/git-last-modified.adoc b/Documentation/git-last-modified.adoc
index 2e5f370c15..a3992db3f2 100644
--- a/Documentation/git-last-modified.adoc
+++ b/Documentation/git-last-modified.adoc
@@ -9,7 +9,7 @@ git-last-modified - EXPERIMENTAL: Show when files were last modified
 SYNOPSIS
 --------
 [synopsis]
-git last-modified [--recursive] [--show-trees] [-z]
+git last-modified [--recursive] [--show-trees] [--max-depth=<depth>] [-z]
 		  [<revision-range>] [[--] <path>...]
 
 DESCRIPTION
@@ -33,6 +33,12 @@ OPTIONS
 	Show tree entries even when recursing into them. It has no effect
 	without `--recursive`.
 
+`--max-depth=<depth>`::
+	For each pathspec given on the command line, descend at most `<depth>`
+	levels of directories. A negative value means no limit.
+	Setting a positive value implies `--recursive`.
+	Cannot be combined with wildcards in the pathspec.
+
 `-z`::
 	Terminate each line with a _NUL_ character rather than a newline.
 
diff --git a/builtin/last-modified.c b/builtin/last-modified.c
index 0d73384e45..324363b5ca 100644
--- a/builtin/last-modified.c
+++ b/builtin/last-modified.c
@@ -511,7 +511,7 @@ int cmd_last_modified(int argc, const char **argv, const char *prefix,
 	struct last_modified lm = { 0 };
 
 	const char * const last_modified_usage[] = {
-		N_("git last-modified [--recursive] [--show-trees] [-z]\n"
+		N_("git last-modified [--recursive] [--show-trees] [--max-depth=<depth>] [-z]\n"
 		   "                  [<revision-range>] [[--] <path>...]"),
 		NULL
 	};

-- 
2.52.0


^ permalink raw reply related	[flat|nested] 36+ messages in thread

* [PATCH v2 4/5] last-modified: add option '--max-depth' to help output
  2026-01-16 13:22 ` [PATCH v2 0/5] Change git-last-modified(1) default behavior and add documentation Toon Claes
                     ` (2 preceding siblings ...)
  2026-01-16 13:22   ` [PATCH v2 3/5] last-modified: document option --max-depth Toon Claes
@ 2026-01-16 13:22   ` Toon Claes
  2026-01-16 18:42     ` Junio C Hamano
  2026-01-16 13:22   ` [PATCH v2 5/5] last-modified: change default max-depth to 0 Toon Claes
                     ` (2 subsequent siblings)
  6 siblings, 1 reply; 36+ messages in thread
From: Toon Claes @ 2026-01-16 13:22 UTC (permalink / raw)
  To: git; +Cc: Kristoffer Haugsbakk, Gusted, Toon Claes

In previous commit option '--max-depth' was added to the documentation.
To have it also appear in the help output of `git last-modified -h`,
move the handling of '--max-depth' to parse_options() in
builtin/last-modified.c itself.

It enables us to change default behavior in a subsequent commit.

Signed-off-by: Toon Claes <toon@iotcl.com>
---
 builtin/last-modified.c | 15 +++++++++++++++
 1 file changed, 15 insertions(+)

diff --git a/builtin/last-modified.c b/builtin/last-modified.c
index 324363b5ca..842700bc6a 100644
--- a/builtin/last-modified.c
+++ b/builtin/last-modified.c
@@ -56,6 +56,7 @@ struct last_modified {
 	bool recursive;
 	bool show_trees;
 	bool null_termination;
+	int max_depth;
 
 	const char **all_paths;
 	size_t all_paths_nr;
@@ -483,6 +484,12 @@ static int last_modified_init(struct last_modified *lm, struct repository *r,
 	lm->rev.diffopt.flags.recursive = lm->recursive;
 	lm->rev.diffopt.flags.tree_in_recursive = lm->show_trees;
 
+	if (lm->max_depth >= 0) {
+		lm->rev.diffopt.flags.recursive = 1;
+		lm->rev.diffopt.max_depth = lm->max_depth;
+		lm->rev.diffopt.max_depth_valid = 1;
+	}
+
 	argc = setup_revisions(argc, argv, &lm->rev, NULL);
 	if (argc > 1) {
 		error(_("unknown last-modified argument: %s"), argv[1]);
@@ -521,11 +528,19 @@ int cmd_last_modified(int argc, const char **argv, const char *prefix,
 			 N_("recurse into subtrees")),
 		OPT_BOOL('t', "show-trees", &lm.show_trees,
 			 N_("show tree entries when recursing into subtrees")),
+		OPT_INTEGER_F(0, "max-depth", &lm.max_depth,
+			N_("maximum tree depth to recurse"), PARSE_OPT_NONEG),
 		OPT_BOOL('z', NULL, &lm.null_termination,
 			N_("lines are separated with NUL character")),
 		OPT_END()
 	};
 
+	/*
+	 * Set the default of a max-depth to "unset". This will change in a
+	 * subsequent commit.
+	 */
+	lm.max_depth = -1;
+
 	argc = parse_options(argc, argv, prefix, last_modified_options,
 			     last_modified_usage,
 			     PARSE_OPT_KEEP_ARGV0 | PARSE_OPT_KEEP_UNKNOWN_OPT |

-- 
2.52.0


^ permalink raw reply related	[flat|nested] 36+ messages in thread

* [PATCH v2 5/5] last-modified: change default max-depth to 0
  2026-01-16 13:22 ` [PATCH v2 0/5] Change git-last-modified(1) default behavior and add documentation Toon Claes
                     ` (3 preceding siblings ...)
  2026-01-16 13:22   ` [PATCH v2 4/5] last-modified: add option '--max-depth' to help output Toon Claes
@ 2026-01-16 13:22   ` Toon Claes
  2026-01-16 18:55     ` Junio C Hamano
  2026-01-16 13:34   ` [PATCH v2 0/5] Change git-last-modified(1) default behavior and add documentation Kristoffer Haugsbakk
  2026-01-20 21:47   ` [PATCH v3 0/4] " Toon Claes
  6 siblings, 1 reply; 36+ messages in thread
From: Toon Claes @ 2026-01-16 13:22 UTC (permalink / raw)
  To: git; +Cc: Kristoffer Haugsbakk, Gusted, Toon Claes

By default git-last-modified(1) doesn't recurse into subtrees. So when
the pathspec contained a path in a subtree, the command would only print
the commit information about the parent tree of the path, like:

    $ git last-modified -- path/file
    aaa0aab1bbb2bcc3ccc4ddd5dde6eee7eff8fff9	path

Change the default behavior to give commit information about the exact
path instead:

    $ git last-modified -- path/file
    aaa0aab1bbb2bcc3ccc4ddd5dde6eee7eff8fff9	path/file

To achieve this, the default max-depth is changed to 0 and recursive is
always enabled.

The handling of option '-r' is modified to disable a max-depth,
resulting in the behavior of this option to remain unchanged.

No existing tests were modified, because there didn't exist any tests
covering the example above. But more tests are added to cover this now.

Signed-off-by: Toon Claes <toon@iotcl.com>
---
 Documentation/git-last-modified.adoc |  3 ++-
 builtin/last-modified.c              | 16 +++-------------
 t/t8020-last-modified.sh             | 35 +++++++++++++++++++++++++++++++++++
 3 files changed, 40 insertions(+), 14 deletions(-)

diff --git a/Documentation/git-last-modified.adoc b/Documentation/git-last-modified.adoc
index a3992db3f2..57136baf3b 100644
--- a/Documentation/git-last-modified.adoc
+++ b/Documentation/git-last-modified.adoc
@@ -27,6 +27,7 @@ OPTIONS
 `--recursive`::
 	Instead of showing tree entries, step into subtrees and show all entries
 	inside them recursively.
+	This is identical as setting `--max-depth=-1`.
 
 `-t`::
 `--show-trees`::
@@ -36,7 +37,7 @@ OPTIONS
 `--max-depth=<depth>`::
 	For each pathspec given on the command line, descend at most `<depth>`
 	levels of directories. A negative value means no limit.
-	Setting a positive value implies `--recursive`.
+	The default depth is 0.
 	Cannot be combined with wildcards in the pathspec.
 
 `-z`::
diff --git a/builtin/last-modified.c b/builtin/last-modified.c
index 842700bc6a..a10e711beb 100644
--- a/builtin/last-modified.c
+++ b/builtin/last-modified.c
@@ -481,14 +481,10 @@ static int last_modified_init(struct last_modified *lm, struct repository *r,
 	lm->rev.no_commit_id = 1;
 	lm->rev.diff = 1;
 	lm->rev.diffopt.flags.no_recursive_diff_tree_combined = 1;
-	lm->rev.diffopt.flags.recursive = lm->recursive;
+	lm->rev.diffopt.flags.recursive = 1;
 	lm->rev.diffopt.flags.tree_in_recursive = lm->show_trees;
-
-	if (lm->max_depth >= 0) {
-		lm->rev.diffopt.flags.recursive = 1;
-		lm->rev.diffopt.max_depth = lm->max_depth;
-		lm->rev.diffopt.max_depth_valid = 1;
-	}
+	lm->rev.diffopt.max_depth = lm->max_depth;
+	lm->rev.diffopt.max_depth_valid = !lm->recursive && lm->max_depth >= 0;
 
 	argc = setup_revisions(argc, argv, &lm->rev, NULL);
 	if (argc > 1) {
@@ -535,12 +531,6 @@ int cmd_last_modified(int argc, const char **argv, const char *prefix,
 		OPT_END()
 	};
 
-	/*
-	 * Set the default of a max-depth to "unset". This will change in a
-	 * subsequent commit.
-	 */
-	lm.max_depth = -1;
-
 	argc = parse_options(argc, argv, prefix, last_modified_options,
 			     last_modified_usage,
 			     PARSE_OPT_KEEP_ARGV0 | PARSE_OPT_KEEP_UNKNOWN_OPT |
diff --git a/t/t8020-last-modified.sh b/t/t8020-last-modified.sh
index 50f4312f71..3944d2e153 100755
--- a/t/t8020-last-modified.sh
+++ b/t/t8020-last-modified.sh
@@ -93,6 +93,41 @@ test_expect_success 'last-modified subdir recursive' '
 	EOF
 '
 
+test_expect_success 'last-modified subdir non-recursive' '
+	check_last_modified a <<-\EOF
+	3 a
+	EOF
+'
+
+test_expect_success 'last-modified path in subdir non-recursive' '
+	check_last_modified a/file <<-\EOF
+	2 a/file
+	EOF
+'
+
+test_expect_success 'last-modified subdir with wildcard non-recursive' '
+	check_last_modified a/* <<-\EOF
+	3 a/b
+	2 a/file
+	EOF
+'
+
+test_expect_success 'last-modified with negative max-depth' '
+	check_last_modified --max-depth=-1 <<-\EOF
+	3 a/b/file
+	2 a/file
+	1 file
+	EOF
+'
+
+test_expect_success 'last-modified with max-depth of 1' '
+	check_last_modified --max-depth=1 <<-\EOF
+	3 a/b
+	2 a/file
+	1 file
+	EOF
+'
+
 test_expect_success 'last-modified from non-HEAD commit' '
 	check_last_modified HEAD^ <<-\EOF
 	2 a

-- 
2.52.0


^ permalink raw reply related	[flat|nested] 36+ messages in thread

* Re: [PATCH v2 0/5] Change git-last-modified(1) default behavior and add documentation
  2026-01-16 13:22 ` [PATCH v2 0/5] Change git-last-modified(1) default behavior and add documentation Toon Claes
                     ` (4 preceding siblings ...)
  2026-01-16 13:22   ` [PATCH v2 5/5] last-modified: change default max-depth to 0 Toon Claes
@ 2026-01-16 13:34   ` Kristoffer Haugsbakk
  2026-01-20 10:44     ` Toon Claes
  2026-01-20 21:47   ` [PATCH v3 0/4] " Toon Claes
  6 siblings, 1 reply; 36+ messages in thread
From: Kristoffer Haugsbakk @ 2026-01-16 13:34 UTC (permalink / raw)
  To: Toon Claes, git; +Cc: Gusted

On Fri, Jan 16, 2026, at 14:22, Toon Claes wrote:
> We have had several reports[1][2] from users that have been thoroughly
> confused by the default behaviour of git-last-modified(1). Most
> importantly, when using the command on trees other than the root tree
> the requests are seemingly ignoring that request and instead blame the
> top-level subtree. For example:
>
>     $ git last-modified t/lib-bash.sh
>     acdfea4394db8e8b42f48e36f7726d64a909a89d t
>
> This result is completely unexpected and feels buggy to anybody who is
> not deeply familiar with git-last-modified(1). Furthermore, users
> typically have a hard time to figure out the correct parameters.
>
> This series changes the behavior to something that intuitively makes
> more sense.
>
> The change of the default behavior is done in the last commit, in the
> lead-up commits changes in the documentation are made.
>
> [1]: <f0c508cc-5c6b-4c4b-a3f3-0cdd8d1071e5@app.fastmail.com>
> [2]: <03f96860-29fc-42a7-a220-c3ec65eb8516@codeberg.org>
>
> ---
> Changes in v2:
> - Change the default --max-depth.
> - Split up commits in smaller pieces.
> - Add more testss
> - Link to v1:
> https://patch.msgid.link/20251126-toon-last-modified-zzzz-v1-0-608350df0caa@iotcl.com
>
> Cc: "Kristoffer Haugsbakk" <kristofferhaugsbakk@fastmail.com>
> Cc: Gusted <gusted@codeberg.org>

I think the Cc list got mixed up.

>
> ---
>[snip]

^ permalink raw reply	[flat|nested] 36+ messages in thread

* Re: [PATCH v2 2/5] last-modified: add option '-z' to help output
  2026-01-16 13:22   ` [PATCH v2 2/5] last-modified: add option '-z' to help output Toon Claes
@ 2026-01-16 18:31     ` Junio C Hamano
  0 siblings, 0 replies; 36+ messages in thread
From: Junio C Hamano @ 2026-01-16 18:31 UTC (permalink / raw)
  To: Toon Claes; +Cc: git, Kristoffer Haugsbakk, Gusted

Toon Claes <toon@iotcl.com> writes:

> +		OPT_BOOL('z', NULL, &lm.null_termination,
> +			N_("lines are separated with NUL character")),

As this is a name of a file-scope static thing, I do not think it
matters too much, but "&lm.null_termination" (instead of
"nul_termination") that controls use of "NUL character" somewhat
bothers me ;-).

^ permalink raw reply	[flat|nested] 36+ messages in thread

* Re: [PATCH v2 4/5] last-modified: add option '--max-depth' to help output
  2026-01-16 13:22   ` [PATCH v2 4/5] last-modified: add option '--max-depth' to help output Toon Claes
@ 2026-01-16 18:42     ` Junio C Hamano
  0 siblings, 0 replies; 36+ messages in thread
From: Junio C Hamano @ 2026-01-16 18:42 UTC (permalink / raw)
  To: Toon Claes; +Cc: git, Kristoffer Haugsbakk, Gusted

Toon Claes <toon@iotcl.com> writes:

> In previous commit option '--max-depth' was added to the documentation.
> To have it also appear in the help output of `git last-modified -h`,
> move the handling of '--max-depth' to parse_options() in
> builtin/last-modified.c itself.
>
> It enables us to change default behavior in a subsequent commit.

This split of the max-depth into two steps does not look right to
me.  If we are handling the option ourselves here, shouldn't we do
so in the same step, so that "git cmd -h" and the synopsis section
of the documentation match?

^ permalink raw reply	[flat|nested] 36+ messages in thread

* Re: [PATCH v2 5/5] last-modified: change default max-depth to 0
  2026-01-16 13:22   ` [PATCH v2 5/5] last-modified: change default max-depth to 0 Toon Claes
@ 2026-01-16 18:55     ` Junio C Hamano
  0 siblings, 0 replies; 36+ messages in thread
From: Junio C Hamano @ 2026-01-16 18:55 UTC (permalink / raw)
  To: Toon Claes; +Cc: git, Kristoffer Haugsbakk, Gusted

Toon Claes <toon@iotcl.com> writes:

> diff --git a/Documentation/git-last-modified.adoc b/Documentation/git-last-modified.adoc
> index a3992db3f2..57136baf3b 100644
> --- a/Documentation/git-last-modified.adoc
> +++ b/Documentation/git-last-modified.adoc
> @@ -27,6 +27,7 @@ OPTIONS
>  `--recursive`::
>  	Instead of showing tree entries, step into subtrees and show all entries
>  	inside them recursively.
> +	This is identical as setting `--max-depth=-1`.

When I heard that the default value of max-depth will be 0, the
first thing I wondered was "how would I spell unlimited in the new
world order?", and the documentation for "--max-depth", not
"--recursive", would have been the place I expected to fish for
necessary information.

Over there, there is "A negative value means no limit", so saying
"identical as setting --max-depth to a negative value" here would
match the description over there better, or the user will be left
wonderign if "-1" is merely an example that is negative, or if it is
more special than other negative values and if so in what way.

> @@ -36,7 +37,7 @@ OPTIONS
>  `--max-depth=<depth>`::
>  	For each pathspec given on the command line, descend at most `<depth>`
>  	levels of directories. A negative value means no limit.
> -	Setting a positive value implies `--recursive`.
> +	The default depth is 0.
>  	Cannot be combined with wildcards in the pathspec.

> diff --git a/builtin/last-modified.c b/builtin/last-modified.c
> index 842700bc6a..a10e711beb 100644
> --- a/builtin/last-modified.c
> +++ b/builtin/last-modified.c
> @@ -481,14 +481,10 @@ static int last_modified_init(struct last_modified *lm, struct repository *r,
>  	lm->rev.no_commit_id = 1;
>  	lm->rev.diff = 1;
>  	lm->rev.diffopt.flags.no_recursive_diff_tree_combined = 1;
> -	lm->rev.diffopt.flags.recursive = lm->recursive;
> +	lm->rev.diffopt.flags.recursive = 1;

Hmph, so this will always be recursive?

>  	lm->rev.diffopt.flags.tree_in_recursive = lm->show_trees;
> +	lm->rev.diffopt.max_depth = lm->max_depth;
> +	lm->rev.diffopt.max_depth_valid = !lm->recursive && lm->max_depth >= 0;

Not saying --recursive would keep lm->recursive==0 and non-negative
value of --max-depth will flip max_depth_valid on.  Saying
"--recursive" or giving a negative "--max-value" would make
max_depth_valid false, and it allows traversal all the way down to
leaves.

It may be correct, but feels quite convoluted.  I wonder if we can
get rid of lm->recursive altogether now as a clean-up, and have
"--recursive" truly do what the documentation claimed is equivalent
earlier, i.e. OPT_SET_INT(0, "recursive", &lm.max_depth, -1).  Would
that simplify the logic a bit and make it easier to reason about the
logic around here, I wonder?


^ permalink raw reply	[flat|nested] 36+ messages in thread

* Re: [PATCH v2 0/5] Change git-last-modified(1) default behavior and add documentation
  2026-01-16 13:34   ` [PATCH v2 0/5] Change git-last-modified(1) default behavior and add documentation Kristoffer Haugsbakk
@ 2026-01-20 10:44     ` Toon Claes
  0 siblings, 0 replies; 36+ messages in thread
From: Toon Claes @ 2026-01-20 10:44 UTC (permalink / raw)
  To: Kristoffer Haugsbakk, git; +Cc: Gusted

"Kristoffer Haugsbakk" <kristofferhaugsbakk@fastmail.com> writes:

> On Fri, Jan 16, 2026, at 14:22, Toon Claes wrote:
>> Cc: "Kristoffer Haugsbakk" <kristofferhaugsbakk@fastmail.com>
>> Cc: Gusted <gusted@codeberg.org>
>
> I think the Cc list got mixed up.

I Cc'd you because of
https://lore.kernel.org/git/f0c508cc-5c6b-4c4b-a3f3-0cdd8d1071e5@app.fastmail.com/


-- 
Cheers,
Toon

^ permalink raw reply	[flat|nested] 36+ messages in thread

* [PATCH v3 0/4] Change git-last-modified(1) default behavior and add documentation
  2026-01-16 13:22 ` [PATCH v2 0/5] Change git-last-modified(1) default behavior and add documentation Toon Claes
                     ` (5 preceding siblings ...)
  2026-01-16 13:34   ` [PATCH v2 0/5] Change git-last-modified(1) default behavior and add documentation Kristoffer Haugsbakk
@ 2026-01-20 21:47   ` Toon Claes
  2026-01-20 21:47     ` [PATCH v3 1/4] last-modified: clarify in the docs the command takes a pathspec Toon Claes
                       ` (4 more replies)
  6 siblings, 5 replies; 36+ messages in thread
From: Toon Claes @ 2026-01-20 21:47 UTC (permalink / raw)
  To: git; +Cc: Gusted, Toon Claes

We have had several reports[1][2] from users that have been thoroughly
confused by the default behaviour of git-last-modified(1). Most
importantly, when using the command on trees other than the root tree
the requests are seemingly ignoring that request and instead blame the
top-level subtree. For example:

    $ git last-modified t/lib-bash.sh
    acdfea4394db8e8b42f48e36f7726d64a909a89d t

This result is completely unexpected and feels buggy to anybody who is
not deeply familiar with git-last-modified(1). Furthermore, users
typically have a hard time to figure out the correct parameters.

This series changes the behavior to something that intuitively makes
more sense.

The change of the default behavior is done in the last commit, in the
lead-up commits changes in the documentation are made.

[1]: <f0c508cc-5c6b-4c4b-a3f3-0cdd8d1071e5@app.fastmail.com>
[2]: <03f96860-29fc-42a7-a220-c3ec65eb8516@codeberg.org>

---
Changes in v3:
- Rejoined the two commits about `-z and the two about `--max-depth`
- In the end, drop `struct last_modified::recursive`. This ensures
  the effect of `--recursive` or `--max-depth` depends on whatever comes
  last.
- Added a commit to rename `<path>` to `<pathspec>` in the docs.
- Link to v2: https://patch.msgid.link/20260116-toon-last-modified-zzzz-v2-0-79e44f2806fe@iotcl.com

Changes in v2:
- Change the default --max-depth.
- Split up commits in smaller pieces.
- Add more testss
- Link to v1: https://patch.msgid.link/20251126-toon-last-modified-zzzz-v1-0-608350df0caa@iotcl.com

Cc: Gusted <gusted@codeberg.org>

---
Toon Claes (4):
      last-modified: clarify in the docs the command takes a pathspec
      last-modified: document option '-z'
      last-modified: document option '--max-depth'
      last-modified: change default max-depth to 0

 Documentation/git-last-modified.adoc | 45 ++++++++++++++++++++++++++++--------
 builtin/last-modified.c              | 25 ++++++++++++--------
 t/t8020-last-modified.sh             | 35 ++++++++++++++++++++++++++++
 3 files changed, 87 insertions(+), 18 deletions(-)

Range-diff versus v2:

-:  ---------- > 1:  1290dbb179 last-modified: clarify in the docs the command takes a pathspec
1:  2c9fa32799 ! 2:  8ba5c59ae8 last-modified: document NUL termination
    @@ Metadata
     Author: Toon Claes <toon@iotcl.com>
     
      ## Commit message ##
    -    last-modified: document NUL termination
    +    last-modified: document option '-z'
     
         The command git-last-modified(1) already recognizes the option '-z', and
         similar to many other commands this will make the output NUL-terminated
         instead of using newlines. Although, this option is missing from the
         documentation, so add it.
     
    +    In addition to that, to have '-z' also appear in the help output of `git
    +    last-modified -h`, move the handling of '-z' to parse_options() in
    +    builtin/last-modified.c itself.
    +
    +    Before, the parsing of option '-z' was done by diff_opt_parse(), which
    +    is called by setup_revisions(). That would fill in `struct
    +    diff_options::line_termination`, but that field was not used by the diff
    +    machinery itself. Thus it makes more sense to have the handling of that
    +    option completely in builtin/last-modified.c.
    +
         Signed-off-by: Toon Claes <toon@iotcl.com>
     
      ## Documentation/git-last-modified.adoc ##
    @@ Documentation/git-last-modified.adoc: git-last-modified - EXPERIMENTAL: Show whe
      SYNOPSIS
      --------
      [synopsis]
    --git last-modified [--recursive] [--show-trees] [<revision-range>] [[--] <path>...]
    +-git last-modified [--recursive] [--show-trees]
     +git last-modified [--recursive] [--show-trees] [-z]
    -+		  [<revision-range>] [[--] <path>...]
    + 		  [<revision-range>] [[--] <pathspec>...]
      
      DESCRIPTION
    - -----------
     @@ Documentation/git-last-modified.adoc: OPTIONS
      	Show tree entries even when recursing into them. It has no effect
      	without `--recursive`.
    @@ Documentation/git-last-modified.adoc: OPTIONS
      	Only traverse commits in the specified revision range. When no
      	`<revision-range>` is specified, it defaults to `HEAD` (i.e. the whole
     @@ Documentation/git-last-modified.adoc: OPTIONS
    - 	Without an optional path parameter, all files and subdirectories
    - 	in path traversal the are included in the output.
    + 	If no _<pathspec>_ is given, all files and subdirectories are included.
    + 	See linkgit:gitglossary[7] for details on pathspec syntax.
      
     +OUTPUT
     +------
    @@ Documentation/git-last-modified.adoc: OPTIONS
      linkgit:git-blame[1],
     
      ## builtin/last-modified.c ##
    +@@ builtin/last-modified.c: struct last_modified {
    + 	struct rev_info rev;
    + 	bool recursive;
    + 	bool show_trees;
    ++	bool nul_termination;
    + 
    + 	const char **all_paths;
    + 	size_t all_paths_nr;
    +@@ builtin/last-modified.c: static void last_modified_emit(struct last_modified *lm,
    + 		putchar('^');
    + 	printf("%s\t", oid_to_hex(&commit->object.oid));
    + 
    +-	if (lm->rev.diffopt.line_termination)
    +-		write_name_quoted(path, stdout, '\n');
    +-	else
    ++	if (lm->nul_termination)
    + 		printf("%s%c", path, '\0');
    ++	else
    ++		write_name_quoted(path, stdout, '\n');
    + }
    + 
    + static void mark_path(const char *path, const struct object_id *oid,
     @@ builtin/last-modified.c: int cmd_last_modified(int argc, const char **argv, const char *prefix,
      	struct last_modified lm = { 0 };
      
      	const char * const last_modified_usage[] = {
    --		N_("git last-modified [--recursive] [--show-trees] "
    --		   "[<revision-range>] [[--] <path>...]"),
    +-		N_("git last-modified [--recursive] [--show-trees]\n"
     +		N_("git last-modified [--recursive] [--show-trees] [-z]\n"
    -+		   "                  [<revision-range>] [[--] <path>...]"),
    + 		   "                  [<revision-range>] [[--] <pathspec>...]"),
      		NULL
      	};
    +@@ builtin/last-modified.c: int cmd_last_modified(int argc, const char **argv, const char *prefix,
    + 			 N_("recurse into subtrees")),
    + 		OPT_BOOL('t', "show-trees", &lm.show_trees,
    + 			 N_("show tree entries when recursing into subtrees")),
    ++		OPT_BOOL('z', NULL, &lm.nul_termination,
    ++			 N_("lines are separated with NUL character")),
    + 		OPT_END()
    + 	};
      
2:  04589b4ccc < -:  ---------- last-modified: add option '-z' to help output
3:  a45fe991ff < -:  ---------- last-modified: document option --max-depth
4:  3103084474 ! 3:  a1f0178263 last-modified: add option '--max-depth' to help output
    @@ Metadata
     Author: Toon Claes <toon@iotcl.com>
     
      ## Commit message ##
    -    last-modified: add option '--max-depth' to help output
    +    last-modified: document option '--max-depth'
    +
    +    Option --max-depth is supported by git-last-modified(1), because it was
    +    added to the diff machinery in a1dfa5448d (diff: teach tree-diff a
    +    max-depth parameter, 2025-08-07).
    +
    +    This option is useful for everyday use of the git-last-modified(1)
    +    command, so document it's existence in the man page.
     
    -    In previous commit option '--max-depth' was added to the documentation.
         To have it also appear in the help output of `git last-modified -h`,
         move the handling of '--max-depth' to parse_options() in
    -    builtin/last-modified.c itself.
    -
    -    It enables us to change default behavior in a subsequent commit.
    +    builtin/last-modified.c itself. This prepares for the change in default
    +    behavior in the next commit.
     
         Signed-off-by: Toon Claes <toon@iotcl.com>
     
    + ## Documentation/git-last-modified.adoc ##
    +@@ Documentation/git-last-modified.adoc: git-last-modified - EXPERIMENTAL: Show when files were last modified
    + SYNOPSIS
    + --------
    + [synopsis]
    +-git last-modified [--recursive] [--show-trees] [-z]
    ++git last-modified [--recursive] [--show-trees] [--max-depth=<depth>] [-z]
    + 		  [<revision-range>] [[--] <pathspec>...]
    + 
    + DESCRIPTION
    +@@ Documentation/git-last-modified.adoc: OPTIONS
    + 	Show tree entries even when recursing into them. It has no effect
    + 	without `--recursive`.
    + 
    ++`--max-depth=<depth>`::
    ++	For each pathspec given on the command line, traverse at most `<depth>`
    ++	levels into subtrees. A negative value means no limit.
    ++	The default is 0, which shows all paths matching the pathspec
    ++	without descending into subtrees.
    ++
    + `-z`::
    + 	Terminate each line with a _NUL_ character rather than a newline.
    + 
    +
      ## builtin/last-modified.c ##
     @@ builtin/last-modified.c: struct last_modified {
      	bool recursive;
      	bool show_trees;
    - 	bool null_termination;
    + 	bool nul_termination;
     +	int max_depth;
      
      	const char **all_paths;
    @@ builtin/last-modified.c: static int last_modified_init(struct last_modified *lm,
      	argc = setup_revisions(argc, argv, &lm->rev, NULL);
      	if (argc > 1) {
      		error(_("unknown last-modified argument: %s"), argv[1]);
    +@@ builtin/last-modified.c: int cmd_last_modified(int argc, const char **argv, const char *prefix,
    + 	struct last_modified lm = { 0 };
    + 
    + 	const char * const last_modified_usage[] = {
    +-		N_("git last-modified [--recursive] [--show-trees] [-z]\n"
    ++		N_("git last-modified [--recursive] [--show-trees] [--max-depth=<depth>] [-z]\n"
    + 		   "                  [<revision-range>] [[--] <pathspec>...]"),
    + 		NULL
    + 	};
     @@ builtin/last-modified.c: int cmd_last_modified(int argc, const char **argv, const char *prefix,
      			 N_("recurse into subtrees")),
      		OPT_BOOL('t', "show-trees", &lm.show_trees,
      			 N_("show tree entries when recursing into subtrees")),
     +		OPT_INTEGER_F(0, "max-depth", &lm.max_depth,
    -+			N_("maximum tree depth to recurse"), PARSE_OPT_NONEG),
    - 		OPT_BOOL('z', NULL, &lm.null_termination,
    - 			N_("lines are separated with NUL character")),
    ++			      N_("maximum tree depth to recurse"), PARSE_OPT_NONEG),
    + 		OPT_BOOL('z', NULL, &lm.nul_termination,
    + 			 N_("lines are separated with NUL character")),
      		OPT_END()
      	};
      
5:  3795e85ab5 ! 4:  81ce108196 last-modified: change default max-depth to 0
    @@ Commit message
     
      ## Documentation/git-last-modified.adoc ##
     @@ Documentation/git-last-modified.adoc: OPTIONS
    + 
    + `-r`::
      `--recursive`::
    - 	Instead of showing tree entries, step into subtrees and show all entries
    - 	inside them recursively.
    -+	This is identical as setting `--max-depth=-1`.
    +-	Instead of showing tree entries, step into subtrees and show all entries
    +-	inside them recursively.
    ++	Recursively traverse into all subtrees. By default, the command only
    ++	shows tree entries matching the `<pathspec>`. With this option, it
    ++	descends into subtrees and displays all entries within them.
    ++	Equivalent to `--max-depth=-1`.
      
      `-t`::
      `--show-trees`::
    -@@ Documentation/git-last-modified.adoc: OPTIONS
    - `--max-depth=<depth>`::
    - 	For each pathspec given on the command line, descend at most `<depth>`
    - 	levels of directories. A negative value means no limit.
    --	Setting a positive value implies `--recursive`.
    -+	The default depth is 0.
    - 	Cannot be combined with wildcards in the pathspec.
    +-	Show tree entries even when recursing into them. It has no effect
    +-	without `--recursive`.
    ++	Show tree entries even when recursing into them.
      
    - `-z`::
    + `--max-depth=<depth>`::
    + 	For each pathspec given on the command line, traverse at most `<depth>`
     
      ## builtin/last-modified.c ##
    +@@ builtin/last-modified.c: define_commit_slab(active_paths_for_commit, struct bitmap *);
    + struct last_modified {
    + 	struct hashmap paths;
    + 	struct rev_info rev;
    +-	bool recursive;
    + 	bool show_trees;
    + 	bool nul_termination;
    + 	int max_depth;
     @@ builtin/last-modified.c: static int last_modified_init(struct last_modified *lm, struct repository *r,
      	lm->rev.no_commit_id = 1;
      	lm->rev.diff = 1;
    @@ builtin/last-modified.c: static int last_modified_init(struct last_modified *lm,
     -		lm->rev.diffopt.max_depth_valid = 1;
     -	}
     +	lm->rev.diffopt.max_depth = lm->max_depth;
    -+	lm->rev.diffopt.max_depth_valid = !lm->recursive && lm->max_depth >= 0;
    ++	lm->rev.diffopt.max_depth_valid = lm->max_depth >= 0;
      
      	argc = setup_revisions(argc, argv, &lm->rev, NULL);
      	if (argc > 1) {
    +@@ builtin/last-modified.c: int cmd_last_modified(int argc, const char **argv, const char *prefix,
    + 	};
    + 
    + 	struct option last_modified_options[] = {
    +-		OPT_BOOL('r', "recursive", &lm.recursive,
    +-			 N_("recurse into subtrees")),
    ++		OPT_SET_INT('r', "recursive", &lm.max_depth,
    ++			    N_("recurse into subtrees"), -1),
    + 		OPT_BOOL('t', "show-trees", &lm.show_trees,
    + 			 N_("show tree entries when recursing into subtrees")),
    + 		OPT_INTEGER_F(0, "max-depth", &lm.max_depth,
     @@ builtin/last-modified.c: int cmd_last_modified(int argc, const char **argv, const char *prefix,
      		OPT_END()
      	};


---
base-commit: b5c409c40f1595e3e590760c6f14a16b6683e22c
change-id: 20251114-toon-last-modified-zzzz-af9c1be74fc4


^ permalink raw reply	[flat|nested] 36+ messages in thread

* [PATCH v3 1/4] last-modified: clarify in the docs the command takes a pathspec
  2026-01-20 21:47   ` [PATCH v3 0/4] " Toon Claes
@ 2026-01-20 21:47     ` Toon Claes
  2026-01-20 21:47     ` [PATCH v3 2/4] last-modified: document option '-z' Toon Claes
                       ` (3 subsequent siblings)
  4 siblings, 0 replies; 36+ messages in thread
From: Toon Claes @ 2026-01-20 21:47 UTC (permalink / raw)
  To: git; +Cc: Gusted, Toon Claes

The documentation mentions git-last-modified(1) takes `<path>...`, but
that argument actually accepts a pathspec. Reword the documentation to
reflect that.

Signed-off-by: Toon Claes <toon@iotcl.com>
---
 Documentation/git-last-modified.adoc | 11 ++++++-----
 builtin/last-modified.c              |  4 ++--
 2 files changed, 8 insertions(+), 7 deletions(-)

diff --git a/Documentation/git-last-modified.adoc b/Documentation/git-last-modified.adoc
index 602843e095..7c3fd844b8 100644
--- a/Documentation/git-last-modified.adoc
+++ b/Documentation/git-last-modified.adoc
@@ -9,7 +9,8 @@ git-last-modified - EXPERIMENTAL: Show when files were last modified
 SYNOPSIS
 --------
 [synopsis]
-git last-modified [--recursive] [--show-trees] [<revision-range>] [[--] <path>...]
+git last-modified [--recursive] [--show-trees]
+		  [<revision-range>] [[--] <pathspec>...]
 
 DESCRIPTION
 -----------
@@ -39,10 +40,10 @@ OPTIONS
 	spell `<revision-range>`, see the 'Specifying Ranges' section of
 	linkgit:gitrevisions[7].
 
-`[--] <path>...`::
-	For each _<path>_ given, the commit which last modified it is returned.
-	Without an optional path parameter, all files and subdirectories
-	in path traversal the are included in the output.
+`[--] <pathspec>...`::
+	Show the commit that last modified each path matching _<pathspec>_.
+	If no _<pathspec>_ is given, all files and subdirectories are included.
+	See linkgit:gitglossary[7] for details on pathspec syntax.
 
 SEE ALSO
 --------
diff --git a/builtin/last-modified.c b/builtin/last-modified.c
index c80f0535f6..e38e0bc4ca 100644
--- a/builtin/last-modified.c
+++ b/builtin/last-modified.c
@@ -510,8 +510,8 @@ int cmd_last_modified(int argc, const char **argv, const char *prefix,
 	struct last_modified lm = { 0 };
 
 	const char * const last_modified_usage[] = {
-		N_("git last-modified [--recursive] [--show-trees] "
-		   "[<revision-range>] [[--] <path>...]"),
+		N_("git last-modified [--recursive] [--show-trees]\n"
+		   "                  [<revision-range>] [[--] <pathspec>...]"),
 		NULL
 	};
 

-- 
2.52.0


^ permalink raw reply related	[flat|nested] 36+ messages in thread

* [PATCH v3 2/4] last-modified: document option '-z'
  2026-01-20 21:47   ` [PATCH v3 0/4] " Toon Claes
  2026-01-20 21:47     ` [PATCH v3 1/4] last-modified: clarify in the docs the command takes a pathspec Toon Claes
@ 2026-01-20 21:47     ` Toon Claes
  2026-01-20 21:47     ` [PATCH v3 3/4] last-modified: document option '--max-depth' Toon Claes
                       ` (2 subsequent siblings)
  4 siblings, 0 replies; 36+ messages in thread
From: Toon Claes @ 2026-01-20 21:47 UTC (permalink / raw)
  To: git; +Cc: Gusted, Toon Claes

The command git-last-modified(1) already recognizes the option '-z', and
similar to many other commands this will make the output NUL-terminated
instead of using newlines. Although, this option is missing from the
documentation, so add it.

In addition to that, to have '-z' also appear in the help output of `git
last-modified -h`, move the handling of '-z' to parse_options() in
builtin/last-modified.c itself.

Before, the parsing of option '-z' was done by diff_opt_parse(), which
is called by setup_revisions(). That would fill in `struct
diff_options::line_termination`, but that field was not used by the diff
machinery itself. Thus it makes more sense to have the handling of that
option completely in builtin/last-modified.c.

Signed-off-by: Toon Claes <toon@iotcl.com>
---
 Documentation/git-last-modified.adoc | 21 ++++++++++++++++++++-
 builtin/last-modified.c              | 11 +++++++----
 2 files changed, 27 insertions(+), 5 deletions(-)

diff --git a/Documentation/git-last-modified.adoc b/Documentation/git-last-modified.adoc
index 7c3fd844b8..3760fd33a1 100644
--- a/Documentation/git-last-modified.adoc
+++ b/Documentation/git-last-modified.adoc
@@ -9,7 +9,7 @@ git-last-modified - EXPERIMENTAL: Show when files were last modified
 SYNOPSIS
 --------
 [synopsis]
-git last-modified [--recursive] [--show-trees]
+git last-modified [--recursive] [--show-trees] [-z]
 		  [<revision-range>] [[--] <pathspec>...]
 
 DESCRIPTION
@@ -33,6 +33,9 @@ OPTIONS
 	Show tree entries even when recursing into them. It has no effect
 	without `--recursive`.
 
+`-z`::
+	Terminate each line with a _NUL_ character rather than a newline.
+
 `<revision-range>`::
 	Only traverse commits in the specified revision range. When no
 	`<revision-range>` is specified, it defaults to `HEAD` (i.e. the whole
@@ -45,6 +48,22 @@ OPTIONS
 	If no _<pathspec>_ is given, all files and subdirectories are included.
 	See linkgit:gitglossary[7] for details on pathspec syntax.
 
+OUTPUT
+------
+
+The output is in the format:
+
+------------
+ <oid> TAB <path> LF
+------------
+
+If a path contains any special characters, the path is C-style quoted. To
+avoid quoting, pass option `-z` to terminate each line with a NUL.
+
+------------
+ <oid> TAB <path> NUL
+------------
+
 SEE ALSO
 --------
 linkgit:git-blame[1],
diff --git a/builtin/last-modified.c b/builtin/last-modified.c
index e38e0bc4ca..4060abfad3 100644
--- a/builtin/last-modified.c
+++ b/builtin/last-modified.c
@@ -55,6 +55,7 @@ struct last_modified {
 	struct rev_info rev;
 	bool recursive;
 	bool show_trees;
+	bool nul_termination;
 
 	const char **all_paths;
 	size_t all_paths_nr;
@@ -165,10 +166,10 @@ static void last_modified_emit(struct last_modified *lm,
 		putchar('^');
 	printf("%s\t", oid_to_hex(&commit->object.oid));
 
-	if (lm->rev.diffopt.line_termination)
-		write_name_quoted(path, stdout, '\n');
-	else
+	if (lm->nul_termination)
 		printf("%s%c", path, '\0');
+	else
+		write_name_quoted(path, stdout, '\n');
 }
 
 static void mark_path(const char *path, const struct object_id *oid,
@@ -510,7 +511,7 @@ int cmd_last_modified(int argc, const char **argv, const char *prefix,
 	struct last_modified lm = { 0 };
 
 	const char * const last_modified_usage[] = {
-		N_("git last-modified [--recursive] [--show-trees]\n"
+		N_("git last-modified [--recursive] [--show-trees] [-z]\n"
 		   "                  [<revision-range>] [[--] <pathspec>...]"),
 		NULL
 	};
@@ -520,6 +521,8 @@ int cmd_last_modified(int argc, const char **argv, const char *prefix,
 			 N_("recurse into subtrees")),
 		OPT_BOOL('t', "show-trees", &lm.show_trees,
 			 N_("show tree entries when recursing into subtrees")),
+		OPT_BOOL('z', NULL, &lm.nul_termination,
+			 N_("lines are separated with NUL character")),
 		OPT_END()
 	};
 

-- 
2.52.0


^ permalink raw reply related	[flat|nested] 36+ messages in thread

* [PATCH v3 3/4] last-modified: document option '--max-depth'
  2026-01-20 21:47   ` [PATCH v3 0/4] " Toon Claes
  2026-01-20 21:47     ` [PATCH v3 1/4] last-modified: clarify in the docs the command takes a pathspec Toon Claes
  2026-01-20 21:47     ` [PATCH v3 2/4] last-modified: document option '-z' Toon Claes
@ 2026-01-20 21:47     ` Toon Claes
  2026-01-20 21:47     ` [PATCH v3 4/4] last-modified: change default max-depth to 0 Toon Claes
  2026-01-21 18:40     ` [PATCH v3 0/4] Change git-last-modified(1) default behavior and add documentation Junio C Hamano
  4 siblings, 0 replies; 36+ messages in thread
From: Toon Claes @ 2026-01-20 21:47 UTC (permalink / raw)
  To: git; +Cc: Gusted, Toon Claes

Option --max-depth is supported by git-last-modified(1), because it was
added to the diff machinery in a1dfa5448d (diff: teach tree-diff a
max-depth parameter, 2025-08-07).

This option is useful for everyday use of the git-last-modified(1)
command, so document it's existence in the man page.

To have it also appear in the help output of `git last-modified -h`,
move the handling of '--max-depth' to parse_options() in
builtin/last-modified.c itself. This prepares for the change in default
behavior in the next commit.

Signed-off-by: Toon Claes <toon@iotcl.com>
---
 Documentation/git-last-modified.adoc |  8 +++++++-
 builtin/last-modified.c              | 17 ++++++++++++++++-
 2 files changed, 23 insertions(+), 2 deletions(-)

diff --git a/Documentation/git-last-modified.adoc b/Documentation/git-last-modified.adoc
index 3760fd33a1..6f9b119bb6 100644
--- a/Documentation/git-last-modified.adoc
+++ b/Documentation/git-last-modified.adoc
@@ -9,7 +9,7 @@ git-last-modified - EXPERIMENTAL: Show when files were last modified
 SYNOPSIS
 --------
 [synopsis]
-git last-modified [--recursive] [--show-trees] [-z]
+git last-modified [--recursive] [--show-trees] [--max-depth=<depth>] [-z]
 		  [<revision-range>] [[--] <pathspec>...]
 
 DESCRIPTION
@@ -33,6 +33,12 @@ OPTIONS
 	Show tree entries even when recursing into them. It has no effect
 	without `--recursive`.
 
+`--max-depth=<depth>`::
+	For each pathspec given on the command line, traverse at most `<depth>`
+	levels into subtrees. A negative value means no limit.
+	The default is 0, which shows all paths matching the pathspec
+	without descending into subtrees.
+
 `-z`::
 	Terminate each line with a _NUL_ character rather than a newline.
 
diff --git a/builtin/last-modified.c b/builtin/last-modified.c
index 4060abfad3..28fc77a0f8 100644
--- a/builtin/last-modified.c
+++ b/builtin/last-modified.c
@@ -56,6 +56,7 @@ struct last_modified {
 	bool recursive;
 	bool show_trees;
 	bool nul_termination;
+	int max_depth;
 
 	const char **all_paths;
 	size_t all_paths_nr;
@@ -483,6 +484,12 @@ static int last_modified_init(struct last_modified *lm, struct repository *r,
 	lm->rev.diffopt.flags.recursive = lm->recursive;
 	lm->rev.diffopt.flags.tree_in_recursive = lm->show_trees;
 
+	if (lm->max_depth >= 0) {
+		lm->rev.diffopt.flags.recursive = 1;
+		lm->rev.diffopt.max_depth = lm->max_depth;
+		lm->rev.diffopt.max_depth_valid = 1;
+	}
+
 	argc = setup_revisions(argc, argv, &lm->rev, NULL);
 	if (argc > 1) {
 		error(_("unknown last-modified argument: %s"), argv[1]);
@@ -511,7 +518,7 @@ int cmd_last_modified(int argc, const char **argv, const char *prefix,
 	struct last_modified lm = { 0 };
 
 	const char * const last_modified_usage[] = {
-		N_("git last-modified [--recursive] [--show-trees] [-z]\n"
+		N_("git last-modified [--recursive] [--show-trees] [--max-depth=<depth>] [-z]\n"
 		   "                  [<revision-range>] [[--] <pathspec>...]"),
 		NULL
 	};
@@ -521,11 +528,19 @@ int cmd_last_modified(int argc, const char **argv, const char *prefix,
 			 N_("recurse into subtrees")),
 		OPT_BOOL('t', "show-trees", &lm.show_trees,
 			 N_("show tree entries when recursing into subtrees")),
+		OPT_INTEGER_F(0, "max-depth", &lm.max_depth,
+			      N_("maximum tree depth to recurse"), PARSE_OPT_NONEG),
 		OPT_BOOL('z', NULL, &lm.nul_termination,
 			 N_("lines are separated with NUL character")),
 		OPT_END()
 	};
 
+	/*
+	 * Set the default of a max-depth to "unset". This will change in a
+	 * subsequent commit.
+	 */
+	lm.max_depth = -1;
+
 	argc = parse_options(argc, argv, prefix, last_modified_options,
 			     last_modified_usage,
 			     PARSE_OPT_KEEP_ARGV0 | PARSE_OPT_KEEP_UNKNOWN_OPT |

-- 
2.52.0


^ permalink raw reply related	[flat|nested] 36+ messages in thread

* [PATCH v3 4/4] last-modified: change default max-depth to 0
  2026-01-20 21:47   ` [PATCH v3 0/4] " Toon Claes
                       ` (2 preceding siblings ...)
  2026-01-20 21:47     ` [PATCH v3 3/4] last-modified: document option '--max-depth' Toon Claes
@ 2026-01-20 21:47     ` Toon Claes
  2026-01-25 11:24       ` Kristoffer Haugsbakk
  2026-01-21 18:40     ` [PATCH v3 0/4] Change git-last-modified(1) default behavior and add documentation Junio C Hamano
  4 siblings, 1 reply; 36+ messages in thread
From: Toon Claes @ 2026-01-20 21:47 UTC (permalink / raw)
  To: git; +Cc: Gusted, Toon Claes

By default git-last-modified(1) doesn't recurse into subtrees. So when
the pathspec contained a path in a subtree, the command would only print
the commit information about the parent tree of the path, like:

    $ git last-modified -- path/file
    aaa0aab1bbb2bcc3ccc4ddd5dde6eee7eff8fff9	path

Change the default behavior to give commit information about the exact
path instead:

    $ git last-modified -- path/file
    aaa0aab1bbb2bcc3ccc4ddd5dde6eee7eff8fff9	path/file

To achieve this, the default max-depth is changed to 0 and recursive is
always enabled.

The handling of option '-r' is modified to disable a max-depth,
resulting in the behavior of this option to remain unchanged.

No existing tests were modified, because there didn't exist any tests
covering the example above. But more tests are added to cover this now.

Signed-off-by: Toon Claes <toon@iotcl.com>
---
 Documentation/git-last-modified.adoc |  9 +++++----
 builtin/last-modified.c              | 21 +++++----------------
 t/t8020-last-modified.sh             | 35 +++++++++++++++++++++++++++++++++++
 3 files changed, 45 insertions(+), 20 deletions(-)

diff --git a/Documentation/git-last-modified.adoc b/Documentation/git-last-modified.adoc
index 6f9b119bb6..d7d16fc4f7 100644
--- a/Documentation/git-last-modified.adoc
+++ b/Documentation/git-last-modified.adoc
@@ -25,13 +25,14 @@ OPTIONS
 
 `-r`::
 `--recursive`::
-	Instead of showing tree entries, step into subtrees and show all entries
-	inside them recursively.
+	Recursively traverse into all subtrees. By default, the command only
+	shows tree entries matching the `<pathspec>`. With this option, it
+	descends into subtrees and displays all entries within them.
+	Equivalent to `--max-depth=-1`.
 
 `-t`::
 `--show-trees`::
-	Show tree entries even when recursing into them. It has no effect
-	without `--recursive`.
+	Show tree entries even when recursing into them.
 
 `--max-depth=<depth>`::
 	For each pathspec given on the command line, traverse at most `<depth>`
diff --git a/builtin/last-modified.c b/builtin/last-modified.c
index 28fc77a0f8..f7f4c5109c 100644
--- a/builtin/last-modified.c
+++ b/builtin/last-modified.c
@@ -53,7 +53,6 @@ define_commit_slab(active_paths_for_commit, struct bitmap *);
 struct last_modified {
 	struct hashmap paths;
 	struct rev_info rev;
-	bool recursive;
 	bool show_trees;
 	bool nul_termination;
 	int max_depth;
@@ -481,14 +480,10 @@ static int last_modified_init(struct last_modified *lm, struct repository *r,
 	lm->rev.no_commit_id = 1;
 	lm->rev.diff = 1;
 	lm->rev.diffopt.flags.no_recursive_diff_tree_combined = 1;
-	lm->rev.diffopt.flags.recursive = lm->recursive;
+	lm->rev.diffopt.flags.recursive = 1;
 	lm->rev.diffopt.flags.tree_in_recursive = lm->show_trees;
-
-	if (lm->max_depth >= 0) {
-		lm->rev.diffopt.flags.recursive = 1;
-		lm->rev.diffopt.max_depth = lm->max_depth;
-		lm->rev.diffopt.max_depth_valid = 1;
-	}
+	lm->rev.diffopt.max_depth = lm->max_depth;
+	lm->rev.diffopt.max_depth_valid = lm->max_depth >= 0;
 
 	argc = setup_revisions(argc, argv, &lm->rev, NULL);
 	if (argc > 1) {
@@ -524,8 +519,8 @@ int cmd_last_modified(int argc, const char **argv, const char *prefix,
 	};
 
 	struct option last_modified_options[] = {
-		OPT_BOOL('r', "recursive", &lm.recursive,
-			 N_("recurse into subtrees")),
+		OPT_SET_INT('r', "recursive", &lm.max_depth,
+			    N_("recurse into subtrees"), -1),
 		OPT_BOOL('t', "show-trees", &lm.show_trees,
 			 N_("show tree entries when recursing into subtrees")),
 		OPT_INTEGER_F(0, "max-depth", &lm.max_depth,
@@ -535,12 +530,6 @@ int cmd_last_modified(int argc, const char **argv, const char *prefix,
 		OPT_END()
 	};
 
-	/*
-	 * Set the default of a max-depth to "unset". This will change in a
-	 * subsequent commit.
-	 */
-	lm.max_depth = -1;
-
 	argc = parse_options(argc, argv, prefix, last_modified_options,
 			     last_modified_usage,
 			     PARSE_OPT_KEEP_ARGV0 | PARSE_OPT_KEEP_UNKNOWN_OPT |
diff --git a/t/t8020-last-modified.sh b/t/t8020-last-modified.sh
index 50f4312f71..3944d2e153 100755
--- a/t/t8020-last-modified.sh
+++ b/t/t8020-last-modified.sh
@@ -93,6 +93,41 @@ test_expect_success 'last-modified subdir recursive' '
 	EOF
 '
 
+test_expect_success 'last-modified subdir non-recursive' '
+	check_last_modified a <<-\EOF
+	3 a
+	EOF
+'
+
+test_expect_success 'last-modified path in subdir non-recursive' '
+	check_last_modified a/file <<-\EOF
+	2 a/file
+	EOF
+'
+
+test_expect_success 'last-modified subdir with wildcard non-recursive' '
+	check_last_modified a/* <<-\EOF
+	3 a/b
+	2 a/file
+	EOF
+'
+
+test_expect_success 'last-modified with negative max-depth' '
+	check_last_modified --max-depth=-1 <<-\EOF
+	3 a/b/file
+	2 a/file
+	1 file
+	EOF
+'
+
+test_expect_success 'last-modified with max-depth of 1' '
+	check_last_modified --max-depth=1 <<-\EOF
+	3 a/b
+	2 a/file
+	1 file
+	EOF
+'
+
 test_expect_success 'last-modified from non-HEAD commit' '
 	check_last_modified HEAD^ <<-\EOF
 	2 a

-- 
2.52.0


^ permalink raw reply related	[flat|nested] 36+ messages in thread

* Re: [PATCH v3 0/4] Change git-last-modified(1) default behavior and add documentation
  2026-01-20 21:47   ` [PATCH v3 0/4] " Toon Claes
                       ` (3 preceding siblings ...)
  2026-01-20 21:47     ` [PATCH v3 4/4] last-modified: change default max-depth to 0 Toon Claes
@ 2026-01-21 18:40     ` Junio C Hamano
  2026-02-03  9:58       ` Karthik Nayak
  4 siblings, 1 reply; 36+ messages in thread
From: Junio C Hamano @ 2026-01-21 18:40 UTC (permalink / raw)
  To: Toon Claes; +Cc: git, Gusted

Toon Claes <toon@iotcl.com> writes:

> Changes in v3:
> - Rejoined the two commits about `-z and the two about `--max-depth`
> - In the end, drop `struct last_modified::recursive`. This ensures
>   the effect of `--recursive` or `--max-depth` depends on whatever comes
>   last.
> - Added a commit to rename `<path>` to `<pathspec>` in the docs.
> - Link to v2: https://patch.msgid.link/20260116-toon-last-modified-zzzz-v2-0-79e44f2806fe@iotcl.com

This round looked reasonable to me.  Shall we mark it for 'next'?

Thanks.

^ permalink raw reply	[flat|nested] 36+ messages in thread

* Re: [PATCH v3 4/4] last-modified: change default max-depth to 0
  2026-01-20 21:47     ` [PATCH v3 4/4] last-modified: change default max-depth to 0 Toon Claes
@ 2026-01-25 11:24       ` Kristoffer Haugsbakk
  0 siblings, 0 replies; 36+ messages in thread
From: Kristoffer Haugsbakk @ 2026-01-25 11:24 UTC (permalink / raw)
  To: Toon Claes, git; +Cc: Gusted

On Tue, Jan 20, 2026, at 22:47, Toon Claes wrote:
> By default git-last-modified(1) doesn't recurse into subtrees. So when
> the pathspec contained a path in a subtree, the command would only print
> the commit information about the parent tree of the path, like:
>
>     $ git last-modified -- path/file
>     aaa0aab1bbb2bcc3ccc4ddd5dde6eee7eff8fff9	path
>
> Change the default behavior to give commit information about the exact
> path instead:
>
>     $ git last-modified -- path/file
>     aaa0aab1bbb2bcc3ccc4ddd5dde6eee7eff8fff9	path/file
>
> To achieve this, the default max-depth is changed to 0 and recursive is
> always enabled.
>
> The handling of option '-r' is modified to disable a max-depth,
> resulting in the behavior of this option to remain unchanged.
>
> No existing tests were modified, because there didn't exist any tests
> covering the example above. But more tests are added to cover this now.
>
> Signed-off-by: Toon Claes <toon@iotcl.com>
> ---

Thanks. I think this makes more sense.

>[snip]
> --- a/Documentation/git-last-modified.adoc
> +++ b/Documentation/git-last-modified.adoc
> @@ -25,13 +25,14 @@ OPTIONS
>
>  `-r`::
>  `--recursive`::
> -	Instead of showing tree entries, step into subtrees and show all entries
> -	inside them recursively.
> +	Recursively traverse into all subtrees. By default, the command only
> +	shows tree entries matching the `<pathspec>`. With this option, it

This should be s/`<pathspec>`/_<pathspec>_ since this is a `[synopsis]`
doc. But this is in `next` so it will have to be left for another time.

>[snip]

^ permalink raw reply	[flat|nested] 36+ messages in thread

* Re: [PATCH v3 0/4] Change git-last-modified(1) default behavior and add documentation
  2026-01-21 18:40     ` [PATCH v3 0/4] Change git-last-modified(1) default behavior and add documentation Junio C Hamano
@ 2026-02-03  9:58       ` Karthik Nayak
  2026-02-03 17:42         ` Junio C Hamano
  0 siblings, 1 reply; 36+ messages in thread
From: Karthik Nayak @ 2026-02-03  9:58 UTC (permalink / raw)
  To: Junio C Hamano, Toon Claes; +Cc: git, Gusted

[-- Attachment #1: Type: text/plain, Size: 663 bytes --]

Junio C Hamano <gitster@pobox.com> writes:

> Toon Claes <toon@iotcl.com> writes:
>
>> Changes in v3:
>> - Rejoined the two commits about `-z and the two about `--max-depth`
>> - In the end, drop `struct last_modified::recursive`. This ensures
>>   the effect of `--recursive` or `--max-depth` depends on whatever comes
>>   last.
>> - Added a commit to rename `<path>` to `<pathspec>` in the docs.
>> - Link to v2: https://patch.msgid.link/20260116-toon-last-modified-zzzz-v2-0-79e44f2806fe@iotcl.com
>
> This round looked reasonable to me.  Shall we mark it for 'next'?
>
> Thanks.

I also did a round of review and think it is indeed ready for 'next'.

Thanks

[-- Attachment #2: signature.asc --]
[-- Type: application/pgp-signature, Size: 690 bytes --]

^ permalink raw reply	[flat|nested] 36+ messages in thread

* Re: [PATCH v3 0/4] Change git-last-modified(1) default behavior and add documentation
  2026-02-03  9:58       ` Karthik Nayak
@ 2026-02-03 17:42         ` Junio C Hamano
  0 siblings, 0 replies; 36+ messages in thread
From: Junio C Hamano @ 2026-02-03 17:42 UTC (permalink / raw)
  To: Karthik Nayak; +Cc: Toon Claes, git, Gusted

Karthik Nayak <karthik.188@gmail.com> writes:

> Junio C Hamano <gitster@pobox.com> writes:
>
>> Toon Claes <toon@iotcl.com> writes:
>>
>>> Changes in v3:
>>> - Rejoined the two commits about `-z and the two about `--max-depth`
>>> - In the end, drop `struct last_modified::recursive`. This ensures
>>>   the effect of `--recursive` or `--max-depth` depends on whatever comes
>>>   last.
>>> - Added a commit to rename `<path>` to `<pathspec>` in the docs.
>>> - Link to v2: https://patch.msgid.link/20260116-toon-last-modified-zzzz-v2-0-79e44f2806fe@iotcl.com
>>
>> This round looked reasonable to me.  Shall we mark it for 'next'?
>>
>> Thanks.
>
> I also did a round of review and think it is indeed ready for 'next'.

;-)

^ permalink raw reply	[flat|nested] 36+ messages in thread

end of thread, other threads:[~2026-02-03 17:42 UTC | newest]

Thread overview: 36+ messages (download: mbox.gz follow: Atom feed
-- links below jump to the message on this page --
2025-11-26  6:09 [PATCH 0/3] Expand and enhance git-last-modified(1) documentation Toon Claes
2025-11-26  6:09 ` [PATCH 1/3] last-modified: handle and document NUL termination Toon Claes
2025-11-26 13:03   ` Karthik Nayak
2025-11-26 16:57   ` Junio C Hamano
2025-11-28 18:50     ` Toon Claes
2025-12-01 10:32       ` Patrick Steinhardt
2025-11-26  6:09 ` [PATCH 2/3] last-modified: document option --max-depth Toon Claes
2025-11-26 13:31   ` Karthik Nayak
2026-01-16 12:13     ` Toon Claes
2025-11-26 19:49   ` Junio C Hamano
2025-11-28 18:51     ` Toon Claes
2025-11-26  6:09 ` [PATCH 3/3] last-modified: better document how depth in handled Toon Claes
2025-11-26 17:38   ` Eric Sunshine
2025-12-01 10:32   ` Patrick Steinhardt
2025-12-02 11:01     ` Toon Claes
2025-12-02 17:14       ` Patrick Steinhardt
2026-01-16 13:22 ` [PATCH v2 0/5] Change git-last-modified(1) default behavior and add documentation Toon Claes
2026-01-16 13:22   ` [PATCH v2 1/5] last-modified: document NUL termination Toon Claes
2026-01-16 13:22   ` [PATCH v2 2/5] last-modified: add option '-z' to help output Toon Claes
2026-01-16 18:31     ` Junio C Hamano
2026-01-16 13:22   ` [PATCH v2 3/5] last-modified: document option --max-depth Toon Claes
2026-01-16 13:22   ` [PATCH v2 4/5] last-modified: add option '--max-depth' to help output Toon Claes
2026-01-16 18:42     ` Junio C Hamano
2026-01-16 13:22   ` [PATCH v2 5/5] last-modified: change default max-depth to 0 Toon Claes
2026-01-16 18:55     ` Junio C Hamano
2026-01-16 13:34   ` [PATCH v2 0/5] Change git-last-modified(1) default behavior and add documentation Kristoffer Haugsbakk
2026-01-20 10:44     ` Toon Claes
2026-01-20 21:47   ` [PATCH v3 0/4] " Toon Claes
2026-01-20 21:47     ` [PATCH v3 1/4] last-modified: clarify in the docs the command takes a pathspec Toon Claes
2026-01-20 21:47     ` [PATCH v3 2/4] last-modified: document option '-z' Toon Claes
2026-01-20 21:47     ` [PATCH v3 3/4] last-modified: document option '--max-depth' Toon Claes
2026-01-20 21:47     ` [PATCH v3 4/4] last-modified: change default max-depth to 0 Toon Claes
2026-01-25 11:24       ` Kristoffer Haugsbakk
2026-01-21 18:40     ` [PATCH v3 0/4] Change git-last-modified(1) default behavior and add documentation Junio C Hamano
2026-02-03  9:58       ` Karthik Nayak
2026-02-03 17:42         ` Junio C Hamano

This is a public inbox, see mirroring instructions
for how to clone and mirror all data and code used for this inbox