Skip to content

Commit 39edfd5

Browse files
newrengitster
authored andcommitted
sequencer: fix edit handling for cherry-pick and revert messages
save_opts() should save any non-default values. It was intended to do this, but since most options in struct replay_opts default to 0, it only saved non-zero values. Unfortunately, this does not always work for options.edit. Roughly speaking, options.edit had a default value of 0 for cherry-pick but a default value of 1 for revert. Make save_opts() record a value whenever it differs from the default. options.edit was also overly simplistic; we had more than two cases. The behavior that previously existed was as follows: Non-conflict commits Right after Conflict revert Edit iff isatty(0) Edit (ignore isatty(0)) cherry-pick No edit See above Specify --edit Edit (ignore isatty(0)) See above Specify --no-edit (*) See above (*) Before stopping for conflicts, No edit is the behavior. After stopping for conflicts, the --no-edit flag is not saved so see the first two rows. However, the expected behavior is: Non-conflict commits Right after Conflict revert Edit iff isatty(0) Edit iff isatty(0) cherry-pick No edit Edit iff isatty(0) Specify --edit Edit (ignore isatty(0)) Edit (ignore isatty(0)) Specify --no-edit No edit No edit In order to get the expected behavior, we need to change options.edit to a tri-state: unspecified, false, or true. When specified, we follow what it says. When unspecified, we need to check whether the current commit being created is resolving a conflict as well as consulting options.action and isatty(0). While at it, add a should_edit() utility function that compresses options.edit down to a boolean based on the additional information for the non-conflict case. continue_single_pick() is the function responsible for resuming after conflict cases, regardless of whether there is one commit being picked or many. Make this function stop assuming edit behavior in all cases, so that it can correctly handle !isatty(0) and specific requests to not edit the commit message. Reported-by: Renato Botelho <[email protected]> Signed-off-by: Elijah Newren <[email protected]> Reviewed-by: Johannes Schindelin <[email protected]> Signed-off-by: Junio C Hamano <[email protected]>
1 parent 48bf2fa commit 39edfd5

File tree

4 files changed

+77
-17
lines changed

4 files changed

+77
-17
lines changed

builtin/revert.c

Lines changed: 1 addition & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -182,7 +182,7 @@ static int run_sequencer(int argc, const char **argv, struct replay_opts *opts)
182182
"--signoff", opts->signoff,
183183
"--no-commit", opts->no_commit,
184184
"-x", opts->record_origin,
185-
"--edit", opts->edit,
185+
"--edit", opts->edit > 0,
186186
NULL);
187187

188188
if (cmd) {
@@ -230,8 +230,6 @@ int cmd_revert(int argc, const char **argv, const char *prefix)
230230
struct replay_opts opts = REPLAY_OPTS_INIT;
231231
int res;
232232

233-
if (isatty(0))
234-
opts.edit = 1;
235233
opts.action = REPLAY_REVERT;
236234
sequencer_init_config(&opts);
237235
res = run_sequencer(argc, argv, &opts);

sequencer.c

Lines changed: 41 additions & 11 deletions
Original file line numberDiff line numberDiff line change
@@ -1860,14 +1860,25 @@ static void record_in_rewritten(struct object_id *oid,
18601860
flush_rewritten_pending();
18611861
}
18621862

1863+
static int should_edit(struct replay_opts *opts) {
1864+
if (opts->edit < 0)
1865+
/*
1866+
* Note that we only handle the case of non-conflicted
1867+
* commits; continue_single_pick() handles the conflicted
1868+
* commits itself instead of calling this function.
1869+
*/
1870+
return (opts->action == REPLAY_REVERT && isatty(0)) ? 1 : 0;
1871+
return opts->edit;
1872+
}
1873+
18631874
static int do_pick_commit(struct repository *r,
18641875
enum todo_command command,
18651876
struct commit *commit,
18661877
struct replay_opts *opts,
18671878
int final_fixup, int *check_todo)
18681879
{
1869-
unsigned int flags = opts->edit ? EDIT_MSG : 0;
1870-
const char *msg_file = opts->edit ? NULL : git_path_merge_msg(r);
1880+
unsigned int flags = should_edit(opts) ? EDIT_MSG : 0;
1881+
const char *msg_file = should_edit(opts) ? NULL : git_path_merge_msg(r);
18711882
struct object_id head;
18721883
struct commit *base, *next, *parent;
18731884
const char *base_label, *next_label;
@@ -3101,9 +3112,9 @@ static int save_opts(struct replay_opts *opts)
31013112
if (opts->no_commit)
31023113
res |= git_config_set_in_file_gently(opts_file,
31033114
"options.no-commit", "true");
3104-
if (opts->edit)
3105-
res |= git_config_set_in_file_gently(opts_file,
3106-
"options.edit", "true");
3115+
if (opts->edit >= 0)
3116+
res |= git_config_set_in_file_gently(opts_file, "options.edit",
3117+
opts->edit ? "true" : "false");
31073118
if (opts->allow_empty)
31083119
res |= git_config_set_in_file_gently(opts_file,
31093120
"options.allow-empty", "true");
@@ -4077,7 +4088,7 @@ static int pick_commits(struct repository *r,
40774088
prev_reflog_action = xstrdup(getenv(GIT_REFLOG_ACTION));
40784089
if (opts->allow_ff)
40794090
assert(!(opts->signoff || opts->no_commit ||
4080-
opts->record_origin || opts->edit ||
4091+
opts->record_origin || should_edit(opts) ||
40814092
opts->committer_date_is_author_date ||
40824093
opts->ignore_date));
40834094
if (read_and_refresh_cache(r, opts))
@@ -4370,14 +4381,33 @@ static int pick_commits(struct repository *r,
43704381
return sequencer_remove_state(opts);
43714382
}
43724383

4373-
static int continue_single_pick(struct repository *r)
4384+
static int continue_single_pick(struct repository *r, struct replay_opts *opts)
43744385
{
4375-
const char *argv[] = { "commit", NULL };
4386+
struct strvec argv = STRVEC_INIT;
4387+
int ret;
43764388

43774389
if (!refs_ref_exists(get_main_ref_store(r), "CHERRY_PICK_HEAD") &&
43784390
!refs_ref_exists(get_main_ref_store(r), "REVERT_HEAD"))
43794391
return error(_("no cherry-pick or revert in progress"));
4380-
return run_command_v_opt(argv, RUN_GIT_CMD);
4392+
4393+
strvec_push(&argv, "commit");
4394+
4395+
/*
4396+
* continue_single_pick() handles the case of recovering from a
4397+
* conflict. should_edit() doesn't handle that case; for a conflict,
4398+
* we want to edit if the user asked for it, or if they didn't specify
4399+
* and stdin is a tty.
4400+
*/
4401+
if (!opts->edit || (opts->edit < 0 && !isatty(0)))
4402+
/*
4403+
* Include --cleanup=strip as well because we don't want the
4404+
* "# Conflicts:" messages.
4405+
*/
4406+
strvec_pushl(&argv, "--no-edit", "--cleanup=strip", NULL);
4407+
4408+
ret = run_command_v_opt(argv.v, RUN_GIT_CMD);
4409+
strvec_clear(&argv);
4410+
return ret;
43814411
}
43824412

43834413
static int commit_staged_changes(struct repository *r,
@@ -4547,7 +4577,7 @@ int sequencer_continue(struct repository *r, struct replay_opts *opts)
45474577
goto release_todo_list;
45484578
}
45494579
} else if (!file_exists(get_todo_path(opts)))
4550-
return continue_single_pick(r);
4580+
return continue_single_pick(r, opts);
45514581
else if ((res = read_populate_todo(r, &todo_list, opts)))
45524582
goto release_todo_list;
45534583

@@ -4556,7 +4586,7 @@ int sequencer_continue(struct repository *r, struct replay_opts *opts)
45564586
if (refs_ref_exists(get_main_ref_store(r),
45574587
"CHERRY_PICK_HEAD") ||
45584588
refs_ref_exists(get_main_ref_store(r), "REVERT_HEAD")) {
4559-
res = continue_single_pick(r);
4589+
res = continue_single_pick(r, opts);
45604590
if (res)
45614591
goto release_todo_list;
45624592
}

sequencer.h

Lines changed: 4 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -31,8 +31,10 @@ enum commit_msg_cleanup_mode {
3131
struct replay_opts {
3232
enum replay_action action;
3333

34-
/* Boolean options */
34+
/* Tri-state options: unspecified, false, or true */
3535
int edit;
36+
37+
/* Boolean options */
3638
int record_origin;
3739
int no_commit;
3840
int signoff;
@@ -71,7 +73,7 @@ struct replay_opts {
7173
/* Only used by REPLAY_NONE */
7274
struct rev_info *revs;
7375
};
74-
#define REPLAY_OPTS_INIT { .action = -1, .current_fixups = STRBUF_INIT }
76+
#define REPLAY_OPTS_INIT { .edit = -1, .action = -1, .current_fixups = STRBUF_INIT }
7577

7678
/*
7779
* Note that ordering matters in this enum. Not only must it match the mapping

t/t3510-cherry-pick-sequence.sh

Lines changed: 31 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -65,7 +65,7 @@ test_expect_success 'cherry-pick persists opts correctly' '
6565
# gets interrupted, use a high-enough number that is larger
6666
# than the number of parents of any commit we have created
6767
mainline=4 &&
68-
test_expect_code 128 git cherry-pick -s -m $mainline --strategy=recursive -X patience -X ours initial..anotherpick &&
68+
test_expect_code 128 git cherry-pick -s -m $mainline --strategy=recursive -X patience -X ours --edit initial..anotherpick &&
6969
test_path_is_dir .git/sequencer &&
7070
test_path_is_file .git/sequencer/head &&
7171
test_path_is_file .git/sequencer/todo &&
@@ -84,6 +84,36 @@ test_expect_success 'cherry-pick persists opts correctly' '
8484
ours
8585
EOF
8686
git config --file=.git/sequencer/opts --get-all options.strategy-option >actual &&
87+
test_cmp expect actual &&
88+
echo "true" >expect &&
89+
git config --file=.git/sequencer/opts --get-all options.edit >actual &&
90+
test_cmp expect actual
91+
'
92+
93+
test_expect_success 'revert persists opts correctly' '
94+
pristine_detach initial &&
95+
# to make sure that the session to revert a sequence
96+
# gets interrupted, revert commits that are not in the history
97+
# of HEAD.
98+
test_expect_code 1 git revert -s --strategy=recursive -X patience -X ours --no-edit picked yetanotherpick &&
99+
test_path_is_dir .git/sequencer &&
100+
test_path_is_file .git/sequencer/head &&
101+
test_path_is_file .git/sequencer/todo &&
102+
test_path_is_file .git/sequencer/opts &&
103+
echo "true" >expect &&
104+
git config --file=.git/sequencer/opts --get-all options.signoff >actual &&
105+
test_cmp expect actual &&
106+
echo "recursive" >expect &&
107+
git config --file=.git/sequencer/opts --get-all options.strategy >actual &&
108+
test_cmp expect actual &&
109+
cat >expect <<-\EOF &&
110+
patience
111+
ours
112+
EOF
113+
git config --file=.git/sequencer/opts --get-all options.strategy-option >actual &&
114+
test_cmp expect actual &&
115+
echo "false" >expect &&
116+
git config --file=.git/sequencer/opts --get-all options.edit >actual &&
87117
test_cmp expect actual
88118
'
89119

0 commit comments

Comments
 (0)