Skip to content

Commit 4aa5ff9

Browse files
phillipwoodgitster
authored andcommitted
sequencer: fix quoting in write_author_script
Single quotes should be escaped as \' not \\'. The bad quoting breaks the interactive version of 'rebase --root' (which is used when there is no '--onto' even if the user does not specify --interactive) for authors that contain "'" as sq_dequote() called by read_author_ident() errors out on the bad quoting. For other interactive rebases this only affects external scripts that read the author script and users whose git is upgraded from the shell version of rebase -i while rebase was stopped when the author contains "'". This is because the parsing in read_env_script() expected the broken quoting. This patch includes code to handle the broken quoting when git has been upgraded while rebase was stopped. It does this by detecting the missing "'" at the end of the GIT_AUTHOR_DATE line to see if it should dequote \\' as "'". Note this is only implemented for normal picks, not for creating a new root commit (rebase will stop with an error complaining out bad quoting in that case). The fallback code has been manually tested by reverting both the quoting fixes in write_author_script() and the previous fix for the missing "'" at the end of the GIT_AUTHOR_DATE line and running t3404-rebase-interactive.sh. Ideally rebase and am would share the same code for reading and writing the author script, but this commit just fixes the immediate bug. Helped-by: Eric Sunshine <[email protected]> Helped-by: Johannes Schindelin <[email protected]> Signed-off-by: Phillip Wood <[email protected]> Signed-off-by: Junio C Hamano <[email protected]>
1 parent 5dfcfe1 commit 4aa5ff9

File tree

2 files changed

+44
-10
lines changed

2 files changed

+44
-10
lines changed

sequencer.c

Lines changed: 29 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -636,42 +636,64 @@ static int write_author_script(const char *message)
636636
else if (*message != '\'')
637637
strbuf_addch(&buf, *(message++));
638638
else
639-
strbuf_addf(&buf, "'\\\\%c'", *(message++));
639+
strbuf_addf(&buf, "'\\%c'", *(message++));
640640
strbuf_addstr(&buf, "'\nGIT_AUTHOR_EMAIL='");
641641
while (*message && *message != '\n' && *message != '\r')
642642
if (skip_prefix(message, "> ", &message))
643643
break;
644644
else if (*message != '\'')
645645
strbuf_addch(&buf, *(message++));
646646
else
647-
strbuf_addf(&buf, "'\\\\%c'", *(message++));
647+
strbuf_addf(&buf, "'\\%c'", *(message++));
648648
strbuf_addstr(&buf, "'\nGIT_AUTHOR_DATE='@");
649649
while (*message && *message != '\n' && *message != '\r')
650650
if (*message != '\'')
651651
strbuf_addch(&buf, *(message++));
652652
else
653-
strbuf_addf(&buf, "'\\\\%c'", *(message++));
653+
strbuf_addf(&buf, "'\\%c'", *(message++));
654654
strbuf_addch(&buf, '\'');
655655
res = write_message(buf.buf, buf.len, rebase_path_author_script(), 1);
656656
strbuf_release(&buf);
657657
return res;
658658
}
659659

660+
661+
/*
662+
* write_author_script() used to fail to terminate the last line with a "'" and
663+
* also escaped "'" incorrectly as "'\\\\''" rather than "'\\''". We check for
664+
* the terminating "'" on the last line to see how "'" has been escaped in case
665+
* git was upgraded while rebase was stopped.
666+
*/
667+
static int quoting_is_broken(const char *s, size_t n)
668+
{
669+
/* Skip any empty lines in case the file was hand edited */
670+
while (n > 0 && s[--n] == '\n')
671+
; /* empty */
672+
if (n > 0 && s[n] != '\'')
673+
return 1;
674+
675+
return 0;
676+
}
677+
660678
/*
661679
* Read a list of environment variable assignments (such as the author-script
662680
* file) into an environment block. Returns -1 on error, 0 otherwise.
663681
*/
664682
static int read_env_script(struct argv_array *env)
665683
{
666684
struct strbuf script = STRBUF_INIT;
667-
int i, count = 0;
668-
char *p, *p2;
685+
int i, count = 0, sq_bug;
686+
const char *p2;
687+
char *p;
669688

670689
if (strbuf_read_file(&script, rebase_path_author_script(), 256) <= 0)
671690
return -1;
672-
691+
/* write_author_script() used to quote incorrectly */
692+
sq_bug = quoting_is_broken(script.buf, script.len);
673693
for (p = script.buf; *p; p++)
674-
if (skip_prefix(p, "'\\\\''", (const char **)&p2))
694+
if (sq_bug && skip_prefix(p, "'\\\\''", &p2))
695+
strbuf_splice(&script, p - script.buf, p2 - p, "'", 1);
696+
else if (skip_prefix(p, "'\\''", &p2))
675697
strbuf_splice(&script, p - script.buf, p2 - p, "'", 1);
676698
else if (*p == '\'')
677699
strbuf_splice(&script, p-- - script.buf, 1, "", 0);

t/t3404-rebase-interactive.sh

Lines changed: 15 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -1382,9 +1382,21 @@ test_expect_success 'rebase -i --gpg-sign=<key-id> overrides commit.gpgSign' '
13821382
test_expect_success 'valid author header after --root swap' '
13831383
rebase_setup_and_clean author-header no-conflict-branch &&
13841384
set_fake_editor &&
1385-
FAKE_LINES="2 1" git rebase -i --root &&
1386-
git cat-file commit HEAD^ >out &&
1387-
grep "^author ..*> [0-9][0-9]* [-+][0-9][0-9][0-9][0-9]$" out
1385+
git commit --amend --author="Au ${SQ}thor <[email protected]>" --no-edit &&
1386+
git cat-file commit HEAD | grep ^author >expected &&
1387+
FAKE_LINES="5 1" git rebase -i --root &&
1388+
git cat-file commit HEAD^ | grep ^author >actual &&
1389+
test_cmp expected actual
1390+
'
1391+
1392+
test_expect_success 'valid author header when author contains single quote' '
1393+
rebase_setup_and_clean author-header no-conflict-branch &&
1394+
set_fake_editor &&
1395+
git commit --amend --author="Au ${SQ}thor <[email protected]>" --no-edit &&
1396+
git cat-file commit HEAD | grep ^author >expected &&
1397+
FAKE_LINES="2" git rebase -i HEAD~2 &&
1398+
git cat-file commit HEAD | grep ^author >actual &&
1399+
test_cmp expected actual
13881400
'
13891401

13901402
test_done

0 commit comments

Comments
 (0)