Skip to content

Commit 4365955

Browse files
committed
rebase -i: generate the script via rebase--helper
This is substantially faster, improving the speedup relative to the shell script version of the interactive rebase from 2x to 3x on Windows. Signed-off-by: Johannes Schindelin <johannes.schindelin@gmx.de>
1 parent 7879ead commit 4365955

File tree

4 files changed

+75
-22
lines changed

4 files changed

+75
-22
lines changed

builtin/rebase--helper.c

Lines changed: 7 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -11,15 +11,19 @@ static const char * const builtin_rebase_helper_usage[] = {
1111
int cmd_rebase__helper(int argc, const char **argv, const char *prefix)
1212
{
1313
struct replay_opts opts = REPLAY_OPTS_INIT;
14+
int keep_empty = 0;
1415
enum {
15-
CONTINUE = 1, ABORT
16+
CONTINUE = 1, ABORT, MAKE_SCRIPT
1617
} command = 0;
1718
struct option options[] = {
1819
OPT_BOOL(0, "ff", &opts.allow_ff, N_("allow fast-forward")),
20+
OPT_BOOL(0, "keep-empty", &keep_empty, N_("keep empty commits")),
1921
OPT_CMDMODE(0, "continue", &command, N_("continue rebase"),
2022
CONTINUE),
2123
OPT_CMDMODE(0, "abort", &command, N_("abort rebase"),
2224
ABORT),
25+
OPT_CMDMODE(0, "make-script", &command,
26+
N_("make rebase script"), MAKE_SCRIPT),
2327
OPT_END()
2428
};
2529

@@ -36,5 +40,7 @@ int cmd_rebase__helper(int argc, const char **argv, const char *prefix)
3640
return sequencer_continue(&opts);
3741
if (command == ABORT && argc == 1)
3842
return sequencer_remove_state(&opts);
43+
if (command == MAKE_SCRIPT && argc > 1)
44+
return sequencer_make_script(keep_empty, stdout, argc, argv);
3945
usage_with_options(builtin_rebase_helper_usage, options);
4046
}

git-rebase--interactive.sh

Lines changed: 22 additions & 21 deletions
Original file line numberDiff line numberDiff line change
@@ -1188,26 +1188,27 @@ else
11881188
revisions=$onto...$orig_head
11891189
shortrevisions=$shorthead
11901190
fi
1191-
format=$(git config --get rebase.instructionFormat)
1192-
# the 'rev-list .. | sed' requires %m to parse; the instruction requires %H to parse
1193-
git rev-list $merges_option --format="%m%H ${format:-%s}" \
1194-
--reverse --left-right --topo-order \
1195-
$revisions ${restrict_revision+^$restrict_revision} | \
1196-
sed -n "s/^>//p" |
1197-
while read -r sha1 rest
1198-
do
1199-
1200-
if test -z "$keep_empty" && is_empty_commit $sha1 && ! is_merge_commit $sha1
1201-
then
1202-
comment_out="$comment_char "
1203-
else
1204-
comment_out=
1205-
fi
1191+
if test t != "$preserve_merges"
1192+
then
1193+
git rebase--helper --make-script ${keep_empty:+--keep-empty} \
1194+
$revisions ${restrict_revision+^$restrict_revision} > "$todo"
1195+
else
1196+
format=$(git config --get rebase.instructionFormat)
1197+
# the 'rev-list .. | sed' requires %m to parse; the instruction requires %H to parse
1198+
git rev-list $merges_option --format="%m%H ${format:-%s}" \
1199+
--reverse --left-right --topo-order \
1200+
$revisions ${restrict_revision+^$restrict_revision} | \
1201+
sed -n "s/^>//p" |
1202+
while read -r sha1 rest
1203+
do
1204+
1205+
if test -z "$keep_empty" && is_empty_commit $sha1 && ! is_merge_commit $sha1
1206+
then
1207+
comment_out="$comment_char "
1208+
else
1209+
comment_out=
1210+
fi
12061211

1207-
if test t != "$preserve_merges"
1208-
then
1209-
printf '%s\n' "${comment_out}pick $sha1 $rest" >>"$todo"
1210-
else
12111212
if test -z "$rebase_root"
12121213
then
12131214
preserve=t
@@ -1226,8 +1227,8 @@ do
12261227
touch "$rewritten"/$sha1
12271228
printf '%s\n' "${comment_out}pick $sha1 $rest" >>"$todo"
12281229
fi
1229-
fi
1230-
done
1230+
done
1231+
fi
12311232

12321233
# Watch for commits that been dropped by --cherry-pick
12331234
if test t = "$preserve_merges"

sequencer.c

Lines changed: 44 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -2161,3 +2161,47 @@ void append_signoff(struct strbuf *msgbuf, int ignore_footer, unsigned flag)
21612161

21622162
strbuf_release(&sob);
21632163
}
2164+
2165+
int sequencer_make_script(int keep_empty, FILE *out,
2166+
int argc, const char **argv)
2167+
{
2168+
char *format = "%s";
2169+
struct pretty_print_context pp = {0};
2170+
struct strbuf buf = STRBUF_INIT;
2171+
struct rev_info revs;
2172+
struct commit *commit;
2173+
2174+
init_revisions(&revs, NULL);
2175+
revs.verbose_header = 1;
2176+
revs.max_parents = 1;
2177+
revs.cherry_pick = 1;
2178+
revs.limited = 1;
2179+
revs.reverse = 1;
2180+
revs.right_only = 1;
2181+
revs.sort_order = REV_SORT_IN_GRAPH_ORDER;
2182+
revs.topo_order = 1;
2183+
2184+
revs.pretty_given = 1;
2185+
git_config_get_string("rebase.instructionFormat", &format);
2186+
get_commit_format(format, &revs);
2187+
pp.fmt = revs.commit_format;
2188+
pp.output_encoding = get_log_output_encoding();
2189+
2190+
if (setup_revisions(argc, argv, &revs, NULL) > 1)
2191+
return error("make_script: unhandled options");
2192+
2193+
if (prepare_revision_walk(&revs) < 0)
2194+
return error("make_script: error preparing revisions");
2195+
2196+
while ((commit = get_revision(&revs))) {
2197+
strbuf_reset(&buf);
2198+
if (!keep_empty && is_original_commit_empty(commit))
2199+
strbuf_addf(&buf, "%c ", comment_line_char);
2200+
strbuf_addf(&buf, "pick %s ", oid_to_hex(&commit->object.oid));
2201+
pretty_print_commit(&pp, commit, &buf);
2202+
strbuf_addch(&buf, '\n');
2203+
fputs(buf.buf, out);
2204+
}
2205+
strbuf_release(&buf);
2206+
return 0;
2207+
}

sequencer.h

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -46,6 +46,8 @@ int sequencer_remove_state(struct replay_opts *opts);
4646

4747
int sequencer_commit(const char *defmsg, struct replay_opts *opts,
4848
int allow_empty, int edit, int amend);
49+
int sequencer_make_script(int keep_empty, FILE *out,
50+
int argc, const char **argv);
4951

5052
extern const char sign_off_header[];
5153

0 commit comments

Comments
 (0)