Skip to content

Commit 4960e5c

Browse files
phillipwoodgitster
authored andcommitted
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 <[email protected]> Signed-off-by: Phillip Wood <[email protected]> Signed-off-by: Junio C Hamano <[email protected]>
1 parent 4a8bc98 commit 4960e5c

File tree

5 files changed

+49
-41
lines changed

5 files changed

+49
-41
lines changed

alias.c

Lines changed: 18 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -3,6 +3,7 @@
33
#include "alloc.h"
44
#include "config.h"
55
#include "gettext.h"
6+
#include "strbuf.h"
67
#include "string-list.h"
78

89
struct config_alias_data {
@@ -46,6 +47,23 @@ void list_aliases(struct string_list *list)
4647
read_early_config(config_alias_cb, &data);
4748
}
4849

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+
}
65+
}
66+
4967
#define SPLIT_CMDLINE_BAD_ENDING 1
5068
#define SPLIT_CMDLINE_UNCLOSED_QUOTE 2
5169
#define SPLIT_CMDLINE_ARGC_OVERFLOW 3

alias.h

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,9 +1,12 @@
11
#ifndef ALIAS_H
22
#define ALIAS_H
33

4+
struct strbuf;
45
struct string_list;
56

67
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);
710
int split_cmdline(char *cmdline, const char ***argv);
811
/* Takes a negative value returned by split_cmdline */
912
const char *split_cmdline_strerror(int cmdline_errno);

sequencer.c

Lines changed: 6 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -2925,7 +2925,7 @@ static void parse_strategy_opts(struct replay_opts *opts, char *raw_opts)
29252925

29262926
count = split_cmdline(strategy_opts_string, &argv);
29272927
if (count < 0)
2928-
die(_("could not split '%s': %s"), strategy_opts_string,
2928+
BUG("could not split '%s': %s", strategy_opts_string,
29292929
split_cmdline_strerror(count));
29302930
for (i = 0; i < count; i++) {
29312931
const char *arg = argv[i];
@@ -3049,12 +3049,13 @@ static int read_populate_opts(struct replay_opts *opts)
30493049

30503050
static void write_strategy_opts(struct replay_opts *opts)
30513051
{
3052-
int i;
30533052
struct strbuf buf = STRBUF_INIT;
30543053

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);
30583059
write_file(rebase_path_strategy_opts(), "%s\n", buf.buf);
30593060
strbuf_release(&buf);
30603061
}

t/t3418-rebase-continue.sh

Lines changed: 22 additions & 12 deletions
Original file line numberDiff line numberDiff line change
@@ -62,29 +62,39 @@ test_expect_success 'rebase --continue remembers merge strategy and options' '
6262
rm -fr .git/rebase-* &&
6363
git reset --hard commit-new-file-F2-on-topic-branch &&
6464
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" &&
6666
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 "$@"
7372
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 &&
7584
(
7685
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
7888
) &&
79-
test -f funny.was.run &&
80-
rm funny.was.run &&
89+
test_cmp expect actual &&
90+
rm actual &&
8191
echo "Resolved" >F2 &&
8292
git add F2 &&
8393
(
8494
PATH=./test-bin:$PATH &&
8595
git rebase --continue
8696
) &&
87-
test -f funny.was.run
97+
test_cmp expect actual
8898
'
8999

90100
test_expect_success 'rebase -i --continue handles merge strategy and options' '

t/t3436-rebase-more-options.sh

Lines changed: 0 additions & 24 deletions
Original file line numberDiff line numberDiff line change
@@ -40,30 +40,6 @@ test_expect_success 'setup' '
4040
EOF
4141
'
4242

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-
6743
test_expect_success '--ignore-whitespace works with apply backend' '
6844
test_must_fail git rebase --apply main side &&
6945
git rebase --abort &&

0 commit comments

Comments
 (0)