From e6c06e87a255995d2e7ead2b8e49e46e29a724fb Mon Sep 17 00:00:00 2001 From: Toon Claes Date: Thu, 18 Sep 2025 10:00:08 +0200 Subject: last-modified: fix bug when some paths remain unhandled The recently introduced new subcommand git-last-modified(1) runs into an error in some scenarios. It then would exit with the message: BUG: paths remaining beyond boundary in last-modified This seems to happens for example when criss-cross merges are involved. In that scenario, the function diff_tree_combined() gets called. The function diff_tree_combined() copies the `struct diff_options` from the input `struct rev_info` to override some flags. One flag is `recursive`, which is always set to 1. This has been the case since the inception of this function in af3feefa1d (diff-tree -c: show a merge commit a bit more sensibly., 2006-01-24). This behavior is incompatible with git-last-modified(1), when called non-recursive (which is the default). The last-modified machinery uses a hashmap for all the paths it wants to get the last-modified commit for. Through log_tree_commit() the callback mark_path() is called. The diff machinery uses diff_tree_combined() internally, and due to it's recursive behavior the callback receives entries inside subtrees, but not the subtree entries themselves. So a directory is never expelled from the hashmap, and the BUG() statement gets hit. Because there are many callers calling into diff_tree_combined(), both directly and indirectly, we cannot simply change it's behavior. Instead, add a flag `no_recursive_diff_tree_combined` which supresses the behavior of diff_tree_combined() to override `recursive` and set this flag in builtin/last-modified.c. Signed-off-by: Toon Claes Signed-off-by: Junio C Hamano --- combine-diff.c | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) (limited to 'combine-diff.c') diff --git a/combine-diff.c b/combine-diff.c index 3878faabe7..e779b86e0b 100644 --- a/combine-diff.c +++ b/combine-diff.c @@ -1515,8 +1515,9 @@ void diff_tree_combined(const struct object_id *oid, diffopts = *opt; copy_pathspec(&diffopts.pathspec, &opt->pathspec); - diffopts.flags.recursive = 1; diffopts.flags.allow_external = 0; + if (!opt->flags.no_recursive_diff_tree_combined) + diffopts.flags.recursive = 1; /* find set of paths that everybody touches * -- cgit v1.2.3