Skip to content

Commit 5cdb384

Browse files
committed
2.36 show regression fix
This only surfaced as a regression after 2.36 release, but the breakage was already there with us for at least a year. e900d49 (diff: add an API for deferred freeing, 2021-02-11) introduced a mechanism to delay freeing resources held in diff_options struct that need to be kept as long as the struct will be reused to compute diff. "git log -p" was taught to utilize the mechanism but it was done with an incorrect assumption that the underlying helper function, cmd_log_walk(), is called only once, and it is OK to do the freeing at the end of it. Alas, for "git show A B", the function is called once for each commit given, so it is not OK to free the resources until we finish calling it for all the commits given from the command line. During 2.36 release cycle, we started clearing the <pathspec> as part of this freeing, which made the bug a lot more visible. Fix this breakage by tweaking how cmd_log_walk() frees the resources at the end and using a variant of it that does not immediately free the resources to show each commit object from the command line in "git show". Protect the fix with a few new tests. Reported-by: Daniel Li <[email protected]> Signed-off-by: Junio C Hamano <[email protected]>
1 parent 362f869 commit 5cdb384

File tree

2 files changed

+37
-5
lines changed

2 files changed

+37
-5
lines changed

builtin/log.c

Lines changed: 18 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -417,7 +417,7 @@ static void finish_early_output(struct rev_info *rev)
417417
show_early_header(rev, "done", n);
418418
}
419419

420-
static int cmd_log_walk(struct rev_info *rev)
420+
static int cmd_log_walk_no_free(struct rev_info *rev)
421421
{
422422
struct commit *commit;
423423
int saved_nrl = 0;
@@ -444,7 +444,6 @@ static int cmd_log_walk(struct rev_info *rev)
444444
* and HAS_CHANGES being accumulated in rev->diffopt, so be careful to
445445
* retain that state information if replacing rev->diffopt in this loop
446446
*/
447-
rev->diffopt.no_free = 1;
448447
while ((commit = get_revision(rev)) != NULL) {
449448
if (!log_tree_commit(rev, commit) && rev->max_count >= 0)
450449
/*
@@ -469,8 +468,6 @@ static int cmd_log_walk(struct rev_info *rev)
469468
}
470469
rev->diffopt.degraded_cc_to_c = saved_dcctc;
471470
rev->diffopt.needed_rename_limit = saved_nrl;
472-
rev->diffopt.no_free = 0;
473-
diff_free(&rev->diffopt);
474471

475472
if (rev->remerge_diff) {
476473
tmp_objdir_destroy(rev->remerge_objdir);
@@ -484,6 +481,17 @@ static int cmd_log_walk(struct rev_info *rev)
484481
return diff_result_code(&rev->diffopt, 0);
485482
}
486483

484+
static int cmd_log_walk(struct rev_info *rev)
485+
{
486+
int retval;
487+
488+
rev->diffopt.no_free = 1;
489+
retval = cmd_log_walk_no_free(rev);
490+
rev->diffopt.no_free = 0;
491+
diff_free(&rev->diffopt);
492+
return retval;
493+
}
494+
487495
static int git_log_config(const char *var, const char *value, void *cb)
488496
{
489497
const char *slot_name;
@@ -680,6 +688,7 @@ int cmd_show(int argc, const char **argv, const char *prefix)
680688

681689
count = rev.pending.nr;
682690
objects = rev.pending.objects;
691+
rev.diffopt.no_free = 1;
683692
for (i = 0; i < count && !ret; i++) {
684693
struct object *o = objects[i].item;
685694
const char *name = objects[i].name;
@@ -725,12 +734,16 @@ int cmd_show(int argc, const char **argv, const char *prefix)
725734
rev.pending.nr = rev.pending.alloc = 0;
726735
rev.pending.objects = NULL;
727736
add_object_array(o, name, &rev.pending);
728-
ret = cmd_log_walk(&rev);
737+
ret = cmd_log_walk_no_free(&rev);
729738
break;
730739
default:
731740
ret = error(_("unknown type: %d"), o->type);
732741
}
733742
}
743+
744+
rev.diffopt.no_free = 0;
745+
diff_free(&rev.diffopt);
746+
734747
free(objects);
735748
return ret;
736749
}

t/t4013-diff-various.sh

Lines changed: 19 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -542,6 +542,25 @@ test_expect_success 'diff-tree --stdin with log formatting' '
542542
test_cmp expect actual
543543
'
544544

545+
test_expect_success 'show A B ... -- <pathspec>' '
546+
# side touches dir/sub, file0, and file3
547+
# master^ touches dir/sub, and file1
548+
# master^^ touches dir/sub, file0, and file2
549+
git show --name-only --format="<%s>" side master^ master^^ -- dir >actual &&
550+
cat >expect <<-\EOF &&
551+
<Side>
552+
553+
dir/sub
554+
<Third>
555+
556+
dir/sub
557+
<Second>
558+
559+
dir/sub
560+
EOF
561+
test_cmp expect actual
562+
'
563+
545564
test_expect_success 'diff -I<regex>: setup' '
546565
git checkout master &&
547566
test_seq 50 >file0 &&

0 commit comments

Comments
 (0)