]> git.ipfire.org Git - thirdparty/git.git/commitdiff
2.36 gitk/diff-tree --stdin regression fix
authorJunio C Hamano <gitster@pobox.com>
Tue, 26 Apr 2022 16:11:44 +0000 (09:11 -0700)
committerJunio C Hamano <gitster@pobox.com>
Tue, 26 Apr 2022 16:26:35 +0000 (09:26 -0700)
This only surfaced as a regression after 2.36 release, but the
breakage was already there with us for at least a year.

The diff_free() call is to be used after we completely finished with
a diffopt structure.  After "git diff A B" finishes producing
output, calling it before process exit is fine.  But there are
commands that prepares diff_options struct once, compares two sets
of paths, releases resources that were used to do the comparison,
then reuses the same diff_option struct to go on to compare the next
two sets of paths, like "git log -p".

After "git log -p" finishes showing a single commit, calling it
before it goes on to the next commit is NOT fine.  There is a
mechanism, the .no_free member in diff_options struct, to help "git
log" to avoid calling diff_free() after showing each commit and
instead call it just one.  When the mechanism was introduced in
e900d494 (diff: add an API for deferred freeing, 2021-02-11),
however, we forgot to do the same to "diff-tree --stdin", which *is*
a moral equivalent to "git log".

During 2.36 release cycle, we started clearing the pathspec in
diff_free(), so programs like gitk that runs

    git diff-tree --stdin -- <pathspec>

downstream of a pipe, processing one commit after another, started
showing irrelevant comparison outside the given <pathspec> from the
second commit.  The same commit, by forgetting to teach the .no_free
mechanism, broke "diff-tree --stdin -I<regexp>" and nobody noticed
it for over a year, presumably because it is so seldom used an
option.

But <pathspec> is a different story.  The breakage was very
prominently visible and was reported immediately after 2.36 was
released.

Fix this breakage by mimicking how "git log" utilizes the .no_free
member so that "diff-tree --stdin" behaves more similarly to "log".

Protect the fix with a few new tests.

Reported-by: Matthias Aßhauer <mha1993@live.de>
Helped-by: René Scharfe <l.s.r@web.de>
Helped-by: Phillip Wood <phillip.wood123@gmail.com>
Signed-off-by: Junio C Hamano <gitster@pobox.com>
builtin/diff-tree.c
log-tree.c
t/t4013-diff-various.sh

index 0e0ac1f1670f3b21f01c4fbbc602c53392331a9c..116097a404a1f1a2e6c3ea735e9159c9eca66066 100644 (file)
@@ -195,6 +195,7 @@ int cmd_diff_tree(int argc, const char **argv, const char *prefix)
                int saved_dcctc = 0;
 
                opt->diffopt.rotate_to_strict = 0;
+               opt->diffopt.no_free = 1;
                if (opt->diffopt.detect_rename) {
                        if (!the_index.cache)
                                repo_read_index(the_repository);
@@ -217,6 +218,8 @@ int cmd_diff_tree(int argc, const char **argv, const char *prefix)
                }
                opt->diffopt.degraded_cc_to_c = saved_dcctc;
                opt->diffopt.needed_rename_limit = saved_nrl;
+               opt->diffopt.no_free = 0;
+               diff_free(&opt->diffopt);
        }
 
        return diff_result_code(&opt->diffopt, 0);
index 25165e2a915e0394cbfc2347d5f3eca83842beff..f8c18fd8b993f5bf2597fa8d86a7459b379f2e5e 100644 (file)
@@ -1098,6 +1098,7 @@ int log_tree_commit(struct rev_info *opt, struct commit *commit)
        opt->loginfo = &log;
        opt->diffopt.no_free = 1;
 
+       /* NEEDSWORK: no restoring of no_free?  Why? */
        if (opt->line_level_traverse)
                return line_log_print(opt, commit);
 
index 750aee17ea9650530a5e716020b4ae05ad6c0ef9..628b01f355a743f0d24dcc33923e72b3fa0062f9 100755 (executable)
@@ -542,6 +542,20 @@ test_expect_success 'diff-tree --stdin with log formatting' '
        test_cmp expect actual
 '
 
+test_expect_success 'diff-tree --stdin with pathspec' '
+       cat >expect <<-EOF &&
+       Third
+
+       dir/sub
+       Second
+
+       dir/sub
+       EOF
+       git rev-list master^ |
+       git diff-tree -r --stdin --name-only --format=%s dir >actual &&
+       test_cmp expect actual
+'
+
 test_expect_success 'diff -I<regex>: setup' '
        git checkout master &&
        test_seq 50 >file0 &&