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;