diff mbox series

[2/2,WIP] sequencer.c: carry forward notes on HEAD across "rebase -x"

Message ID 20210312030107.1849942-3-gitster@pobox.com (mailing list archive)
State New, archived
Headers show
Series "rebase -x cmd" loses notes | expand

Commit Message

Junio C Hamano March 12, 2021, 3:01 a.m. UTC
When the external command invoked by "rebase -x" replaces the HEAD,
we seem to lose the notes attached to the original HEAD, which is
why we have been losing the amlog mappings in the git core project.

Here is a half-successful attempt to fix it.  For whatever reason,
when the external command is "git commit --amend --no-edit", the
updated code carries notes forward correctly, but when the command
is changed to "git commit --amend -m tweak", it fails to do so, and
I do not have more time to work on this, so I'd stop here with an
expected failure in the test.

Help is appreciated.

Signed-off-by: Junio C Hamano <gitster@pobox.com>
---
 sequencer.c                   | 10 +++++++++-
 t/t3404-rebase-interactive.sh | 18 ++++++++++++++++++
 2 files changed, 27 insertions(+), 1 deletion(-)

Comments

Phillip Wood March 12, 2021, 11:12 a.m. UTC | #1
On 12/03/2021 03:01, Junio C Hamano wrote:
> When the external command invoked by "rebase -x" replaces the HEAD,
> we seem to lose the notes attached to the original HEAD, which is
> why we have been losing the amlog mappings in the git core project.
> 
> Here is a half-successful attempt to fix it.  For whatever reason,
> when the external command is "git commit --amend --no-edit", the
> updated code carries notes forward correctly, but when the command
> is changed to "git commit --amend -m tweak", it fails to do so, and
> I do not have more time to work on this, so I'd stop here with an
> expected failure in the test.
> 
> Help is appreciated.
> 
> Signed-off-by: Junio C Hamano <gitster@pobox.com>
> ---
>   sequencer.c                   | 10 +++++++++-
>   t/t3404-rebase-interactive.sh | 18 ++++++++++++++++++
>   2 files changed, 27 insertions(+), 1 deletion(-)
> 
> diff --git a/sequencer.c b/sequencer.c
> index 92a4871997..e0bdc39e4d 100644
> --- a/sequencer.c
> +++ b/sequencer.c
> @@ -3276,7 +3276,10 @@ static int do_exec(struct repository *r, const char *command_line)
>   {
>   	struct strvec child_env = STRVEC_INIT;
>   	const char *child_argv[] = { NULL, NULL };
> -	int dirty, status;
> +	int dirty, status, bad_head;
> +	struct object_id old_head_oid, new_head_oid;
> +
> +	bad_head = get_oid("HEAD", &old_head_oid);
>   
>   	fprintf(stderr, _("Executing: %s\n"), command_line);
>   	child_argv[0] = command_line;
> @@ -3286,6 +3289,11 @@ static int do_exec(struct repository *r, const char *command_line)
>   	status = run_command_v_opt_cd_env(child_argv, RUN_USING_SHELL, NULL,
>   					  child_env.v);
>   
> +	bad_head |= get_oid("HEAD", &new_head_oid);
> +
> +	if (!bad_head && !oideq(&old_head_oid, &new_head_oid))
> +		commit_post_rewrite(r, &old_head_oid, &new_head_oid);

If HEAD has changed then we copy the notes - but we never copied the 
notes from the original commit to the rewritten commit before running 
the exec command so this does not do what we want. I think this is why 
the second test below fails. I think the first test passes because the 
author and committer identities and dates of the amended commit are 
unchanged from the rebased commit so we end up copying the notes at the 
end of the rebase because the oid is unchanged.

Blindly copying the notes here would mean that the notes would be copied 
if an exec command created a new commit on top of HEAD rather than 
amending HEAD which is not what we want.

Best Wishes

Phillip

>   	/* force re-reading of the cache */
>   	if (discard_index(r->index) < 0 || repo_read_index(r) < 0)
>   		return error(_("could not read index"));
> diff --git a/t/t3404-rebase-interactive.sh b/t/t3404-rebase-interactive.sh
> index 66bcbbf952..3222c594ab 100755
> --- a/t/t3404-rebase-interactive.sh
> +++ b/t/t3404-rebase-interactive.sh
> @@ -155,6 +155,8 @@ test_expect_success 'rebase -i with the exec command checks tree cleanness' '
>   	git rebase --continue
>   '
>   
> +# NEEDSWORK: Fix c762aada1ab3a2c428c with s/@/HEAD/;
> +
>   test_expect_success 'rebase -x with empty command fails' '
>   	test_when_finished "git rebase --abort ||:" &&
>   	test_must_fail env git rebase -x "" @ 2>actual &&
> @@ -867,6 +869,22 @@ test_expect_success 'rebase -i can copy notes over a fixup' '
>   	test_cmp expect output
>   '
>   
> +test_expect_success 'notes are copied even rebase -x changes HEAD' '
> +	git reset --hard n3 &&
> +	git rebase -x "git commit --amend --no-edit" n1^1 &&
> +	git log --format="%s <%N>" n1^1..n3 >expect &&
> +	git log --format="%s <%N>" n1^1..HEAD >actual &&
> +	test_cmp expect actual
> +'
> +
> +test_expect_failure 'notes are copied even rebase -x changes HEAD' '
> +	git reset --hard n3 &&
> +	git rebase -x "git commit --amend -m tweak" n1^1 &&
> +	git log --format="tweak <%N>" n1^1..n3 >expect &&
> +	git log --format="%s <%N>" n1^1..HEAD >actual &&
> +	test_cmp expect actual
> +'
> +
>   test_expect_success 'rebase while detaching HEAD' '
>   	git symbolic-ref HEAD &&
>   	grandparent=$(git rev-parse HEAD~2) &&
>
Ævar Arnfjörð Bjarmason March 12, 2021, 12:26 p.m. UTC | #2
On Fri, Mar 12 2021, Junio C Hamano wrote:

> +# NEEDSWORK: Fix c762aada1ab3a2c428c with s/@/HEAD/;
> +
>  test_expect_success 'rebase -x with empty command fails' '
>  	test_when_finished "git rebase --abort ||:" &&
>  	test_must_fail env git rebase -x "" @ 2>actual &&
> @@ -867,6 +869,22 @@ test_expect_success 'rebase -i can copy notes over a fixup' '
>  	test_cmp expect output
>  '

I eyeballed c762aada1ab (rebase -x: sanity check command, 2019-01-29)
for a bit and still don't quite know what this HEAD v.s. @ is about in
that context, seems this is a stray FIXME comment for an unrelated test.

Maybe it would be better to have test_expect_failure etc. here as
appropriate?

> +test_expect_success 'notes are copied even rebase -x changes HEAD' '
> +	git reset --hard n3 &&
> +	git rebase -x "git commit --amend --no-edit" n1^1 &&
> +	git log --format="%s <%N>" n1^1..n3 >expect &&
> +	git log --format="%s <%N>" n1^1..HEAD >actual &&
> +	test_cmp expect actual
> +'
> +
> +test_expect_failure 'notes are copied even rebase -x changes HEAD' '
> +	git reset --hard n3 &&
> +	git rebase -x "git commit --amend -m tweak" n1^1 &&
> +	git log --format="tweak <%N>" n1^1..n3 >expect &&
> +	git log --format="%s <%N>" n1^1..HEAD >actual &&
> +	test_cmp expect actual
> +'
> +
>  test_expect_success 'rebase while detaching HEAD' '
>  	git symbolic-ref HEAD &&
>  	grandparent=$(git rev-parse HEAD~2) &&
diff mbox series

Patch

diff --git a/sequencer.c b/sequencer.c
index 92a4871997..e0bdc39e4d 100644
--- a/sequencer.c
+++ b/sequencer.c
@@ -3276,7 +3276,10 @@  static int do_exec(struct repository *r, const char *command_line)
 {
 	struct strvec child_env = STRVEC_INIT;
 	const char *child_argv[] = { NULL, NULL };
-	int dirty, status;
+	int dirty, status, bad_head;
+	struct object_id old_head_oid, new_head_oid;
+
+	bad_head = get_oid("HEAD", &old_head_oid);
 
 	fprintf(stderr, _("Executing: %s\n"), command_line);
 	child_argv[0] = command_line;
@@ -3286,6 +3289,11 @@  static int do_exec(struct repository *r, const char *command_line)
 	status = run_command_v_opt_cd_env(child_argv, RUN_USING_SHELL, NULL,
 					  child_env.v);
 
+	bad_head |= get_oid("HEAD", &new_head_oid);
+
+	if (!bad_head && !oideq(&old_head_oid, &new_head_oid))
+		commit_post_rewrite(r, &old_head_oid, &new_head_oid);
+
 	/* force re-reading of the cache */
 	if (discard_index(r->index) < 0 || repo_read_index(r) < 0)
 		return error(_("could not read index"));
diff --git a/t/t3404-rebase-interactive.sh b/t/t3404-rebase-interactive.sh
index 66bcbbf952..3222c594ab 100755
--- a/t/t3404-rebase-interactive.sh
+++ b/t/t3404-rebase-interactive.sh
@@ -155,6 +155,8 @@  test_expect_success 'rebase -i with the exec command checks tree cleanness' '
 	git rebase --continue
 '
 
+# NEEDSWORK: Fix c762aada1ab3a2c428c with s/@/HEAD/;
+
 test_expect_success 'rebase -x with empty command fails' '
 	test_when_finished "git rebase --abort ||:" &&
 	test_must_fail env git rebase -x "" @ 2>actual &&
@@ -867,6 +869,22 @@  test_expect_success 'rebase -i can copy notes over a fixup' '
 	test_cmp expect output
 '
 
+test_expect_success 'notes are copied even rebase -x changes HEAD' '
+	git reset --hard n3 &&
+	git rebase -x "git commit --amend --no-edit" n1^1 &&
+	git log --format="%s <%N>" n1^1..n3 >expect &&
+	git log --format="%s <%N>" n1^1..HEAD >actual &&
+	test_cmp expect actual
+'
+
+test_expect_failure 'notes are copied even rebase -x changes HEAD' '
+	git reset --hard n3 &&
+	git rebase -x "git commit --amend -m tweak" n1^1 &&
+	git log --format="tweak <%N>" n1^1..n3 >expect &&
+	git log --format="%s <%N>" n1^1..HEAD >actual &&
+	test_cmp expect actual
+'
+
 test_expect_success 'rebase while detaching HEAD' '
 	git symbolic-ref HEAD &&
 	grandparent=$(git rev-parse HEAD~2) &&