Skip to content

Commit c23d453

Browse files
committed
last-modified: handle repo_parse_commit() failures
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>
1 parent 60beb13 commit c23d453

1 file changed

Lines changed: 6 additions & 3 deletions

File tree

builtin/last-modified.c

Lines changed: 6 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -290,7 +290,8 @@ static void process_parent(struct last_modified *lm,
290290
{
291291
struct bitmap *active_p;
292292

293-
repo_parse_commit(lm->rev.repo, parent);
293+
if (repo_parse_commit(lm->rev.repo, parent))
294+
return;
294295
active_p = active_paths_for(lm, parent);
295296

296297
/*
@@ -414,13 +415,15 @@ static int last_modified_run(struct last_modified *lm)
414415
* Otherwise, make sure that 'c' isn't reachable from anything
415416
* in the '--not' queue.
416417
*/
417-
repo_parse_commit(lm->rev.repo, c);
418+
if (repo_parse_commit(lm->rev.repo, c))
419+
continue;
418420

419421
while (not_queue.nr) {
420422
struct commit_list *np;
421423
struct commit *n = prio_queue_get(&not_queue);
422424

423-
repo_parse_commit(lm->rev.repo, n);
425+
if (repo_parse_commit(lm->rev.repo, n))
426+
continue;
424427

425428
for (np = n->parents; np; np = np->next) {
426429
if (!(np->item->object.flags & PARENT2)) {

0 commit comments

Comments
 (0)