rebase --skip: fix commit message clean up when skipping squash
PATCH 1/1 rebase --skip: fix commit message clean up when skipping squash
From: Phillip Wood <[email protected]>
During a series of "fixup" and/or "squash" commands, the interactive rebase accumulates a commit message from all the commits that are being squashed together. If one of the commits has conflicts when it is picked and the user chooses to skip that commit then we need to remove that commit's message from accumulated messages. To do this 15ef69314d5 (rebase --skip: clean up commit message after a failed fixup/squash, 2018-04-27) updated commit_staged_changes() to reset the accumulated message to the commit message of HEAD (which does not contain the message from the skipped commit) when the last command was "fixup" or "squash" and there are no staged changes. Unfortunately the code to do this contains two bugs.
(1) If parse_head() fails we pass an invalid pointer to unuse_commit_buffer().
(2) The reconstructed message uses the entire commit buffer from HEAD including the headers, rather than just the commit message.
The fist issue is fixed by splitting up the "if" condition into several statements each with its own error handling. The second issue is fixed by finding the start of the commit message within the commit buffer using find_commit_subject().
The existing test added by 15ef69314d5 is modified to show the effect of this bug. The bug is triggered when skipping the first command in the chain (as the test does before this commit) but the effect is hidden because opts->current_fixup_count is set to zero which leads update_squash_messages() to recreate the squash message file from scratch overwriting the bad message created by commit_staged_changes(). The test is also updated to explicitly check the commit messages rather than relying on grep to ensure they do not contain any stay commit headers.
To check the commit message the function test_commit_message() is moved from t3437-rebase-fixup-options.sh to test-lib.sh. As the function is now publicly available it is updated to provide better error detection and avoid overwriting the commonly used files "actual" and "expect". Support for reading the expected commit message from stdin is also added.
---
rebase --skip: fix commit message clean up when skipping squash
This patch is based on maint.
Published-As: https://github.com/gitgitgadget/git/releases/tag/pr-git-1558%2Fphillipwood%2Fwip%2Frebase-skip-fix-message-cleanup-v1
Fetch-It-Via: git fetch https://github.com/gitgitgadget/git pr-git-1558/phillipwood/wip/rebase-skip-fix-message-cleanup-v1
Pull-Request: https://github.com/git/git/pull/1558
sequencer.c | 26 +++++++++++---- t/t3418-rebase-continue.sh | 58 +++++++++++++++++++++++---------- t/t3437-rebase-fixup-options.sh | 15 --------- t/test-lib-functions.sh | 33 +++++++++++++++++++ 4 files changed, 93 insertions(+), 39 deletions(-)
sequencer.c +19 -7
@@ -5038,19 +5038,31 @@ static int commit_staged_changes(struct repository *r,* We need to update the squash message to skip * the latest commit message. */ +int res =0 ;struct commit *commit; +const char *msg;const char *path =rebase_path_squash_msg ();const char *encoding =get_commit_output_encoding (); -if (parse_head (r, &commit) || - !(p =repo_logmsg_reencode (r, commit,NULL , encoding)) || -write_message (p,strlen (p), path,0 )) { -repo_unuse_commit_buffer (r, commit, p); -return error (_ ("could not write file: " +if (parse_head (r, &commit)) +return error (_ ("could not parse HEAD" )); + + p =repo_logmsg_reencode (r, commit,NULL , encoding); +if (!p) { + res =error (_ ("could not parse commit %s" ), +oid_to_hex (&commit->object.oid)); + goto unuse_commit_buffer; + } +find_commit_subject (p, &msg); +if (write_message (msg,strlen (msg), path,0 )) { + res =error (_ ("could not write file: " "'%s'" ), path); + goto unuse_commit_buffer; } -repo_unuse_commit_buffer (r, - commit, p); + unuse_commit_buffer: +repo_unuse_commit_buffer (r, commit, p); +if (res) +return res; } }
t/t3418-rebase-continue.sh +41 -17
89 lines not on this page · raw message
t/t3437-rebase-fixup-options.sh +0 -15
@@ -21,21 +21,6 @@ TEST_PASSES_SANITIZE_LEAK=true EMPTY="" -# test_commit_message <rev> -m <msg> -# test_commit_message <rev> <path> -# Verify that the commit message of <rev> matches -# <msg> or the content of <path>. -test_commit_message () { - git show --no-patch --pretty=format:%B "$1" >actual && - case "$2" in - -m) - echo "$3" >expect && - test_cmp expect actual ;; - *) - test_cmp "$2" actual ;; - esac -} - get_author () { rev="$1" && git log -1 --pretty=format:"%an %ae %at" "$rev"
t/test-lib-functions.sh +33 -0
@@ -1273,6 +1273,39 @@ test_cmp_rev () { fi } +# Tests that a commit message matches the expected text +# +# Usage: test_commit_message <rev> [-m <msg> | <file>] +# +# When using "-m" <msg> will have a line feed appended. If the second +# argument is omitted then the expected message is read from stdin. + +test_commit_message () { + local msg_file=expect.msg + + case $# in + 3) + if test "$2" = "-m" + then + printf "%s\n" "$3" >"$msg_file" + else + BUG "Usage: test_commit_message <rev> [-m <message> | <file>]" + fi + ;; + 2) + msg_file="$2" + ;; + 1) + cat >"$msg_file" + ;; + *) + BUG "Usage: test_commit_message <rev> [-m <message> | <file>]" + ;; + esac + git show --no-patch --pretty=format:%B "$1" -- >actual.msg && + test_cmp "$msg_file" actual.msg +} + # Compare paths respecting core.ignoreCase test_cmp_fspath () { if test "x$1" = "x$2"
base-commit: fb7d80edcae482f4fa5d4be0227dc3054734e5f3
Signature · 1 line
gitgitgadget
Quoted 3 lines · Phillip Wood via GitGitGadget
... The test is also updated to explicitly check
the commit messages rather than relying on grep to ensure they do not contain any stay commit headers.
"stay" -> "stray" presumably.
Quoted 6 lines
To check the commit message the function
test_commit_message()is moved fromt3437-rebase-fixup-options.shtotest-lib.sh. As the function is now publicly available it is updated to provide better error detection and avoid overwriting the commonly used files "actual" and "expect". Support for reading the expected commit message from stdin is also added.
It may make it cleaner to do the refactoring as a separate preparatory patch, but the end-result is not so large from the diffstat below, so it probably is OK.
I am not sure if deviating from expect vs actual is such a good idea. It is not like use of two temporary files are transparent to the caller of the new test helper---indeed, expect and actual are likely to be used by the caller in tests that comes before or after the ones that use test_commit_message, and by using a pair of files that are different, the caller will now see two extra untracked files left in the working tree.
The only case such a renaming could help callers is when they do
cat >expect <<\-EOF && here to prepare some outcome EOF git do-something-to-make-commit && test_commit_message HEAD <<\-EOF && here is what we expect to see in HEAD EOF git some-other-thing >actual && test_cmp expect actual
as use of files other than expect/actual in test_commit_message will avoid stomping on the "expect" file that was already prepared.
I suspect that it would be rare, and something we can fix when need arises by allowing test_commit_message to accept an option to use non-standard filenames for its temporaries. The current callers, both the existing ones in t3437 and the new ones added by this patch, would not benefit. The only externally visible side effect is that the existing ones will have two extra untracked files in their working tree.
Quoted 11 lines
Signed-off-by Phillip Wood <[email protected]>---
rebase --skip: fix commit message clean up when skipping squashThis patch is based on maint.
...sequencer.c | 26 +++++++++++---- t/t3418-rebase-continue.sh | 58 +++++++++++++++++++++++---------- t/t3437-rebase-fixup-options.sh | 15 --------- t/test-lib-functions.sh | 33 +++++++++++++++++++ 4 files changed, 93 insertions(+), 39 deletions(-)
OK.
Quoted 32 lines
sequencer.c +14
-5@@ -5038,19 +5038,31 @@ static int commit_staged_changes(struct repository *r,* We need to update the squash message to skip * the latest commit message. */ +int res =0 ;struct commit *commit; +const char *msg;const char *path =rebase_path_squash_msg ();const char *encoding =get_commit_output_encoding (); -if (parse_head (r, &commit) || - !(p =repo_logmsg_reencode (r, commit,NULL , encoding)) || -write_message (p,strlen (p), path,0 )) { -repo_unuse_commit_buffer (r, commit, p); -return error (_ ("could not write file: " +if (parse_head (r, &commit)) +return error (_ ("could not parse HEAD" )); + + p =repo_logmsg_reencode (r, commit,NULL , encoding); +if (!p) { + res =error (_ ("could not parse commit %s" ), +oid_to_hex (&commit->object.oid)); + goto unuse_commit_buffer; + } +find_commit_subject (p, &msg); +if (write_message (msg,strlen (msg), path,0 )) { + res =error (_ ("could not write file: " "'%s'" ), path);
sequencer.c static int commit_staged_changes(struct repository *r, · line 5067
Quoted 10 lines
sequencer.c +5
-2+ goto unuse_commit_buffer; } -repo_unuse_commit_buffer (r, - commit, p); + unuse_commit_buffer: +repo_unuse_commit_buffer (r, commit, p); +if (res) +return res; } }
Just as described in the proposed log message. Looking good.
Will queue. Thanks.
Quoted 7 lines · Junio C Hamano
Quoted 3 lines · Phillip Wood via GitGitGadget
... The test is also updated to explicitly check
the commit messages rather than relying on grep to ensure they do not contain any stay commit headers."stay" -> "stray" presumably.
I see this is in next already, thanks for fixing that and the other typo pointed out by Marc
Quoted 10 lines
Quoted 6 lines
To check the commit message the function
test_commit_message()is moved fromt3437-rebase-fixup-options.shtotest-lib.sh. As the function is now publicly available it is updated to provide better error detection and avoid overwriting the commonly used files "actual" and "expect". Support for reading the expected commit message from stdin is also added.It may make it cleaner to do the refactoring as a separate preparatory patch, but the end-result is not so large from the diffstat below, so it probably is OK.
I was in two minds about splitting out the refactoring, but in the end I decided that as I was adding the ability to read the expected message from stdin for this commit it was easier include it all here rather than splitting it out.
Quoted 7 lines
I am not sure if deviating from expect vs actual is such a good idea. It is not like use of two temporary files are transparent to the caller of the new test helper---indeed, expect and actual are likely to be used by the caller in tests that comes before or after the ones that use
test_commit_message, and by using a pair of files that are different, the caller will now see two extra untracked files left in the working tree.
That's true, looking at test-lib-functions.sh it seems I unwittingly followed the example of test_cmp_config() which uses {actual,expect}.config when it calls test_cmp(). Anyway as this is in next I assume you're happy enough with the implementation as it stands.
Best Wishes
Phillip
Quoted 83 lines
The only case such a renaming could help callers is when they do
cat >expect <<\-EOF && here to prepare some outcome EOF git do-something-to-make-commit && test_commit_message HEAD <<\-EOF && here is what we expect to see in HEAD EOF git some-other-thing >actual && test_cmp expect actualas use of files other than expect/actual in
test_commit_messagewill avoid stomping on the "expect" file that was already prepared.I suspect that it would be rare, and something we can fix when need arises by allowing
test_commit_messageto accept an option to use non-standard filenames for its temporaries. The current callers, both the existing ones in t3437 and the new ones added by this patch, would not benefit. The only externally visible side effect is that the existing ones will have two extra untracked files in their working tree.Quoted 11 lines
Signed-off-by Phillip Wood <[email protected]>---
rebase --skip: fix commit message clean up when skipping squashThis patch is based on maint.
...sequencer.c | 26 +++++++++++---- t/t3418-rebase-continue.sh | 58 +++++++++++++++++++++++---------- t/t3437-rebase-fixup-options.sh | 15 --------- t/test-lib-functions.sh | 33 +++++++++++++++++++ 4 files changed, 93 insertions(+), 39 deletions(-)OK.
Quoted 32 lines
sequencer.c +14
-5@@ -5038,19 +5038,31 @@ static int commit_staged_changes(struct repository *r,* We need to update the squash message to skip * the latest commit message. */ +int res =0 ;struct commit *commit; +const char *msg;const char *path =rebase_path_squash_msg ();const char *encoding =get_commit_output_encoding (); -if (parse_head (r, &commit) || - !(p =repo_logmsg_reencode (r, commit,NULL , encoding)) || -write_message (p,strlen (p), path,0 )) { -repo_unuse_commit_buffer (r, commit, p); -return error (_ ("could not write file: " +if (parse_head (r, &commit)) +return error (_ ("could not parse HEAD" )); + + p =repo_logmsg_reencode (r, commit,NULL , encoding); +if (!p) { + res =error (_ ("could not parse commit %s" ), +oid_to_hex (&commit->object.oid)); + goto unuse_commit_buffer; + } +find_commit_subject (p, &msg); +if (write_message (msg,strlen (msg), path,0 )) { + res =error (_ ("could not write file: " "'%s'" ), path);sequencer.c static int commit_staged_changes(struct repository *r, · line 5067
Quoted 10 lines
sequencer.c +5
-2+ goto unuse_commit_buffer; } -repo_unuse_commit_buffer (r, - commit, p); + unuse_commit_buffer: +repo_unuse_commit_buffer (r, commit, p); +if (res) +return res; } }Just as described in the proposed log message. Looking good.
Will queue. Thanks.
Quoted 8 lines · Phillip Wood
Quoted 7 lines
I am not sure if deviating from expect vs actual is such a good idea. It is not like use of two temporary files are transparent to the caller of the new test helper---indeed, expect and actual are likely to be used by the caller in tests that comes before or after the ones that use
test_commit_message, and by using a pair of files that are different, the caller will now see two extra untracked files left in the working tree.
Quoted 5 lines
That's true, looking at
test-lib-functions.shit seems I unwittingly followed the example oftest_cmp_config()which uses {actual,expect}.config when it callstest_cmp(). Anyway as this is in next I assume you're happy enough with the implementation as it stands.
You assumed too much ;-). The above is a bad move, but is minor enough and it is not a show-stopper. If you are aware of other bad examples, clean-up patches after the dust settles would be very much welcomed.
Thanks.
Quoted 14 lines · Phillip Wood via GitGitGadget
From: Phillip Wood <[email protected]>
During a series of "fixup" and/or "squash" commands, the interactive rebase accumulates a commit message from all the commits that are being squashed together. If one of the commits has conflicts when it is picked and the user chooses to skip that commit then we need to remove that commit's message from accumulated messages. To do this
15ef69314d5(rebase --skip: clean up commit message after a failed fixup/squash, 2018-04-27) updatedcommit_staged_changes()to reset the accumulated message to the commit message of HEAD (which does not contain the message from the skipped commit) when the last command was "fixup" or "squash" and there are no staged changes. Unfortunately the code to do this contains two bugs.
Quoted 7 lines
(1) If
parse_head()fails we pass an invalid pointer tounuse_commit_buffer().(2) The reconstructed message uses the entire commit buffer from HEAD including the headers, rather than just the commit message.
The fist issue is fixed by splitting up the "if" condition into several
s/fist/first/
(I'm not qualified to review anything else about this patch, but I like its intention... :D )
M.
Quoted 5 lines · Marc Branchaud
Quoted 1 line
The fist issue is fixed by splitting up the "if" condition into several
s/fist/first/
(I'm not qualified to review anything else about this patch, but I like its intention... :D )
Thanks Marc, it looks like Junio fixed that typo when he applied the patch.
Best Wishes
Phillip