]> git.ipfire.org Git - thirdparty/git.git/commitdiff
merge-ort: propagate callback errors from traverse_trees_wrapper()
authorElijah Newren <newren@gmail.com>
Sun, 14 Jun 2026 06:37:22 +0000 (06:37 +0000)
committerJunio C Hamano <gitster@pobox.com>
Sun, 14 Jun 2026 14:50:47 +0000 (07:50 -0700)
traverse_trees_wrapper() saves entries from a first pass through
traverse_trees() and then replays them through the real callback
(collect_merge_info_callback).  However, the replay loop silently
discards the callback return value.  This is not a deferred error;
it is an ignored error.

Today the only originator of a negative return in this entire call
graph is traverse_trees()'s "exceeded maximum allowed tree depth"
check; everything else (collect_merge_info_callback,
traverse_trees_wrapper, the inner traverse_trees recursion) only
relays that.  So in current Git, the visible effect of dropping the
replay callback's return value is narrow but bad: a tree nested past
core.maxTreeDepth has its -1 swallowed, the subtree below the limit
is silently pruned, and the merge completes as if that were the
correct result.

A later patch in this series will teach collect_merge_info_callback()
to return -1 on an additional path -- detecting duplicate
entries in malformed trees -- which is similarly handled today by
just ignoring the problem (resulting in mostly a "last one wins" rule,
though the non-last entry can mutate various state flags).

Capture the return value, stop the loop on negative returns, and
propagate the error to the caller.  The callback returns a positive mask
value on success, so normalize non-negative returns to
0 for the caller.

Signed-off-by: Elijah Newren <newren@gmail.com>
Signed-off-by: Junio C Hamano <gitster@pobox.com>
merge-ort.c

index 00923ce3cd749b4dfe379741117eac693c7d3bfc..4b8e32209d9b3ad04d840087aef97ff703f2a35d 100644 (file)
@@ -1008,18 +1008,20 @@ static int traverse_trees_wrapper(struct index_state *istate,
        info->traverse_path = renames->callback_data_traverse_path;
        info->fn = old_fn;
        for (i = old_offset; i < renames->callback_data_nr; ++i) {
-               info->fn(n,
-                        renames->callback_data[i].mask,
-                        renames->callback_data[i].dirmask,
-                        renames->callback_data[i].names,
-                        info);
+               ret = info->fn(n,
+                              renames->callback_data[i].mask,
+                              renames->callback_data[i].dirmask,
+                              renames->callback_data[i].names,
+                              info);
+               if (ret < 0)
+                       break;
        }
 
        renames->callback_data_nr = old_offset;
        free(renames->callback_data_traverse_path);
        renames->callback_data_traverse_path = old_callback_data_traverse_path;
        info->traverse_path = NULL;
-       return 0;
+       return ret < 0 ? ret : 0;
 }
 
 static void setup_path_info(struct merge_options *opt,