* [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
* 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 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 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 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
* [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
* 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 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
* 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 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
* [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 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 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
* [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
* 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
* [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
* 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
* [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 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: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 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 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-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 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