]> git.ipfire.org Git - thirdparty/git.git/commitdiff
last-modified: handle repo_parse_commit() failures
authorJohannes Schindelin <johannes.schindelin@gmx.de>
Tue, 14 Jul 2026 22:48:38 +0000 (22:48 +0000)
committerJunio C Hamano <gitster@pobox.com>
Wed, 15 Jul 2026 01:02:01 +0000 (18:02 -0700)
last_modified_run() and process_parent() call repo_parse_commit()
without checking the return value at three sites. When a commit
object is corrupt or unavailable (e.g., a shallow clone boundary
or a missing object in a partial clone), the parse fails and the
commit's internal fields (parents, tree, date) are not populated.

The consequences depend on which call site fails:

At line 417 (the main walk loop), c->parents stays NULL after a
failed parse. The parent-walking loop at line 440 simply does not
execute, silently treating the unparsable commit as a root commit.
This produces incorrect "last modified" results: paths changed in
ancestors beyond the corrupt commit are attributed to the wrong
commit or not reported at all.

At line 423 (the --not exclusion walk), n->parents stays NULL,
causing the exclusion walk to stop prematurely. Commits that
should be excluded from the output may be incorrectly included.

At line 293 (process_parent), the parent's tree and parents are
unavailable, so diff operations against it produce wrong results
and the parent's own ancestors are never enqueued for walking.

Skip unparsable commits by checking the return value and
continuing to the next iteration (or returning early in
process_parent). This matches the defensive pattern used in other
revision walkers such as limit_list() and get_revision_internal().

Pointed out by Coverity.

Assisted-by: Claude Opus 4.6
Signed-off-by: Johannes Schindelin <johannes.schindelin@gmx.de>
Signed-off-by: Junio C Hamano <gitster@pobox.com>
builtin/last-modified.c

index 5478182f2e95c235357dbed778c10d4b656a1d62..fe012b0c2ecb3279bb350ebe6c2f915583c8d2b0 100644 (file)
@@ -290,7 +290,8 @@ static void process_parent(struct last_modified *lm,
 {
        struct bitmap *active_p;
 
-       repo_parse_commit(lm->rev.repo, parent);
+       if (repo_parse_commit(lm->rev.repo, parent))
+               return;
        active_p = active_paths_for(lm, parent);
 
        /*
@@ -414,12 +415,14 @@ static int last_modified_run(struct last_modified *lm)
                 * Otherwise, make sure that 'c' isn't reachable from anything
                 * in the '--not' queue.
                 */
-               repo_parse_commit(lm->rev.repo, c);
+               if (repo_parse_commit(lm->rev.repo, c))
+                       continue;
 
                while ((n = prio_queue_get(&not_queue))) {
                        struct commit_list *np;
 
-                       repo_parse_commit(lm->rev.repo, n);
+                       if (repo_parse_commit(lm->rev.repo, n))
+                               continue;
 
                        for (np = n->parents; np; np = np->next) {
                                if (!(np->item->object.flags & PARENT2)) {