Skip to content

Commit c7d8f69

Browse files
committed
Merge branch 'en/rebase-no-keep-empty'
"git rebase" (again) learns to honor "--no-keep-empty", which lets the user to discard commits that are empty from the beginning (as opposed to the ones that become empty because of rebasing). The interactive rebase also marks commits that are empty in the todo. * en/rebase-no-keep-empty: rebase: fix an incompatible-options error message rebase: reinstate --no-keep-empty rebase -i: mark commits that begin empty in todo editor
2 parents 8b39dfd + 50ed761 commit c7d8f69

File tree

6 files changed

+93
-30
lines changed

6 files changed

+93
-30
lines changed

Documentation/git-rebase.txt

Lines changed: 31 additions & 16 deletions
Original file line numberDiff line numberDiff line change
@@ -277,20 +277,32 @@ See also INCOMPATIBLE OPTIONS below.
277277
Other options, like --exec, will use the default of drop unless
278278
-i/--interactive is explicitly specified.
279279
+
280-
Note that commits which start empty are kept, and commits which are
281-
clean cherry-picks (as determined by `git log --cherry-mark ...`) are
282-
always dropped.
280+
Note that commits which start empty are kept (unless --no-keep-empty
281+
is specified), and commits which are clean cherry-picks (as determined
282+
by `git log --cherry-mark ...`) are always dropped.
283283
+
284284
See also INCOMPATIBLE OPTIONS below.
285285

286+
--no-keep-empty::
286287
--keep-empty::
287-
No-op. Rebasing commits that started empty (had no change
288-
relative to their parent) used to fail and this option would
289-
override that behavior, allowing commits with empty changes to
290-
be rebased. Now commits with no changes do not cause rebasing
291-
to halt.
288+
Do not keep commits that start empty before the rebase
289+
(i.e. that do not change anything from its parent) in the
290+
result. The default is to keep commits which start empty,
291+
since creating such commits requires passing the --allow-empty
292+
override flag to `git commit`, signifying that a user is very
293+
intentionally creating such a commit and thus wants to keep
294+
it.
292295
+
293-
See also BEHAVIORAL DIFFERENCES and INCOMPATIBLE OPTIONS below.
296+
Usage of this flag will probably be rare, since you can get rid of
297+
commits that start empty by just firing up an interactive rebase and
298+
removing the lines corresponding to the commits you don't want. This
299+
flag exists as a convenient shortcut, such as for cases where external
300+
tools generate many empty commits and you want them all removed.
301+
+
302+
For commits which do not start empty but become empty after rebasing,
303+
see the --empty flag.
304+
+
305+
See also INCOMPATIBLE OPTIONS below.
294306

295307
--allow-empty-message::
296308
No-op. Rebasing commits with an empty message used to fail
@@ -590,7 +602,7 @@ are incompatible with the following options:
590602
* --preserve-merges
591603
* --interactive
592604
* --exec
593-
* --keep-empty
605+
* --no-keep-empty
594606
* --empty=
595607
* --edit-todo
596608
* --root when used in combination with --onto
@@ -623,12 +635,15 @@ commits that started empty, though these are rare in practice. It
623635
also drops commits that become empty and has no option for controlling
624636
this behavior.
625637

626-
The merge backend keeps intentionally empty commits. Similar to the
627-
apply backend, by default the merge backend drops commits that become
628-
empty unless -i/--interactive is specified (in which case it stops and
629-
asks the user what to do). The merge backend also has an
630-
--empty={drop,keep,ask} option for changing the behavior of handling
631-
commits that become empty.
638+
The merge backend keeps intentionally empty commits by default (though
639+
with -i they are marked as empty in the todo list editor, or they can
640+
be dropped automatically with --no-keep-empty).
641+
642+
Similar to the apply backend, by default the merge backend drops
643+
commits that become empty unless -i/--interactive is specified (in
644+
which case it stops and asks the user what to do). The merge backend
645+
also has an --empty={drop,keep,ask} option for changing the behavior
646+
of handling commits that become empty.
632647

633648
Directory rename detection
634649
~~~~~~~~~~~~~~~~~~~~~~~~~~

builtin/rebase.c

Lines changed: 10 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -85,6 +85,7 @@ struct rebase_options {
8585
const char *action;
8686
int signoff;
8787
int allow_rerere_autoupdate;
88+
int keep_empty;
8889
int autosquash;
8990
char *gpg_sign_opt;
9091
int autostash;
@@ -100,6 +101,7 @@ struct rebase_options {
100101
#define REBASE_OPTIONS_INIT { \
101102
.type = REBASE_UNSPECIFIED, \
102103
.empty = EMPTY_UNSPECIFIED, \
104+
.keep_empty = 1, \
103105
.default_backend = "merge", \
104106
.flags = REBASE_NO_QUIET, \
105107
.git_am_opts = ARGV_ARRAY_INIT, \
@@ -379,6 +381,7 @@ static int run_sequencer_rebase(struct rebase_options *opts,
379381

380382
git_config_get_bool("rebase.abbreviatecommands", &abbreviate_commands);
381383

384+
flags |= opts->keep_empty ? TODO_LIST_KEEP_EMPTY : 0;
382385
flags |= abbreviate_commands ? TODO_LIST_ABBREVIATE_CMDS : 0;
383386
flags |= opts->rebase_merges ? TODO_LIST_REBASE_MERGES : 0;
384387
flags |= opts->rebase_cousins > 0 ? TODO_LIST_REBASE_COUSINS : 0;
@@ -442,17 +445,16 @@ static int run_sequencer_rebase(struct rebase_options *opts,
442445
return ret;
443446
}
444447

448+
static void imply_merge(struct rebase_options *opts, const char *option);
445449
static int parse_opt_keep_empty(const struct option *opt, const char *arg,
446450
int unset)
447451
{
448452
struct rebase_options *opts = opt->value;
449453

450454
BUG_ON_OPT_ARG(arg);
451455

452-
/*
453-
* If we ever want to remap --keep-empty to --empty=keep, insert:
454-
* opts->empty = unset ? EMPTY_UNSPECIFIED : EMPTY_KEEP;
455-
*/
456+
imply_merge(opts, unset ? "--no-keep-empty" : "--keep-empty");
457+
opts->keep_empty = !unset;
456458
opts->type = REBASE_MERGE;
457459
return 0;
458460
}
@@ -471,7 +473,7 @@ int cmd_rebase__interactive(int argc, const char **argv, const char *prefix)
471473
OPT_NEGBIT(0, "ff", &opts.flags, N_("allow fast-forward"),
472474
REBASE_FORCE),
473475
{ OPTION_CALLBACK, 'k', "keep-empty", &options, NULL,
474-
N_("(DEPRECATED) keep empty commits"),
476+
N_("keep commits which start empty"),
475477
PARSE_OPT_NOARG | PARSE_OPT_HIDDEN,
476478
parse_opt_keep_empty },
477479
OPT_BOOL_F(0, "allow-empty-message", &opts.allow_empty_message,
@@ -559,7 +561,7 @@ static void imply_merge(struct rebase_options *opts, const char *option)
559561
{
560562
switch (opts->type) {
561563
case REBASE_APPLY:
562-
die(_("%s requires an interactive rebase"), option);
564+
die(_("%s requires the merge backend"), option);
563565
break;
564566
case REBASE_MERGE:
565567
case REBASE_PRESERVE_MERGES:
@@ -1163,6 +1165,7 @@ static int run_specific_rebase(struct rebase_options *opts, enum action action)
11631165
opts->allow_rerere_autoupdate ?
11641166
opts->allow_rerere_autoupdate == RERERE_AUTOUPDATE ?
11651167
"--rerere-autoupdate" : "--no-rerere-autoupdate" : "");
1168+
add_var(&script_snippet, "keep_empty", opts->keep_empty ? "yes" : "");
11661169
add_var(&script_snippet, "autosquash", opts->autosquash ? "t" : "");
11671170
add_var(&script_snippet, "gpg_sign_opt", opts->gpg_sign_opt);
11681171
add_var(&script_snippet, "cmd", opts->cmd);
@@ -1548,7 +1551,7 @@ int cmd_rebase(int argc, const char **argv, const char *prefix)
15481551
N_("how to handle commits that become empty"),
15491552
PARSE_OPT_NONEG, parse_opt_empty),
15501553
{ OPTION_CALLBACK, 'k', "keep-empty", &options, NULL,
1551-
N_("(DEPRECATED) keep empty commits"),
1554+
N_("keep commits which start empty"),
15521555
PARSE_OPT_NOARG | PARSE_OPT_HIDDEN,
15531556
parse_opt_keep_empty },
15541557
OPT_BOOL(0, "autosquash", &options.autosquash,

sequencer.c

Lines changed: 11 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -4615,6 +4615,7 @@ static int make_script_with_merges(struct pretty_print_context *pp,
46154615
struct rev_info *revs, struct strbuf *out,
46164616
unsigned flags)
46174617
{
4618+
int keep_empty = flags & TODO_LIST_KEEP_EMPTY;
46184619
int rebase_cousins = flags & TODO_LIST_REBASE_COUSINS;
46194620
int root_with_onto = flags & TODO_LIST_ROOT_WITH_ONTO;
46204621
struct strbuf buf = STRBUF_INIT, oneline = STRBUF_INIT;
@@ -4669,6 +4670,8 @@ static int make_script_with_merges(struct pretty_print_context *pp,
46694670
is_empty = is_original_commit_empty(commit);
46704671
if (!is_empty && (commit->object.flags & PATCHSAME))
46714672
continue;
4673+
if (is_empty && !keep_empty)
4674+
continue;
46724675

46734676
strbuf_reset(&oneline);
46744677
pretty_print_commit(pp, commit, &oneline);
@@ -4680,6 +4683,9 @@ static int make_script_with_merges(struct pretty_print_context *pp,
46804683
strbuf_addf(&buf, "%s %s %s", cmd_pick,
46814684
oid_to_hex(&commit->object.oid),
46824685
oneline.buf);
4686+
if (is_empty)
4687+
strbuf_addf(&buf, " %c empty",
4688+
comment_line_char);
46834689

46844690
FLEX_ALLOC_STR(entry, string, buf.buf);
46854691
oidcpy(&entry->entry.oid, &commit->object.oid);
@@ -4843,6 +4849,7 @@ int sequencer_make_script(struct repository *r, struct strbuf *out, int argc,
48434849
struct pretty_print_context pp = {0};
48444850
struct rev_info revs;
48454851
struct commit *commit;
4852+
int keep_empty = flags & TODO_LIST_KEEP_EMPTY;
48464853
const char *insn = flags & TODO_LIST_ABBREVIATE_CMDS ? "p" : "pick";
48474854
int rebase_merges = flags & TODO_LIST_REBASE_MERGES;
48484855

@@ -4882,9 +4889,13 @@ int sequencer_make_script(struct repository *r, struct strbuf *out, int argc,
48824889

48834890
if (!is_empty && (commit->object.flags & PATCHSAME))
48844891
continue;
4892+
if (is_empty && !keep_empty)
4893+
continue;
48854894
strbuf_addf(out, "%s %s ", insn,
48864895
oid_to_hex(&commit->object.oid));
48874896
pretty_print_commit(&pp, commit, out);
4897+
if (is_empty)
4898+
strbuf_addf(out, " %c empty", comment_line_char);
48884899
strbuf_addch(out, '\n');
48894900
}
48904901
return 0;

sequencer.h

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -134,7 +134,7 @@ int sequencer_rollback(struct repository *repo, struct replay_opts *opts);
134134
int sequencer_skip(struct repository *repo, struct replay_opts *opts);
135135
int sequencer_remove_state(struct replay_opts *opts);
136136

137-
/* #define TODO_LIST_KEEP_EMPTY (1U << 0) */ /* No longer used */
137+
#define TODO_LIST_KEEP_EMPTY (1U << 0)
138138
#define TODO_LIST_SHORTEN_IDS (1U << 1)
139139
#define TODO_LIST_ABBREVIATE_CMDS (1U << 2)
140140
#define TODO_LIST_REBASE_MERGES (1U << 3)

t/t3421-rebase-topology-linear.sh

Lines changed: 4 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -220,14 +220,13 @@ test_have_prereq !REBASE_P || test_run_rebase failure -p
220220
test_run_rebase () {
221221
result=$1
222222
shift
223-
test_expect_$result "rebase $* --keep-empty" "
223+
test_expect_$result "rebase $* --no-keep-empty drops begin-empty commits" "
224224
reset_rebase &&
225-
git rebase $* --keep-empty c l &&
226-
test_cmp_rev c HEAD~3 &&
227-
test_linear_range 'd k l' c..
225+
git rebase $* --no-keep-empty c l &&
226+
test_cmp_rev c HEAD~2 &&
227+
test_linear_range 'd l' c..
228228
"
229229
}
230-
test_run_rebase success --apply
231230
test_run_rebase success -m
232231
test_run_rebase success -i
233232
test_have_prereq !REBASE_P || test_run_rebase success -p
@@ -242,7 +241,6 @@ test_run_rebase () {
242241
test_linear_range 'd k l' j..
243242
"
244243
}
245-
test_run_rebase success --apply
246244
test_run_rebase success -m
247245
test_run_rebase success -i
248246
test_have_prereq !REBASE_P || test_run_rebase success -p

t/t3424-rebase-empty.sh

Lines changed: 36 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -123,6 +123,42 @@ test_expect_success 'rebase --interactive uses default of --empty=ask' '
123123
test_cmp expect actual
124124
'
125125

126+
test_expect_success 'rebase --merge --empty=drop --keep-empty' '
127+
git checkout -B testing localmods &&
128+
git rebase --merge --empty=drop --keep-empty upstream &&
129+
130+
test_write_lines D C B A >expect &&
131+
git log --format=%s >actual &&
132+
test_cmp expect actual
133+
'
134+
135+
test_expect_success 'rebase --merge --empty=drop --no-keep-empty' '
136+
git checkout -B testing localmods &&
137+
git rebase --merge --empty=drop --no-keep-empty upstream &&
138+
139+
test_write_lines C B A >expect &&
140+
git log --format=%s >actual &&
141+
test_cmp expect actual
142+
'
143+
144+
test_expect_success 'rebase --merge --empty=keep --keep-empty' '
145+
git checkout -B testing localmods &&
146+
git rebase --merge --empty=keep --keep-empty upstream &&
147+
148+
test_write_lines D C2 C B A >expect &&
149+
git log --format=%s >actual &&
150+
test_cmp expect actual
151+
'
152+
153+
test_expect_success 'rebase --merge --empty=keep --no-keep-empty' '
154+
git checkout -B testing localmods &&
155+
git rebase --merge --empty=keep --no-keep-empty upstream &&
156+
157+
test_write_lines C2 C B A >expect &&
158+
git log --format=%s >actual &&
159+
test_cmp expect actual
160+
'
161+
126162
test_expect_success 'rebase --merge does not leave state laying around' '
127163
git checkout -B testing localmods~2 &&
128164
git rebase --merge upstream &&

0 commit comments

Comments
 (0)