git.vger.kernel.org archive mirror
 help / color / mirror / Atom feed
From: Junio C Hamano <gitster@pobox.com>
To: Daniel Li <dan@danielyli.com>
Cc: "Ævar Arnfjörð Bjarmason" <avarab@gmail.com>,
	"Phillip Wood" <phillip.wood@dunelm.org.uk>,
	"René Scharfe" <l.s.r@web.de>,
	git@vger.kernel.org
Subject: [PATCH] 2.36 show regression fix
Date: Fri, 29 Apr 2022 22:29:51 -0700	[thread overview]
Message-ID: <xmqqo80j87g0.fsf_-_@gitster.g> (raw)
In-Reply-To: <xmqqzgk388tt.fsf@gitster.g> (Junio C. Hamano's message of "Fri, 29 Apr 2022 21:59:58 -0700")

This only surfaced as a regression after 2.36 release, but the
breakage was already there with us for at least a year.

e900d494 (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 <dan@danielyli.com>
Signed-off-by: Junio C Hamano <gitster@pobox.com>
---

Junio C Hamano <gitster@pobox.com> writes:

> Daniel Li <dan@danielyli.com> writes:
>
>> git version: 2.36.0
>> OS: macOS Monterey 12.2.1
>> Installed via: homebrew
>
> I think this is the same regression as the recently talked about
> "diff-tree --stdin" aka "gitk" regression.
>
> https://lore.kernel.org/git/xmqq7d7bsu2n.fsf@gitster.g/
>
> e900d494 (diff: add an API for deferred freeing, 2021-02-11), broke
> cmd_log_walk(), and we started to lose some setting that was parsed
> from the command line and stored in the diff_options structure after
> cmd_log_walk() runs just once.  But "git show A B" runs the function
> once for each commit.   A recent change in 2.36.0 made it worse by
> adding <pathspec> to the set of setting that gets lost after
> cmd_log_walk() runs once.
>
> Thanks for a report.

 builtin/log.c           | 23 ++++++++++++++++++-----
 t/t4013-diff-various.sh | 19 +++++++++++++++++++
 2 files changed, 37 insertions(+), 5 deletions(-)

diff --git a/builtin/log.c b/builtin/log.c
index c211d66d1d..6696c4cfd0 100644
--- a/builtin/log.c
+++ b/builtin/log.c
@@ -417,7 +417,7 @@ static void finish_early_output(struct rev_info *rev)
 	show_early_header(rev, "done", n);
 }
 
-static int cmd_log_walk(struct rev_info *rev)
+static int cmd_log_walk_no_free(struct rev_info *rev)
 {
 	struct commit *commit;
 	int saved_nrl = 0;
@@ -444,7 +444,6 @@ static int cmd_log_walk(struct rev_info *rev)
 	 * and HAS_CHANGES being accumulated in rev->diffopt, so be careful to
 	 * retain that state information if replacing rev->diffopt in this loop
 	 */
-	rev->diffopt.no_free = 1;
 	while ((commit = get_revision(rev)) != NULL) {
 		if (!log_tree_commit(rev, commit) && rev->max_count >= 0)
 			/*
@@ -469,8 +468,6 @@ static int cmd_log_walk(struct rev_info *rev)
 	}
 	rev->diffopt.degraded_cc_to_c = saved_dcctc;
 	rev->diffopt.needed_rename_limit = saved_nrl;
-	rev->diffopt.no_free = 0;
-	diff_free(&rev->diffopt);
 
 	if (rev->remerge_diff) {
 		tmp_objdir_destroy(rev->remerge_objdir);
@@ -484,6 +481,17 @@ static int cmd_log_walk(struct rev_info *rev)
 	return diff_result_code(&rev->diffopt, 0);
 }
 
+static int cmd_log_walk(struct rev_info *rev)
+{
+	int retval;
+
+	rev->diffopt.no_free = 1;
+	retval = cmd_log_walk_no_free(rev);
+	rev->diffopt.no_free = 0;
+	diff_free(&rev->diffopt);
+	return retval;
+}
+
 static int git_log_config(const char *var, const char *value, void *cb)
 {
 	const char *slot_name;
@@ -680,6 +688,7 @@ int cmd_show(int argc, const char **argv, const char *prefix)
 
 	count = rev.pending.nr;
 	objects = rev.pending.objects;
+	rev.diffopt.no_free = 1;
 	for (i = 0; i < count && !ret; i++) {
 		struct object *o = objects[i].item;
 		const char *name = objects[i].name;
@@ -725,12 +734,16 @@ int cmd_show(int argc, const char **argv, const char *prefix)
 			rev.pending.nr = rev.pending.alloc = 0;
 			rev.pending.objects = NULL;
 			add_object_array(o, name, &rev.pending);
-			ret = cmd_log_walk(&rev);
+			ret = cmd_log_walk_no_free(&rev);
 			break;
 		default:
 			ret = error(_("unknown type: %d"), o->type);
 		}
 	}
+
+	rev.diffopt.no_free = 0;
+	diff_free(&rev.diffopt);
+
 	free(objects);
 	return ret;
 }
diff --git a/t/t4013-diff-various.sh b/t/t4013-diff-various.sh
index 750aee17ea..7a44d5d595 100755
--- a/t/t4013-diff-various.sh
+++ b/t/t4013-diff-various.sh
@@ -542,6 +542,25 @@ test_expect_success 'diff-tree --stdin with log formatting' '
 	test_cmp expect actual
 '
 
+test_expect_success 'show A B ... -- <pathspec>' '
+	# side touches dir/sub, file0, and file3
+	# master^ touches dir/sub, and file1
+	# master^^ touches dir/sub, file0, and file2
+	git show --name-only --format="<%s>" side master^ master^^ -- dir >actual &&
+	cat >expect <<-\EOF &&
+	<Side>
+
+	dir/sub
+	<Third>
+
+	dir/sub
+	<Second>
+
+	dir/sub
+	EOF
+	test_cmp expect actual
+'
+
 test_expect_success 'diff -I<regex>: setup' '
 	git checkout master &&
 	test_seq 50 >file0 &&
-- 
2.36.0-256-g547811d5a1


  reply	other threads:[~2022-04-30  5:30 UTC|newest]

Thread overview: 10+ messages / expand[flat|nested]  mbox.gz  Atom feed  top
2022-04-30  2:22 Bug: `git show` honors path filters only for the first commit Daniel Li
2022-04-30  4:59 ` Junio C Hamano
2022-04-30  5:29   ` Junio C Hamano [this message]
2022-04-30 10:32     ` [PATCH] 2.36 format-patch regression fix René Scharfe
2022-04-30 16:32       ` Carlo Marcelo Arenas Belón
2022-05-01  9:35         ` René Scharfe
2022-05-20 15:23           ` the state of diff_free() and release_revisions() (was: [PATCH] 2.36 format-patch regression fix) Ævar Arnfjörð Bjarmason
2022-05-20 17:23             ` the state of diff_free() and release_revisions() Junio C Hamano
2022-04-30 14:31     ` [PATCH] 2.36 fast-export regression fix René Scharfe
2022-04-30 20:50       ` Junio C Hamano

Reply instructions:

You may reply publicly to this message via plain-text email
using any one of the following methods:

* Save the following mbox file, import it into your mail client,
  and reply-to-all from there: mbox

  Avoid top-posting and favor interleaved quoting:
  https://en.wikipedia.org/wiki/Posting_style#Interleaved_style

* Reply using the --to, --cc, and --in-reply-to
  switches of git-send-email(1):

  git send-email \
    --in-reply-to=xmqqo80j87g0.fsf_-_@gitster.g \
    --to=gitster@pobox.com \
    --cc=avarab@gmail.com \
    --cc=dan@danielyli.com \
    --cc=git@vger.kernel.org \
    --cc=l.s.r@web.de \
    --cc=phillip.wood@dunelm.org.uk \
    /path/to/YOUR_REPLY

  https://kernel.org/pub/software/scm/git/docs/git-send-email.html

* If your mail client supports setting the In-Reply-To header
  via mailto: links, try the mailto: link
Be sure your reply has a Subject: header at the top and a blank line before the message body.
This is a public inbox, see mirroring instructions
for how to clone and mirror all data and code used for this inbox;
as well as URLs for NNTP newsgroup(s).