Skip to content

Commit f085189

Browse files
committed
Merge branch 'pw/advise-rebase-skip'
The mechanism to prevent "git commit" from making an empty commit or amending during an interrupted cherry-pick was broken during the rewrite of "git rebase" in C, which has been corrected. * pw/advise-rebase-skip: commit: give correct advice for empty commit during a rebase commit: encapsulate determine_whence() for sequencer commit: use enum value for multiple cherry-picks sequencer: write CHERRY_PICK_HEAD for reword and edit cherry-pick: check commit error messages cherry-pick: add test for `--skip` advice in `git commit` t3404: use test_cmp_rev
2 parents 9a0fa17 + 430b75f commit f085189

File tree

8 files changed

+233
-46
lines changed

8 files changed

+233
-46
lines changed

builtin/commit.c

Lines changed: 24 additions & 16 deletions
Original file line numberDiff line numberDiff line change
@@ -59,6 +59,9 @@ N_("The previous cherry-pick is now empty, possibly due to conflict resolution.\
5959
" git commit --allow-empty\n"
6060
"\n");
6161

62+
static const char empty_rebase_pick_advice[] =
63+
N_("Otherwise, please use 'git rebase --skip'\n");
64+
6265
static const char empty_cherry_pick_advice_single[] =
6366
N_("Otherwise, please use 'git cherry-pick --skip'\n");
6467

@@ -122,7 +125,6 @@ static enum commit_msg_cleanup_mode cleanup_mode;
122125
static const char *cleanup_arg;
123126

124127
static enum commit_whence whence;
125-
static int sequencer_in_use;
126128
static int use_editor = 1, include_status = 1;
127129
static int have_option_m;
128130
static struct strbuf message = STRBUF_INIT;
@@ -179,12 +181,7 @@ static void determine_whence(struct wt_status *s)
179181
{
180182
if (file_exists(git_path_merge_head(the_repository)))
181183
whence = FROM_MERGE;
182-
else if (file_exists(git_path_cherry_pick_head(the_repository))) {
183-
whence = FROM_CHERRY_PICK;
184-
if (file_exists(git_path_seq_dir()))
185-
sequencer_in_use = 1;
186-
}
187-
else
184+
else if (!sequencer_determine_whence(the_repository, &whence))
188185
whence = FROM_COMMIT;
189186
if (s)
190187
s->whence = whence;
@@ -477,8 +474,10 @@ static const char *prepare_index(int argc, const char **argv, const char *prefix
477474
if (whence != FROM_COMMIT) {
478475
if (whence == FROM_MERGE)
479476
die(_("cannot do a partial commit during a merge."));
480-
else if (whence == FROM_CHERRY_PICK)
477+
else if (is_from_cherry_pick(whence))
481478
die(_("cannot do a partial commit during a cherry-pick."));
479+
else if (is_from_rebase(whence))
480+
die(_("cannot do a partial commit during a rebase."));
482481
}
483482

484483
if (list_paths(&partial, !current_head ? NULL : "HEAD", &pathspec))
@@ -795,7 +794,7 @@ static int prepare_to_commit(const char *index_file, const char *prefix,
795794
*/
796795
else if (whence == FROM_MERGE)
797796
hook_arg1 = "merge";
798-
else if (whence == FROM_CHERRY_PICK) {
797+
else if (is_from_cherry_pick(whence) || whence == FROM_REBASE_PICK) {
799798
hook_arg1 = "commit";
800799
hook_arg2 = "CHERRY_PICK_HEAD";
801800
}
@@ -973,12 +972,15 @@ static int prepare_to_commit(const char *index_file, const char *prefix,
973972
run_status(stdout, index_file, prefix, 0, s);
974973
if (amend)
975974
fputs(_(empty_amend_advice), stderr);
976-
else if (whence == FROM_CHERRY_PICK) {
975+
else if (is_from_cherry_pick(whence) ||
976+
whence == FROM_REBASE_PICK) {
977977
fputs(_(empty_cherry_pick_advice), stderr);
978-
if (!sequencer_in_use)
978+
if (whence == FROM_CHERRY_PICK_SINGLE)
979979
fputs(_(empty_cherry_pick_advice_single), stderr);
980-
else
980+
else if (whence == FROM_CHERRY_PICK_MULTI)
981981
fputs(_(empty_cherry_pick_advice_multi), stderr);
982+
else
983+
fputs(_(empty_rebase_pick_advice), stderr);
982984
}
983985
return 0;
984986
}
@@ -1181,8 +1183,10 @@ static int parse_and_validate_options(int argc, const char *argv[],
11811183
if (amend && whence != FROM_COMMIT) {
11821184
if (whence == FROM_MERGE)
11831185
die(_("You are in the middle of a merge -- cannot amend."));
1184-
else if (whence == FROM_CHERRY_PICK)
1186+
else if (is_from_cherry_pick(whence))
11851187
die(_("You are in the middle of a cherry-pick -- cannot amend."));
1188+
else if (whence == FROM_REBASE_PICK)
1189+
die(_("You are in the middle of a rebase -- cannot amend."));
11861190
}
11871191
if (fixup_message && squash_message)
11881192
die(_("Options --squash and --fixup cannot be used together"));
@@ -1204,7 +1208,8 @@ static int parse_and_validate_options(int argc, const char *argv[],
12041208
use_message = edit_message;
12051209
if (amend && !use_message && !fixup_message)
12061210
use_message = "HEAD";
1207-
if (!use_message && whence != FROM_CHERRY_PICK && renew_authorship)
1211+
if (!use_message && !is_from_cherry_pick(whence) &&
1212+
!is_from_rebase(whence) && renew_authorship)
12081213
die(_("--reset-author can be used only with -C, -c or --amend."));
12091214
if (use_message) {
12101215
use_message_buffer = read_commit_message(use_message);
@@ -1213,7 +1218,8 @@ static int parse_and_validate_options(int argc, const char *argv[],
12131218
author_message_buffer = use_message_buffer;
12141219
}
12151220
}
1216-
if (whence == FROM_CHERRY_PICK && !renew_authorship) {
1221+
if ((is_from_cherry_pick(whence) || whence == FROM_REBASE_PICK) &&
1222+
!renew_authorship) {
12171223
author_message = "CHERRY_PICK_HEAD";
12181224
author_message_buffer = read_commit_message(author_message);
12191225
}
@@ -1631,8 +1637,10 @@ int cmd_commit(int argc, const char **argv, const char *prefix)
16311637
reduce_heads_replace(&parents);
16321638
} else {
16331639
if (!reflog_msg)
1634-
reflog_msg = (whence == FROM_CHERRY_PICK)
1640+
reflog_msg = is_from_cherry_pick(whence)
16351641
? "commit (cherry-pick)"
1642+
: is_from_rebase(whence)
1643+
? "commit (rebase)"
16361644
: "commit";
16371645
commit_list_insert(current_head, &parents);
16381646
}

sequencer.c

Lines changed: 44 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -40,7 +40,7 @@ static const char cherry_picked_prefix[] = "(cherry picked from commit ";
4040

4141
GIT_PATH_FUNC(git_path_commit_editmsg, "COMMIT_EDITMSG")
4242

43-
GIT_PATH_FUNC(git_path_seq_dir, "sequencer")
43+
static GIT_PATH_FUNC(git_path_seq_dir, "sequencer")
4444

4545
static GIT_PATH_FUNC(git_path_todo_file, "sequencer/todo")
4646
static GIT_PATH_FUNC(git_path_opts_file, "sequencer/opts")
@@ -1433,9 +1433,19 @@ static int try_to_commit(struct repository *r,
14331433
return res;
14341434
}
14351435

1436+
static int write_rebase_head(struct object_id *oid)
1437+
{
1438+
if (update_ref("rebase", "REBASE_HEAD", oid,
1439+
NULL, REF_NO_DEREF, UPDATE_REFS_MSG_ON_ERR))
1440+
return error(_("could not update %s"), "REBASE_HEAD");
1441+
1442+
return 0;
1443+
}
1444+
14361445
static int do_commit(struct repository *r,
14371446
const char *msg_file, const char *author,
1438-
struct replay_opts *opts, unsigned int flags)
1447+
struct replay_opts *opts, unsigned int flags,
1448+
struct object_id *oid)
14391449
{
14401450
int res = 1;
14411451

@@ -1460,8 +1470,12 @@ static int do_commit(struct repository *r,
14601470
return res;
14611471
}
14621472
}
1463-
if (res == 1)
1473+
if (res == 1) {
1474+
if (is_rebase_i(opts) && oid)
1475+
if (write_rebase_head(oid))
1476+
return -1;
14641477
return run_git_commit(r, msg_file, opts, flags);
1478+
}
14651479

14661480
return res;
14671481
}
@@ -1929,7 +1943,9 @@ static int do_pick_commit(struct repository *r,
19291943
* However, if the merge did not even start, then we don't want to
19301944
* write it at all.
19311945
*/
1932-
if (command == TODO_PICK && !opts->no_commit && (res == 0 || res == 1) &&
1946+
if ((command == TODO_PICK || command == TODO_REWORD ||
1947+
command == TODO_EDIT) && !opts->no_commit &&
1948+
(res == 0 || res == 1) &&
19331949
update_ref(NULL, "CHERRY_PICK_HEAD", &commit->object.oid, NULL,
19341950
REF_NO_DEREF, UPDATE_REFS_MSG_ON_ERR))
19351951
res = -1;
@@ -1965,7 +1981,8 @@ static int do_pick_commit(struct repository *r,
19651981
} /* else allow == 0 and there's nothing special to do */
19661982
if (!opts->no_commit && !drop_commit) {
19671983
if (author || command == TODO_REVERT || (flags & AMEND_MSG))
1968-
res = do_commit(r, msg_file, author, opts, flags);
1984+
res = do_commit(r, msg_file, author, opts, flags,
1985+
commit? &commit->object.oid : NULL);
19691986
else
19701987
res = error(_("unable to parse commit author"));
19711988
*check_todo = !!(flags & EDIT_MSG);
@@ -3000,9 +3017,7 @@ static int make_patch(struct repository *r,
30003017
p = short_commit_name(commit);
30013018
if (write_message(p, strlen(p), rebase_path_stopped_sha(), 1) < 0)
30023019
return -1;
3003-
if (update_ref("rebase", "REBASE_HEAD", &commit->object.oid,
3004-
NULL, REF_NO_DEREF, UPDATE_REFS_MSG_ON_ERR))
3005-
res |= error(_("could not update %s"), "REBASE_HEAD");
3020+
res |= write_rebase_head(&commit->object.oid);
30063021

30073022
strbuf_addf(&buf, "%s/patch", get_dir(opts));
30083023
memset(&log_tree_opt, 0, sizeof(log_tree_opt));
@@ -5315,3 +5330,24 @@ int todo_list_rearrange_squash(struct todo_list *todo_list)
53155330

53165331
return 0;
53175332
}
5333+
5334+
int sequencer_determine_whence(struct repository *r, enum commit_whence *whence)
5335+
{
5336+
if (file_exists(git_path_cherry_pick_head(r))) {
5337+
struct object_id cherry_pick_head, rebase_head;
5338+
5339+
if (file_exists(git_path_seq_dir()))
5340+
*whence = FROM_CHERRY_PICK_MULTI;
5341+
if (file_exists(rebase_path()) &&
5342+
!get_oid("REBASE_HEAD", &rebase_head) &&
5343+
!get_oid("CHERRY_PICK_HEAD", &cherry_pick_head) &&
5344+
oideq(&rebase_head, &cherry_pick_head))
5345+
*whence = FROM_REBASE_PICK;
5346+
else
5347+
*whence = FROM_CHERRY_PICK_SINGLE;
5348+
5349+
return 1;
5350+
}
5351+
5352+
return 0;
5353+
}

sequencer.h

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -3,12 +3,12 @@
33

44
#include "cache.h"
55
#include "strbuf.h"
6+
#include "wt-status.h"
67

78
struct commit;
89
struct repository;
910

1011
const char *git_path_commit_editmsg(void);
11-
const char *git_path_seq_dir(void);
1212
const char *rebase_path_todo(void);
1313
const char *rebase_path_todo_backup(void);
1414
const char *rebase_path_dropped(void);
@@ -206,4 +206,5 @@ int write_basic_state(struct replay_opts *opts, const char *head_name,
206206
void sequencer_post_commit_cleanup(struct repository *r, int verbose);
207207
int sequencer_get_last_command(struct repository* r,
208208
enum replay_action *action);
209+
int sequencer_determine_whence(struct repository *r, enum commit_whence *whence);
209210
#endif /* SEQUENCER_H */

t/t3403-rebase-skip.sh

Lines changed: 79 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -29,6 +29,13 @@ test_expect_success setup '
2929
test_tick &&
3030
git commit -m reverted-goodbye &&
3131
git tag reverted-goodbye &&
32+
git checkout goodbye &&
33+
test_tick &&
34+
GIT_AUTHOR_NAME="Another Author" \
35+
GIT_AUTHOR_EMAIL="[email protected]" \
36+
git commit --amend --no-edit -m amended-goodbye &&
37+
test_tick &&
38+
git tag amended-goodbye &&
3239
3340
git checkout -f skip-reference &&
3441
echo moo > hello &&
@@ -85,6 +92,78 @@ test_expect_success 'moved back to branch correctly' '
8592

8693
test_debug 'gitk --all & sleep 1'
8794

95+
test_expect_success 'correct advice upon picking empty commit' '
96+
test_when_finished "git rebase --abort" &&
97+
test_must_fail git rebase -i --onto goodbye \
98+
amended-goodbye^ amended-goodbye 2>err &&
99+
test_i18ngrep "previous cherry-pick is now empty" err &&
100+
test_i18ngrep "git rebase --skip" err &&
101+
test_must_fail git commit &&
102+
test_i18ngrep "git rebase --skip" err
103+
'
104+
105+
test_expect_success 'correct authorship when committing empty pick' '
106+
test_when_finished "git rebase --abort" &&
107+
test_must_fail git rebase -i --onto goodbye \
108+
amended-goodbye^ amended-goodbye &&
109+
git commit --allow-empty &&
110+
git log --pretty=format:"%an <%ae>%n%ad%B" -1 amended-goodbye >expect &&
111+
git log --pretty=format:"%an <%ae>%n%ad%B" -1 HEAD >actual &&
112+
test_cmp expect actual
113+
'
114+
115+
test_expect_success 'correct advice upon rewording empty commit' '
116+
test_when_finished "git rebase --abort" &&
117+
(
118+
set_fake_editor &&
119+
test_must_fail env FAKE_LINES="reword 1" git rebase -i \
120+
--onto goodbye amended-goodbye^ amended-goodbye 2>err
121+
) &&
122+
test_i18ngrep "previous cherry-pick is now empty" err &&
123+
test_i18ngrep "git rebase --skip" err &&
124+
test_must_fail git commit &&
125+
test_i18ngrep "git rebase --skip" err
126+
'
127+
128+
test_expect_success 'correct advice upon editing empty commit' '
129+
test_when_finished "git rebase --abort" &&
130+
(
131+
set_fake_editor &&
132+
test_must_fail env FAKE_LINES="edit 1" git rebase -i \
133+
--onto goodbye amended-goodbye^ amended-goodbye 2>err
134+
) &&
135+
test_i18ngrep "previous cherry-pick is now empty" err &&
136+
test_i18ngrep "git rebase --skip" err &&
137+
test_must_fail git commit &&
138+
test_i18ngrep "git rebase --skip" err
139+
'
140+
141+
test_expect_success 'correct advice upon cherry-picking an empty commit during a rebase' '
142+
test_when_finished "git rebase --abort" &&
143+
(
144+
set_fake_editor &&
145+
test_must_fail env FAKE_LINES="1 exec_git_cherry-pick_amended-goodbye" \
146+
git rebase -i goodbye^ goodbye 2>err
147+
) &&
148+
test_i18ngrep "previous cherry-pick is now empty" err &&
149+
test_i18ngrep "git cherry-pick --skip" err &&
150+
test_must_fail git commit 2>err &&
151+
test_i18ngrep "git cherry-pick --skip" err
152+
'
153+
154+
test_expect_success 'correct advice upon multi cherry-pick picking an empty commit during a rebase' '
155+
test_when_finished "git rebase --abort" &&
156+
(
157+
set_fake_editor &&
158+
test_must_fail env FAKE_LINES="1 exec_git_cherry-pick_goodbye_amended-goodbye" \
159+
git rebase -i goodbye^^ goodbye 2>err
160+
) &&
161+
test_i18ngrep "previous cherry-pick is now empty" err &&
162+
test_i18ngrep "git cherry-pick --skip" err &&
163+
test_must_fail git commit 2>err &&
164+
test_i18ngrep "git cherry-pick --skip" err
165+
'
166+
88167
test_expect_success 'fixup that empties commit fails' '
89168
test_when_finished "git rebase --abort" &&
90169
(

0 commit comments

Comments
 (0)