From: Toon Claes Date: Fri, 28 Nov 2025 18:50:30 GMT Subject: Re: [PATCH 1/3] last-modified: handle and document NUL termination Message-ID: <87tsye0z61.fsf@iotcl.com> In-Reply-To: Junio C Hamano writes: > Toon Claes 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] " >> "[] [[--] ...]"), >> 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