Merge branch 'tc/last-modified-options-cleanup' into jch

The "-z" and "--max-depth" documentation (and implementation of
"-z") in the "git last-modified" command have been updated.

* tc/last-modified-options-cleanup:
  fixup! last-modified: document option --max-depth
  last-modified: document how depth is handled better
  last-modified: document option --max-depth
  last-modified: handle and document NUL termination
This commit is contained in:
Junio C Hamano
2025-12-12 15:53:07 +09:00
2 changed files with 91 additions and 5 deletions

View File

@@ -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] [--max-depth=<depth>] [-z]
[<revision-range>] [[--] <path>...]
DESCRIPTION
-----------
@@ -26,12 +27,23 @@ 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`::
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.
See the section "NOTES ABOUT DEPTH" below for more details.
`-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 +56,63 @@ 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
------------
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],

View File

@@ -23,6 +23,11 @@
#define PARENT1 (1u<<16) /* used instead of SEEN */
#define PARENT2 (1u<<17) /* used instead of BOTTOM, BOUNDARY */
#define LAST_MODIFIED_INIT { \
.line_termination = '\n', \
.max_depth = -1, \
}
struct last_modified_entry {
struct hashmap_entry hashent;
struct object_id oid;
@@ -55,6 +60,8 @@ struct last_modified {
struct rev_info rev;
bool recursive;
bool show_trees;
int line_termination;
int max_depth;
const char **all_paths;
size_t all_paths_nr;
@@ -165,7 +172,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');
@@ -482,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]);
@@ -507,11 +520,11 @@ 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] "
"[<revision-range>] [[--] <path>...]"),
N_("git last-modified [--recursive] [--show-trees] [--max-depth=<depth>] [-z]\n"
" [<revision-range>] [[--] <path>...]"),
NULL
};
@@ -520,6 +533,10 @@ 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()
};