Git fork

rebase -m: fix serialization of strategy options

To store the strategy options rebase prepends " --" to each one and
writes them to a file. To load them it reads the file and passes the
contents to split_cmdline(). This roughly mimics the behavior of the
scripted rebase but has a couple of limitations, (1) options containing
whitespace are not properly preserved (this is true of the scripted
rebase as well) and (2) options containing '"' or '\' are incorrectly
parsed and may cause the parser to return an error.

Fix these limitations by quoting each option when they are stored so
that they can be parsed correctly. Now that "--preserve-merges" no
longer exist this change also stops prepending "--" to the options when
they are stored as that was an artifact of the scripted rebase.

These changes are backwards compatible so the files written by an older
version of git can still be read. They are also forwards compatible,
the file can still be parsed by recent versions of git as they treat the
"--" prefix as optional.

Reviewed-by: Elijah Newren <newren@gmail.com>
Signed-off-by: Phillip Wood <phillip.wood@dunelm.org.uk>
Signed-off-by: Junio C Hamano <gitster@pobox.com>

authored by

Phillip Wood and committed by
Junio C Hamano
4960e5c7 4a8bc986

+49 -41
+18
alias.c
··· 3 3 #include "alloc.h" 4 4 #include "config.h" 5 5 #include "gettext.h" 6 + #include "strbuf.h" 6 7 #include "string-list.h" 7 8 8 9 struct config_alias_data { ··· 44 45 struct config_alias_data data = { NULL, NULL, list }; 45 46 46 47 read_early_config(config_alias_cb, &data); 48 + } 49 + 50 + void quote_cmdline(struct strbuf *buf, const char **argv) 51 + { 52 + for (const char **argp = argv; *argp; argp++) { 53 + if (argp != argv) 54 + strbuf_addch(buf, ' '); 55 + strbuf_addch(buf, '"'); 56 + for (const char *p = *argp; *p; p++) { 57 + const char c = *p; 58 + 59 + if (c == '"' || c =='\\') 60 + strbuf_addch(buf, '\\'); 61 + strbuf_addch(buf, c); 62 + } 63 + strbuf_addch(buf, '"'); 64 + } 47 65 } 48 66 49 67 #define SPLIT_CMDLINE_BAD_ENDING 1
+3
alias.h
··· 1 1 #ifndef ALIAS_H 2 2 #define ALIAS_H 3 3 4 + struct strbuf; 4 5 struct string_list; 5 6 6 7 char *alias_lookup(const char *alias); 8 + /* Quote argv so buf can be parsed by split_cmdline() */ 9 + void quote_cmdline(struct strbuf *buf, const char **argv); 7 10 int split_cmdline(char *cmdline, const char ***argv); 8 11 /* Takes a negative value returned by split_cmdline */ 9 12 const char *split_cmdline_strerror(int cmdline_errno);
+6 -5
sequencer.c
··· 2925 2925 2926 2926 count = split_cmdline(strategy_opts_string, &argv); 2927 2927 if (count < 0) 2928 - die(_("could not split '%s': %s"), strategy_opts_string, 2928 + BUG("could not split '%s': %s", strategy_opts_string, 2929 2929 split_cmdline_strerror(count)); 2930 2930 for (i = 0; i < count; i++) { 2931 2931 const char *arg = argv[i]; ··· 3049 3049 3050 3050 static void write_strategy_opts(struct replay_opts *opts) 3051 3051 { 3052 - int i; 3053 3052 struct strbuf buf = STRBUF_INIT; 3054 3053 3055 - for (i = 0; i < opts->xopts.nr; ++i) 3056 - strbuf_addf(&buf, " --%s", opts->xopts.v[i]); 3057 - 3054 + /* 3055 + * Quote strategy options so that they can be read correctly 3056 + * by split_cmdline(). 3057 + */ 3058 + quote_cmdline(&buf, opts->xopts.v); 3058 3059 write_file(rebase_path_strategy_opts(), "%s\n", buf.buf); 3059 3060 strbuf_release(&buf); 3060 3061 }
+22 -12
t/t3418-rebase-continue.sh
··· 62 62 rm -fr .git/rebase-* && 63 63 git reset --hard commit-new-file-F2-on-topic-branch && 64 64 test_commit "commit-new-file-F3-on-topic-branch" F3 32 && 65 - test_when_finished "rm -fr test-bin funny.was.run" && 65 + test_when_finished "rm -fr test-bin" && 66 66 mkdir test-bin && 67 - cat >test-bin/git-merge-funny <<-EOF && 68 - #!$SHELL_PATH 69 - case "\$1" in --opt) ;; *) exit 2 ;; esac 70 - shift && 71 - >funny.was.run && 72 - exec git merge-recursive "\$@" 67 + 68 + write_script test-bin/git-merge-funny <<-\EOF && 69 + printf "[%s]\n" $# "$1" "$2" "$3" "$5" >actual 70 + shift 3 && 71 + exec git merge-recursive "$@" 73 72 EOF 74 - chmod +x test-bin/git-merge-funny && 73 + 74 + cat >expect <<-\EOF && 75 + [7] 76 + [--option=arg with space] 77 + [--op"tion\] 78 + [--new 79 + line ] 80 + [--] 81 + EOF 82 + 83 + rm -f actual && 75 84 ( 76 85 PATH=./test-bin:$PATH && 77 - test_must_fail git rebase -s funny -Xopt main topic 86 + test_must_fail git rebase -s funny -X"option=arg with space" \ 87 + -Xop\"tion\\ -X"new${LF}line " main topic 78 88 ) && 79 - test -f funny.was.run && 80 - rm funny.was.run && 89 + test_cmp expect actual && 90 + rm actual && 81 91 echo "Resolved" >F2 && 82 92 git add F2 && 83 93 ( 84 94 PATH=./test-bin:$PATH && 85 95 git rebase --continue 86 96 ) && 87 - test -f funny.was.run 97 + test_cmp expect actual 88 98 ' 89 99 90 100 test_expect_success 'rebase -i --continue handles merge strategy and options' '
-24
t/t3436-rebase-more-options.sh
··· 40 40 EOF 41 41 ' 42 42 43 - test_expect_success 'bad -X <strategy-option> arguments: unclosed quote' ' 44 - test_when_finished "test_might_fail git rebase --abort" && 45 - cat >expect <<-\EOF && 46 - fatal: could not split '\''--bad'\'': unclosed quote 47 - EOF 48 - GIT_SEQUENCE_EDITOR="echo break >" \ 49 - git rebase -i -X"bad argument\"" side main && 50 - test_expect_code 128 git rebase --continue >out 2>actual && 51 - test_must_be_empty out && 52 - test_cmp expect actual 53 - ' 54 - 55 - test_expect_success 'bad -X <strategy-option> arguments: bad escape' ' 56 - test_when_finished "test_might_fail git rebase --abort" && 57 - cat >expect <<-\EOF && 58 - fatal: could not split '\''--bad'\'': cmdline ends with \ 59 - EOF 60 - GIT_SEQUENCE_EDITOR="echo break >" \ 61 - git rebase -i -X"bad escape \\" side main && 62 - test_expect_code 128 git rebase --continue >out 2>actual && 63 - test_must_be_empty out && 64 - test_cmp expect actual 65 - ' 66 - 67 43 test_expect_success '--ignore-whitespace works with apply backend' ' 68 44 test_must_fail git rebase --apply main side && 69 45 git rebase --abort &&