rebase -i: combine rebase--interactive.c with rebase.c

In order to run `rebase -i` without forking `rebase--interactive` it will be convenient to have all the code from rebase--interactive.c in rebase.c. This is a straight forward copy of the code from rebase--interactive.c, it will be simplified slightly in the next commit. Signed-off-by: Phillip Wood <phillip.wood@dunelm.org.uk> Signed-off-by: Junio C Hamano <gitster@pobox.com>

Phillip Wood committed Apr 17, 2019 at 15:30 UTC 0609b741a43dee2934ceb76850fea5963ab0c8f6
3 files changed +367 -378
Makefile
-1
@@ -1130,7 +1130,6 @@ BUILTIN_OBJS += builtin/push.o
1130 BUILTIN_OBJS += builtin/range-diff.o
1131 BUILTIN_OBJS += builtin/read-tree.o
1132 BUILTIN_OBJS += builtin/rebase.o
1133 -BUILTIN_OBJS += builtin/rebase--interactive.o
1133 BUILTIN_OBJS += builtin/receive-pack.o
1134 BUILTIN_OBJS += builtin/reflog.o
1135 BUILTIN_OBJS += builtin/remote.o
builtin/rebase--interactive.c deleted
-377
@@ -1,377 +0,0 @@
1 -#define USE_THE_INDEX_COMPATIBILITY_MACROS
2 -#include "builtin.h"
3 -#include "cache.h"
4 -#include "config.h"
5 -#include "parse-options.h"
6 -#include "sequencer.h"
7 -#include "rebase-interactive.h"
8 -#include "argv-array.h"
9 -#include "refs.h"
10 -#include "rerere.h"
11 -#include "run-command.h"
12 -
13 -static GIT_PATH_FUNC(path_state_dir, "rebase-merge/")
14 -static GIT_PATH_FUNC(path_squash_onto, "rebase-merge/squash-onto")
15 -static GIT_PATH_FUNC(path_interactive, "rebase-merge/interactive")
16 -
17 -static int add_exec_commands(struct string_list *commands)
18 -{
19 - const char *todo_file = rebase_path_todo();
20 - struct todo_list todo_list = TODO_LIST_INIT;
21 - int res;
22 -
23 - if (strbuf_read_file(&todo_list.buf, todo_file, 0) < 0)
24 - return error_errno(_("could not read '%s'."), todo_file);
25 -
26 - if (todo_list_parse_insn_buffer(the_repository, todo_list.buf.buf,
27 - &todo_list)) {
28 - todo_list_release(&todo_list);
29 - return error(_("unusable todo list: '%s'"), todo_file);
30 - }
31 -
32 - todo_list_add_exec_commands(&todo_list, commands);
33 - res = todo_list_write_to_file(the_repository, &todo_list,
34 - todo_file, NULL, NULL, -1, 0);
35 - todo_list_release(&todo_list);
36 -
37 - if (res)
38 - return error_errno(_("could not write '%s'."), todo_file);
39 - return 0;
40 -}
41 -
42 -static int rearrange_squash_in_todo_file(void)
43 -{
44 - const char *todo_file = rebase_path_todo();
45 - struct todo_list todo_list = TODO_LIST_INIT;
46 - int res = 0;
47 -
48 - if (strbuf_read_file(&todo_list.buf, todo_file, 0) < 0)
49 - return error_errno(_("could not read '%s'."), todo_file);
50 - if (todo_list_parse_insn_buffer(the_repository, todo_list.buf.buf,
51 - &todo_list)) {
52 - todo_list_release(&todo_list);
53 - return error(_("unusable todo list: '%s'"), todo_file);
54 - }
55 -
56 - res = todo_list_rearrange_squash(&todo_list);
57 - if (!res)
58 - res = todo_list_write_to_file(the_repository, &todo_list,
59 - todo_file, NULL, NULL, -1, 0);
60 -
61 - todo_list_release(&todo_list);
62 -
63 - if (res)
64 - return error_errno(_("could not write '%s'."), todo_file);
65 - return 0;
66 -}
67 -
68 -static int transform_todo_file(unsigned flags)
69 -{
70 - const char *todo_file = rebase_path_todo();
71 - struct todo_list todo_list = TODO_LIST_INIT;
72 - int res;
73 -
74 - if (strbuf_read_file(&todo_list.buf, todo_file, 0) < 0)
75 - return error_errno(_("could not read '%s'."), todo_file);
76 -
77 - if (todo_list_parse_insn_buffer(the_repository, todo_list.buf.buf,
78 - &todo_list)) {
79 - todo_list_release(&todo_list);
80 - return error(_("unusable todo list: '%s'"), todo_file);
81 - }
82 -
83 - res = todo_list_write_to_file(the_repository, &todo_list, todo_file,
84 - NULL, NULL, -1, flags);
85 - todo_list_release(&todo_list);
86 -
87 - if (res)
88 - return error_errno(_("could not write '%s'."), todo_file);
89 - return 0;
90 -}
91 -
92 -static int edit_todo_file(unsigned flags)
93 -{
94 - const char *todo_file = rebase_path_todo();
95 - struct todo_list todo_list = TODO_LIST_INIT,
96 - new_todo = TODO_LIST_INIT;
97 - int res = 0;
98 -
99 - if (strbuf_read_file(&todo_list.buf, todo_file, 0) < 0)
100 - return error_errno(_("could not read '%s'."), todo_file);
101 -
102 - strbuf_stripspace(&todo_list.buf, 1);
103 - res = edit_todo_list(the_repository, &todo_list, &new_todo, NULL, NULL, flags);
104 - if (!res && todo_list_write_to_file(the_repository, &new_todo, todo_file,
105 - NULL, NULL, -1, flags & ~(TODO_LIST_SHORTEN_IDS)))
106 - res = error_errno(_("could not write '%s'"), todo_file);
107 -
108 - todo_list_release(&todo_list);
109 - todo_list_release(&new_todo);
110 -
111 - return res;
112 -}
113 -
114 -static int get_revision_ranges(const char *upstream, const char *onto,
115 - const char **head_hash,
116 - char **revisions, char **shortrevisions)
117 -{
118 - const char *base_rev = upstream ? upstream : onto, *shorthead;
119 - struct object_id orig_head;
120 -
121 - if (get_oid("HEAD", &orig_head))
122 - return error(_("no HEAD?"));
123 -
124 - *head_hash = find_unique_abbrev(&orig_head, GIT_MAX_HEXSZ);
125 - *revisions = xstrfmt("%s...%s", base_rev, *head_hash);
126 -
127 - shorthead = find_unique_abbrev(&orig_head, DEFAULT_ABBREV);
128 -
129 - if (upstream) {
130 - const char *shortrev;
131 - struct object_id rev_oid;
132 -
133 - get_oid(base_rev, &rev_oid);
134 - shortrev = find_unique_abbrev(&rev_oid, DEFAULT_ABBREV);
135 -
136 - *shortrevisions = xstrfmt("%s..%s", shortrev, shorthead);
137 - } else
138 - *shortrevisions = xstrdup(shorthead);
139 -
140 - return 0;
141 -}
142 -
143 -static int init_basic_state(struct replay_opts *opts, const char *head_name,
144 - const char *onto, const char *orig_head)
145 -{
146 - FILE *interactive;
147 -
148 - if (!is_directory(path_state_dir()) && mkdir_in_gitdir(path_state_dir()))
149 - return error_errno(_("could not create temporary %s"), path_state_dir());
150 -
151 - delete_reflog("REBASE_HEAD");
152 -
153 - interactive = fopen(path_interactive(), "w");
154 - if (!interactive)
155 - return error_errno(_("could not mark as interactive"));
156 - fclose(interactive);
157 -
158 - return write_basic_state(opts, head_name, onto, orig_head);
159 -}
160 -
161 -static int do_interactive_rebase(struct replay_opts *opts, unsigned flags,
162 - const char *switch_to, const char *upstream,
163 - const char *onto, const char *onto_name,
164 - const char *squash_onto, const char *head_name,
165 - const char *restrict_revision, char *raw_strategies,
166 - struct string_list *commands, unsigned autosquash)
167 -{
168 - int ret;
169 - const char *head_hash = NULL;
170 - char *revisions = NULL, *shortrevisions = NULL;
171 - struct argv_array make_script_args = ARGV_ARRAY_INIT;
172 - struct todo_list todo_list = TODO_LIST_INIT;
173 -
174 - if (prepare_branch_to_be_rebased(the_repository, opts, switch_to))
175 - return -1;
176 -
177 - if (get_revision_ranges(upstream, onto, &head_hash,
178 - &revisions, &shortrevisions))
179 - return -1;
180 -
181 - if (raw_strategies)
182 - parse_strategy_opts(opts, raw_strategies);
183 -
184 - if (init_basic_state(opts, head_name, onto, head_hash)) {
185 - free(revisions);
186 - free(shortrevisions);
187 -
188 - return -1;
189 - }
190 -
191 - if (!upstream && squash_onto)
192 - write_file(path_squash_onto(), "%s\n", squash_onto);
193 -
194 - argv_array_pushl(&make_script_args, "", revisions, NULL);
195 - if (restrict_revision)
196 - argv_array_push(&make_script_args, restrict_revision);
197 -
198 - ret = sequencer_make_script(the_repository, &todo_list.buf,
199 - make_script_args.argc, make_script_args.argv,
200 - flags);
201 -
202 - if (ret)
203 - error(_("could not generate todo list"));
204 - else {
205 - discard_cache();
206 - if (todo_list_parse_insn_buffer(the_repository, todo_list.buf.buf,
207 - &todo_list))
208 - BUG("unusable todo list");
209 -
210 - ret = complete_action(the_repository, opts, flags, shortrevisions, onto_name,
211 - onto, head_hash, commands, autosquash, &todo_list);
212 - }
213 -
214 - free(revisions);
215 - free(shortrevisions);
216 - todo_list_release(&todo_list);
217 - argv_array_clear(&make_script_args);
218 -
219 - return ret;
220 -}
221 -
222 -static const char * const builtin_rebase_interactive_usage[] = {
223 - N_("git rebase--interactive [<options>]"),
224 - NULL
225 -};
226 -
227 -int cmd_rebase__interactive(int argc, const char **argv, const char *prefix)
228 -{
229 - struct replay_opts opts = REPLAY_OPTS_INIT;
230 - unsigned flags = 0, keep_empty = 0, rebase_merges = 0, autosquash = 0;
231 - int abbreviate_commands = 0, rebase_cousins = -1, ret = 0;
232 - const char *onto = NULL, *onto_name = NULL, *restrict_revision = NULL,
233 - *squash_onto = NULL, *upstream = NULL, *head_name = NULL,
234 - *switch_to = NULL, *cmd = NULL;
235 - struct string_list commands = STRING_LIST_INIT_DUP;
236 - char *raw_strategies = NULL;
237 - enum {
238 - NONE = 0, CONTINUE, SKIP, EDIT_TODO, SHOW_CURRENT_PATCH,
239 - SHORTEN_OIDS, EXPAND_OIDS, CHECK_TODO_LIST, REARRANGE_SQUASH, ADD_EXEC
240 - } command = 0;
241 - struct option options[] = {
242 - OPT_BOOL(0, "ff", &opts.allow_ff, N_("allow fast-forward")),
243 - OPT_BOOL(0, "keep-empty", &keep_empty, N_("keep empty commits")),
244 - OPT_BOOL(0, "allow-empty-message", &opts.allow_empty_message,
245 - N_("allow commits with empty messages")),
246 - OPT_BOOL(0, "rebase-merges", &rebase_merges, N_("rebase merge commits")),
247 - OPT_BOOL(0, "rebase-cousins", &rebase_cousins,
248 - N_("keep original branch points of cousins")),
249 - OPT_BOOL(0, "autosquash", &autosquash,
250 - N_("move commits that begin with squash!/fixup!")),
251 - OPT_BOOL(0, "signoff", &opts.signoff, N_("sign commits")),
252 - OPT__VERBOSE(&opts.verbose, N_("be verbose")),
253 - OPT_CMDMODE(0, "continue", &command, N_("continue rebase"),
254 - CONTINUE),
255 - OPT_CMDMODE(0, "skip", &command, N_("skip commit"), SKIP),
256 - OPT_CMDMODE(0, "edit-todo", &command, N_("edit the todo list"),
257 - EDIT_TODO),
258 - OPT_CMDMODE(0, "show-current-patch", &command, N_("show the current patch"),
259 - SHOW_CURRENT_PATCH),
260 - OPT_CMDMODE(0, "shorten-ids", &command,
261 - N_("shorten commit ids in the todo list"), SHORTEN_OIDS),
262 - OPT_CMDMODE(0, "expand-ids", &command,
263 - N_("expand commit ids in the todo list"), EXPAND_OIDS),
264 - OPT_CMDMODE(0, "check-todo-list", &command,
265 - N_("check the todo list"), CHECK_TODO_LIST),
266 - OPT_CMDMODE(0, "rearrange-squash", &command,
267 - N_("rearrange fixup/squash lines"), REARRANGE_SQUASH),
268 - OPT_CMDMODE(0, "add-exec-commands", &command,
269 - N_("insert exec commands in todo list"), ADD_EXEC),
270 - OPT_STRING(0, "onto", &onto, N_("onto"), N_("onto")),
271 - OPT_STRING(0, "restrict-revision", &restrict_revision,
272 - N_("restrict-revision"), N_("restrict revision")),
273 - OPT_STRING(0, "squash-onto", &squash_onto, N_("squash-onto"),
274 - N_("squash onto")),
275 - OPT_STRING(0, "upstream", &upstream, N_("upstream"),
276 - N_("the upstream commit")),
277 - OPT_STRING(0, "head-name", &head_name, N_("head-name"), N_("head name")),
278 - { OPTION_STRING, 'S', "gpg-sign", &opts.gpg_sign, N_("key-id"),
279 - N_("GPG-sign commits"),
280 - PARSE_OPT_OPTARG, NULL, (intptr_t) "" },
281 - OPT_STRING(0, "strategy", &opts.strategy, N_("strategy"),
282 - N_("rebase strategy")),
283 - OPT_STRING(0, "strategy-opts", &raw_strategies, N_("strategy-opts"),
284 - N_("strategy options")),
285 - OPT_STRING(0, "switch-to", &switch_to, N_("switch-to"),
286 - N_("the branch or commit to checkout")),
287 - OPT_STRING(0, "onto-name", &onto_name, N_("onto-name"), N_("onto name")),
288 - OPT_STRING(0, "cmd", &cmd, N_("cmd"), N_("the command to run")),
289 - OPT_RERERE_AUTOUPDATE(&opts.allow_rerere_auto),
290 - OPT_BOOL(0, "reschedule-failed-exec", &opts.reschedule_failed_exec,
291 - N_("automatically re-schedule any `exec` that fails")),
292 - OPT_END()
293 - };
294 -
295 - sequencer_init_config(&opts);
296 - git_config_get_bool("rebase.abbreviatecommands", &abbreviate_commands);
297 -
298 - opts.action = REPLAY_INTERACTIVE_REBASE;
299 - opts.allow_ff = 1;
300 - opts.allow_empty = 1;
301 -
302 - if (argc == 1)
303 - usage_with_options(builtin_rebase_interactive_usage, options);
304 -
305 - argc = parse_options(argc, argv, NULL, options,
306 - builtin_rebase_interactive_usage, PARSE_OPT_KEEP_ARGV0);
307 -
308 - opts.gpg_sign = xstrdup_or_null(opts.gpg_sign);
309 -
310 - flags |= keep_empty ? TODO_LIST_KEEP_EMPTY : 0;
311 - flags |= abbreviate_commands ? TODO_LIST_ABBREVIATE_CMDS : 0;
312 - flags |= rebase_merges ? TODO_LIST_REBASE_MERGES : 0;
313 - flags |= rebase_cousins > 0 ? TODO_LIST_REBASE_COUSINS : 0;
314 - flags |= command == SHORTEN_OIDS ? TODO_LIST_SHORTEN_IDS : 0;
315 -
316 - if (rebase_cousins >= 0 && !rebase_merges)
317 - warning(_("--[no-]rebase-cousins has no effect without "
318 - "--rebase-merges"));
319 -
320 - if (cmd && *cmd) {
321 - string_list_split(&commands, cmd, '\n', -1);
322 -
323 - /* rebase.c adds a new line to cmd after every command,
324 - * so here the last command is always empty */
325 - string_list_remove_empty_items(&commands, 0);
326 - }
327 -
328 - switch (command) {
329 - case NONE:
330 - if (!onto && !upstream)
331 - die(_("a base commit must be provided with --upstream or --onto"));
332 -
333 - ret = do_interactive_rebase(&opts, flags, switch_to, upstream, onto,
334 - onto_name, squash_onto, head_name, restrict_revision,
335 - raw_strategies, &commands, autosquash);
336 - break;
337 - case SKIP: {
338 - struct string_list merge_rr = STRING_LIST_INIT_DUP;
339 -
340 - rerere_clear(the_repository, &merge_rr);
341 - /* fallthrough */
342 - case CONTINUE:
343 - ret = sequencer_continue(the_repository, &opts);
344 - break;
345 - }
346 - case EDIT_TODO:
347 - ret = edit_todo_file(flags);
348 - break;
349 - case SHOW_CURRENT_PATCH: {
350 - struct child_process cmd = CHILD_PROCESS_INIT;
351 -
352 - cmd.git_cmd = 1;
353 - argv_array_pushl(&cmd.args, "show", "REBASE_HEAD", "--", NULL);
354 - ret = run_command(&cmd);
355 -
356 - break;
357 - }
358 - case SHORTEN_OIDS:
359 - case EXPAND_OIDS:
360 - ret = transform_todo_file(flags);
361 - break;
362 - case CHECK_TODO_LIST:
363 - ret = check_todo_list_from_file(the_repository);
364 - break;
365 - case REARRANGE_SQUASH:
366 - ret = rearrange_squash_in_todo_file();
367 - break;
368 - case ADD_EXEC:
369 - ret = add_exec_commands(&commands);
370 - break;
371 - default:
372 - BUG("invalid command '%d'", command);
373 - }
374 -
375 - string_list_clear(&commands, 0);
376 - return !!ret;
377 -}
builtin/rebase.c
+367
@@ -25,6 +25,8 @@
25 #include "commit-reach.h"
26 #include "rerere.h"
27 #include "branch.h"
28 +#include "sequencer.h"
29 +#include "rebase-interactive.h"
30
31 static char const * const builtin_rebase_usage[] = {
32 N_("git rebase [-i] [options] [--exec <cmd>] [--onto <newbase>] "
@@ -35,6 +37,9 @@ static char const * const builtin_rebase_usage[] = {
37 NULL
38 };
39
40 +static GIT_PATH_FUNC(path_state_dir, "rebase-merge/")
41 +static GIT_PATH_FUNC(path_squash_onto, "rebase-merge/squash-onto")
42 +static GIT_PATH_FUNC(path_interactive, "rebase-merge/interactive")
43 static GIT_PATH_FUNC(apply_dir, "rebase-apply")
44 static GIT_PATH_FUNC(merge_dir, "rebase-merge")
45
@@ -46,6 +51,368 @@ enum rebase_type {
51 REBASE_PRESERVE_MERGES
52 };
53
54 +static int add_exec_commands(struct string_list *commands)
55 +{
56 + const char *todo_file = rebase_path_todo();
57 + struct todo_list todo_list = TODO_LIST_INIT;
58 + int res;
59 +
60 + if (strbuf_read_file(&todo_list.buf, todo_file, 0) < 0)
61 + return error_errno(_("could not read '%s'."), todo_file);
62 +
63 + if (todo_list_parse_insn_buffer(the_repository, todo_list.buf.buf,
64 + &todo_list)) {
65 + todo_list_release(&todo_list);
66 + return error(_("unusable todo list: '%s'"), todo_file);
67 + }
68 +
69 + todo_list_add_exec_commands(&todo_list, commands);
70 + res = todo_list_write_to_file(the_repository, &todo_list,
71 + todo_file, NULL, NULL, -1, 0);
72 + todo_list_release(&todo_list);
73 +
74 + if (res)
75 + return error_errno(_("could not write '%s'."), todo_file);
76 + return 0;
77 +}
78 +
79 +static int rearrange_squash_in_todo_file(void)
80 +{
81 + const char *todo_file = rebase_path_todo();
82 + struct todo_list todo_list = TODO_LIST_INIT;
83 + int res = 0;
84 +
85 + if (strbuf_read_file(&todo_list.buf, todo_file, 0) < 0)
86 + return error_errno(_("could not read '%s'."), todo_file);
87 + if (todo_list_parse_insn_buffer(the_repository, todo_list.buf.buf,
88 + &todo_list)) {
89 + todo_list_release(&todo_list);
90 + return error(_("unusable todo list: '%s'"), todo_file);
91 + }
92 +
93 + res = todo_list_rearrange_squash(&todo_list);
94 + if (!res)
95 + res = todo_list_write_to_file(the_repository, &todo_list,
96 + todo_file, NULL, NULL, -1, 0);
97 +
98 + todo_list_release(&todo_list);
99 +
100 + if (res)
101 + return error_errno(_("could not write '%s'."), todo_file);
102 + return 0;
103 +}
104 +
105 +static int transform_todo_file(unsigned flags)
106 +{
107 + const char *todo_file = rebase_path_todo();
108 + struct todo_list todo_list = TODO_LIST_INIT;
109 + int res;
110 +
111 + if (strbuf_read_file(&todo_list.buf, todo_file, 0) < 0)
112 + return error_errno(_("could not read '%s'."), todo_file);
113 +
114 + if (todo_list_parse_insn_buffer(the_repository, todo_list.buf.buf,
115 + &todo_list)) {
116 + todo_list_release(&todo_list);
117 + return error(_("unusable todo list: '%s'"), todo_file);
118 + }
119 +
120 + res = todo_list_write_to_file(the_repository, &todo_list, todo_file,
121 + NULL, NULL, -1, flags);
122 + todo_list_release(&todo_list);
123 +
124 + if (res)
125 + return error_errno(_("could not write '%s'."), todo_file);
126 + return 0;
127 +}
128 +
129 +static int edit_todo_file(unsigned flags)
130 +{
131 + const char *todo_file = rebase_path_todo();
132 + struct todo_list todo_list = TODO_LIST_INIT,
133 + new_todo = TODO_LIST_INIT;
134 + int res = 0;
135 +
136 + if (strbuf_read_file(&todo_list.buf, todo_file, 0) < 0)
137 + return error_errno(_("could not read '%s'."), todo_file);
138 +
139 + strbuf_stripspace(&todo_list.buf, 1);
140 + res = edit_todo_list(the_repository, &todo_list, &new_todo, NULL, NULL, flags);
141 + if (!res && todo_list_write_to_file(the_repository, &new_todo, todo_file,
142 + NULL, NULL, -1, flags & ~(TODO_LIST_SHORTEN_IDS)))
143 + res = error_errno(_("could not write '%s'"), todo_file);
144 +
145 + todo_list_release(&todo_list);
146 + todo_list_release(&new_todo);
147 +
148 + return res;
149 +}
150 +
151 +static int get_revision_ranges(const char *upstream, const char *onto,
152 + const char **head_hash,
153 + char **revisions, char **shortrevisions)
154 +{
155 + const char *base_rev = upstream ? upstream : onto, *shorthead;
156 + struct object_id orig_head;
157 +
158 + if (get_oid("HEAD", &orig_head))
159 + return error(_("no HEAD?"));
160 +
161 + *head_hash = find_unique_abbrev(&orig_head, GIT_MAX_HEXSZ);
162 + *revisions = xstrfmt("%s...%s", base_rev, *head_hash);
163 +
164 + shorthead = find_unique_abbrev(&orig_head, DEFAULT_ABBREV);
165 +
166 + if (upstream) {
167 + const char *shortrev;
168 + struct object_id rev_oid;
169 +
170 + get_oid(base_rev, &rev_oid);
171 + shortrev = find_unique_abbrev(&rev_oid, DEFAULT_ABBREV);
172 +
173 + *shortrevisions = xstrfmt("%s..%s", shortrev, shorthead);
174 + } else
175 + *shortrevisions = xstrdup(shorthead);
176 +
177 + return 0;
178 +}
179 +
180 +static int init_basic_state(struct replay_opts *opts, const char *head_name,
181 + const char *onto, const char *orig_head)
182 +{
183 + FILE *interactive;
184 +
185 + if (!is_directory(path_state_dir()) && mkdir_in_gitdir(path_state_dir()))
186 + return error_errno(_("could not create temporary %s"), path_state_dir());
187 +
188 + delete_reflog("REBASE_HEAD");
189 +
190 + interactive = fopen(path_interactive(), "w");
191 + if (!interactive)
192 + return error_errno(_("could not mark as interactive"));
193 + fclose(interactive);
194 +
195 + return write_basic_state(opts, head_name, onto, orig_head);
196 +}
197 +
198 +static int do_interactive_rebase(struct replay_opts *opts, unsigned flags,
199 + const char *switch_to, const char *upstream,
200 + const char *onto, const char *onto_name,
201 + const char *squash_onto, const char *head_name,
202 + const char *restrict_revision, char *raw_strategies,
203 + struct string_list *commands, unsigned autosquash)
204 +{
205 + int ret;
206 + const char *head_hash = NULL;
207 + char *revisions = NULL, *shortrevisions = NULL;
208 + struct argv_array make_script_args = ARGV_ARRAY_INIT;
209 + struct todo_list todo_list = TODO_LIST_INIT;
210 +
211 + if (prepare_branch_to_be_rebased(the_repository, opts, switch_to))
212 + return -1;
213 +
214 + if (get_revision_ranges(upstream, onto, &head_hash,
215 + &revisions, &shortrevisions))
216 + return -1;
217 +
218 + if (raw_strategies)
219 + parse_strategy_opts(opts, raw_strategies);
220 +
221 + if (init_basic_state(opts, head_name, onto, head_hash)) {
222 + free(revisions);
223 + free(shortrevisions);
224 +
225 + return -1;
226 + }
227 +
228 + if (!upstream && squash_onto)
229 + write_file(path_squash_onto(), "%s\n", squash_onto);
230 +
231 + argv_array_pushl(&make_script_args, "", revisions, NULL);
232 + if (restrict_revision)
233 + argv_array_push(&make_script_args, restrict_revision);
234 +
235 + ret = sequencer_make_script(the_repository, &todo_list.buf,
236 + make_script_args.argc, make_script_args.argv,
237 + flags);
238 +
239 + if (ret)
240 + error(_("could not generate todo list"));
241 + else {
242 + discard_cache();
243 + if (todo_list_parse_insn_buffer(the_repository, todo_list.buf.buf,
244 + &todo_list))
245 + BUG("unusable todo list");
246 +
247 + ret = complete_action(the_repository, opts, flags, shortrevisions, onto_name,
248 + onto, head_hash, commands, autosquash, &todo_list);
249 + }
250 +
251 + free(revisions);
252 + free(shortrevisions);
253 + todo_list_release(&todo_list);
254 + argv_array_clear(&make_script_args);
255 +
256 + return ret;
257 +}
258 +
259 +static const char * const builtin_rebase_interactive_usage[] = {
260 + N_("git rebase--interactive [<options>]"),
261 + NULL
262 +};
263 +
264 +int cmd_rebase__interactive(int argc, const char **argv, const char *prefix)
265 +{
266 + struct replay_opts opts = REPLAY_OPTS_INIT;
267 + unsigned flags = 0, keep_empty = 0, rebase_merges = 0, autosquash = 0;
268 + int abbreviate_commands = 0, rebase_cousins = -1, ret = 0;
269 + const char *onto = NULL, *onto_name = NULL, *restrict_revision = NULL,
270 + *squash_onto = NULL, *upstream = NULL, *head_name = NULL,
271 + *switch_to = NULL, *cmd = NULL;
272 + struct string_list commands = STRING_LIST_INIT_DUP;
273 + char *raw_strategies = NULL;
274 + enum {
275 + NONE = 0, CONTINUE, SKIP, EDIT_TODO, SHOW_CURRENT_PATCH,
276 + SHORTEN_OIDS, EXPAND_OIDS, CHECK_TODO_LIST, REARRANGE_SQUASH, ADD_EXEC
277 + } command = 0;
278 + struct option options[] = {
279 + OPT_BOOL(0, "ff", &opts.allow_ff, N_("allow fast-forward")),
280 + OPT_BOOL(0, "keep-empty", &keep_empty, N_("keep empty commits")),
281 + OPT_BOOL(0, "allow-empty-message", &opts.allow_empty_message,
282 + N_("allow commits with empty messages")),
283 + OPT_BOOL(0, "rebase-merges", &rebase_merges, N_("rebase merge commits")),
284 + OPT_BOOL(0, "rebase-cousins", &rebase_cousins,
285 + N_("keep original branch points of cousins")),
286 + OPT_BOOL(0, "autosquash", &autosquash,
287 + N_("move commits that begin with squash!/fixup!")),
288 + OPT_BOOL(0, "signoff", &opts.signoff, N_("sign commits")),
289 + OPT__VERBOSE(&opts.verbose, N_("be verbose")),
290 + OPT_CMDMODE(0, "continue", &command, N_("continue rebase"),
291 + CONTINUE),
292 + OPT_CMDMODE(0, "skip", &command, N_("skip commit"), SKIP),
293 + OPT_CMDMODE(0, "edit-todo", &command, N_("edit the todo list"),
294 + EDIT_TODO),
295 + OPT_CMDMODE(0, "show-current-patch", &command, N_("show the current patch"),
296 + SHOW_CURRENT_PATCH),
297 + OPT_CMDMODE(0, "shorten-ids", &command,
298 + N_("shorten commit ids in the todo list"), SHORTEN_OIDS),
299 + OPT_CMDMODE(0, "expand-ids", &command,
300 + N_("expand commit ids in the todo list"), EXPAND_OIDS),
301 + OPT_CMDMODE(0, "check-todo-list", &command,
302 + N_("check the todo list"), CHECK_TODO_LIST),
303 + OPT_CMDMODE(0, "rearrange-squash", &command,
304 + N_("rearrange fixup/squash lines"), REARRANGE_SQUASH),
305 + OPT_CMDMODE(0, "add-exec-commands", &command,
306 + N_("insert exec commands in todo list"), ADD_EXEC),
307 + OPT_STRING(0, "onto", &onto, N_("onto"), N_("onto")),
308 + OPT_STRING(0, "restrict-revision", &restrict_revision,
309 + N_("restrict-revision"), N_("restrict revision")),
310 + OPT_STRING(0, "squash-onto", &squash_onto, N_("squash-onto"),
311 + N_("squash onto")),
312 + OPT_STRING(0, "upstream", &upstream, N_("upstream"),
313 + N_("the upstream commit")),
314 + OPT_STRING(0, "head-name", &head_name, N_("head-name"), N_("head name")),
315 + { OPTION_STRING, 'S', "gpg-sign", &opts.gpg_sign, N_("key-id"),
316 + N_("GPG-sign commits"),
317 + PARSE_OPT_OPTARG, NULL, (intptr_t) "" },
318 + OPT_STRING(0, "strategy", &opts.strategy, N_("strategy"),
319 + N_("rebase strategy")),
320 + OPT_STRING(0, "strategy-opts", &raw_strategies, N_("strategy-opts"),
321 + N_("strategy options")),
322 + OPT_STRING(0, "switch-to", &switch_to, N_("switch-to"),
323 + N_("the branch or commit to checkout")),
324 + OPT_STRING(0, "onto-name", &onto_name, N_("onto-name"), N_("onto name")),
325 + OPT_STRING(0, "cmd", &cmd, N_("cmd"), N_("the command to run")),
326 + OPT_RERERE_AUTOUPDATE(&opts.allow_rerere_auto),
327 + OPT_BOOL(0, "reschedule-failed-exec", &opts.reschedule_failed_exec,
328 + N_("automatically re-schedule any `exec` that fails")),
329 + OPT_END()
330 + };
331 +
332 + sequencer_init_config(&opts);
333 + git_config_get_bool("rebase.abbreviatecommands", &abbreviate_commands);
334 +
335 + opts.action = REPLAY_INTERACTIVE_REBASE;
336 + opts.allow_ff = 1;
337 + opts.allow_empty = 1;
338 +
339 + if (argc == 1)
340 + usage_with_options(builtin_rebase_interactive_usage, options);
341 +
342 + argc = parse_options(argc, argv, NULL, options,
343 + builtin_rebase_interactive_usage, PARSE_OPT_KEEP_ARGV0);
344 +
345 + opts.gpg_sign = xstrdup_or_null(opts.gpg_sign);
346 +
347 + flags |= keep_empty ? TODO_LIST_KEEP_EMPTY : 0;
348 + flags |= abbreviate_commands ? TODO_LIST_ABBREVIATE_CMDS : 0;
349 + flags |= rebase_merges ? TODO_LIST_REBASE_MERGES : 0;
350 + flags |= rebase_cousins > 0 ? TODO_LIST_REBASE_COUSINS : 0;
351 + flags |= command == SHORTEN_OIDS ? TODO_LIST_SHORTEN_IDS : 0;
352 +
353 + if (rebase_cousins >= 0 && !rebase_merges)
354 + warning(_("--[no-]rebase-cousins has no effect without "
355 + "--rebase-merges"));
356 +
357 + if (cmd && *cmd) {
358 + string_list_split(&commands, cmd, '\n', -1);
359 +
360 + /* rebase.c adds a new line to cmd after every command,
361 + * so here the last command is always empty */
362 + string_list_remove_empty_items(&commands, 0);
363 + }
364 +
365 + switch (command) {
366 + case NONE:
367 + if (!onto && !upstream)
368 + die(_("a base commit must be provided with --upstream or --onto"));
369 +
370 + ret = do_interactive_rebase(&opts, flags, switch_to, upstream, onto,
371 + onto_name, squash_onto, head_name, restrict_revision,
372 + raw_strategies, &commands, autosquash);
373 + break;
374 + case SKIP: {
375 + struct string_list merge_rr = STRING_LIST_INIT_DUP;
376 +
377 + rerere_clear(the_repository, &merge_rr);
378 + /* fallthrough */
379 + case CONTINUE:
380 + ret = sequencer_continue(the_repository, &opts);
381 + break;
382 + }
383 + case EDIT_TODO:
384 + ret = edit_todo_file(flags);
385 + break;
386 + case SHOW_CURRENT_PATCH: {
387 + struct child_process cmd = CHILD_PROCESS_INIT;
388 +
389 + cmd.git_cmd = 1;
390 + argv_array_pushl(&cmd.args, "show", "REBASE_HEAD", "--", NULL);
391 + ret = run_command(&cmd);
392 +
393 + break;
394 + }
395 + case SHORTEN_OIDS:
396 + case EXPAND_OIDS:
397 + ret = transform_todo_file(flags);
398 + break;
399 + case CHECK_TODO_LIST:
400 + ret = check_todo_list_from_file(the_repository);
401 + break;
402 + case REARRANGE_SQUASH:
403 + ret = rearrange_squash_in_todo_file();
404 + break;
405 + case ADD_EXEC:
406 + ret = add_exec_commands(&commands);
407 + break;
408 + default:
409 + BUG("invalid command '%d'", command);
410 + }
411 +
412 + string_list_clear(&commands, 0);
413 + return !!ret;
414 +}
415 +
416 static int use_builtin_rebase(void)
417 {
418 struct child_process cp = CHILD_PROCESS_INIT;