diff options
-rw-r--r-- | sequencer.c | 36 | ||||
-rwxr-xr-x | t/t3404-rebase-interactive.sh | 18 |
2 files changed, 44 insertions, 10 deletions
diff --git a/sequencer.c b/sequencer.c index c4e4418559..ba11fe5bca 100644 --- a/sequencer.c +++ b/sequencer.c @@ -636,7 +636,7 @@ missing_author: else if (*message != '\'') strbuf_addch(&buf, *(message++)); else - strbuf_addf(&buf, "'\\\\%c'", *(message++)); + strbuf_addf(&buf, "'\\%c'", *(message++)); strbuf_addstr(&buf, "'\nGIT_AUTHOR_EMAIL='"); while (*message && *message != '\n' && *message != '\r') if (skip_prefix(message, "> ", &message)) @@ -644,19 +644,37 @@ missing_author: else if (*message != '\'') strbuf_addch(&buf, *(message++)); else - strbuf_addf(&buf, "'\\\\%c'", *(message++)); + strbuf_addf(&buf, "'\\%c'", *(message++)); strbuf_addstr(&buf, "'\nGIT_AUTHOR_DATE='@"); while (*message && *message != '\n' && *message != '\r') if (*message != '\'') strbuf_addch(&buf, *(message++)); else - strbuf_addf(&buf, "'\\\\%c'", *(message++)); + strbuf_addf(&buf, "'\\%c'", *(message++)); strbuf_addch(&buf, '\''); res = write_message(buf.buf, buf.len, rebase_path_author_script(), 1); strbuf_release(&buf); return res; } + +/* + * write_author_script() used to fail to terminate the last line with a "'" and + * also escaped "'" incorrectly as "'\\\\''" rather than "'\\''". We check for + * the terminating "'" on the last line to see how "'" has been escaped in case + * git was upgraded while rebase was stopped. + */ +static int quoting_is_broken(const char *s, size_t n) +{ + /* Skip any empty lines in case the file was hand edited */ + while (n > 0 && s[--n] == '\n') + ; /* empty */ + if (n > 0 && s[n] != '\'') + return 1; + + return 0; +} + /* * Read a list of environment variable assignments (such as the author-script * file) into an environment block. Returns -1 on error, 0 otherwise. @@ -664,14 +682,18 @@ missing_author: static int read_env_script(struct argv_array *env) { struct strbuf script = STRBUF_INIT; - int i, count = 0; - char *p, *p2; + int i, count = 0, sq_bug; + const char *p2; + char *p; if (strbuf_read_file(&script, rebase_path_author_script(), 256) <= 0) return -1; - + /* write_author_script() used to quote incorrectly */ + sq_bug = quoting_is_broken(script.buf, script.len); for (p = script.buf; *p; p++) - if (skip_prefix(p, "'\\\\''", (const char **)&p2)) + if (sq_bug && skip_prefix(p, "'\\\\''", &p2)) + strbuf_splice(&script, p - script.buf, p2 - p, "'", 1); + else if (skip_prefix(p, "'\\''", &p2)) strbuf_splice(&script, p - script.buf, p2 - p, "'", 1); else if (*p == '\'') strbuf_splice(&script, p-- - script.buf, 1, "", 0); diff --git a/t/t3404-rebase-interactive.sh b/t/t3404-rebase-interactive.sh index b72167ecd5..834a124d60 100755 --- a/t/t3404-rebase-interactive.sh +++ b/t/t3404-rebase-interactive.sh @@ -1382,9 +1382,21 @@ test_expect_success 'rebase -i --gpg-sign=<key-id> overrides commit.gpgSign' ' test_expect_success 'valid author header after --root swap' ' rebase_setup_and_clean author-header no-conflict-branch && set_fake_editor && - FAKE_LINES="2 1" git rebase -i --root && - git cat-file commit HEAD^ >out && - grep "^author ..*> [0-9][0-9]* [-+][0-9][0-9][0-9][0-9]$" out + git commit --amend --author="Au ${SQ}thor <author@example.com>" --no-edit && + git cat-file commit HEAD | grep ^author >expected && + FAKE_LINES="5 1" git rebase -i --root && + git cat-file commit HEAD^ | grep ^author >actual && + test_cmp expected actual +' + +test_expect_success 'valid author header when author contains single quote' ' + rebase_setup_and_clean author-header no-conflict-branch && + set_fake_editor && + git commit --amend --author="Au ${SQ}thor <author@example.com>" --no-edit && + git cat-file commit HEAD | grep ^author >expected && + FAKE_LINES="2" git rebase -i HEAD~2 && + git cat-file commit HEAD | grep ^author >actual && + test_cmp expected actual ' test_done |