9
#include "exec-cmd.h"
10
#include "argv-array.h"
11
#include "dir.h"
12
+#include "packfile.h"
13
+#include "refs.h"
14
+#include "quote.h"
15
+#include "config.h"
16
+#include "cache-tree.h"
17
+#include "unpack-trees.h"
18
+#include "lockfile.h"
19
+
20
+static GIT_PATH_FUNC(apply_dir, "rebase-apply")
21
+static GIT_PATH_FUNC(merge_dir, "rebase-merge")
22
+
23
+enum rebase_type {
24
+ REBASE_UNSPECIFIED = -1,
25
+ REBASE_AM,
26
+ REBASE_MERGE,
27
+ REBASE_INTERACTIVE,
28
+ REBASE_PRESERVE_MERGES
29
+};
30
31
static int use_builtin_rebase(void)
32
{
48
return ret;
49
}
50
51
+static int apply_autostash(void)
52
+{
53
+ warning("TODO");
54
+ return 0;
55
+}
56
+
57
+struct rebase_options {
58
+ enum rebase_type type;
59
+ const char *state_dir;
60
+ struct commit *upstream;
61
+ const char *upstream_name;
62
+ char *head_name;
63
+ struct object_id orig_head;
64
+ struct commit *onto;
65
+ const char *onto_name;
66
+ const char *revisions;
67
+ int root;
68
+ struct commit *restrict_revision;
69
+ int dont_finish_rebase;
70
+};
71
+
72
+/* Returns the filename prefixed by the state_dir */
73
+static const char *state_dir_path(const char *filename, struct rebase_options *opts)
74
+{
75
+ static struct strbuf path = STRBUF_INIT;
76
+ static size_t prefix_len;
77
+
78
+ if (!prefix_len) {
79
+ strbuf_addf(&path, "%s/", opts->state_dir);
80
+ prefix_len = path.len;
81
+ }
82
+
83
+ strbuf_setlen(&path, prefix_len);
84
+ strbuf_addstr(&path, filename);
85
+ return path.buf;
86
+}
87
+
88
+static int finish_rebase(struct rebase_options *opts)
89
+{
90
+ struct strbuf dir = STRBUF_INIT;
91
+ const char *argv_gc_auto[] = { "gc", "--auto", NULL };
92
+
93
+ delete_ref(NULL, "REBASE_HEAD", NULL, REF_NO_DEREF);
94
+ apply_autostash();
95
+ close_all_packs(the_repository->objects);
96
+ /*
97
+ * We ignore errors in 'gc --auto', since the
98
+ * user should see them.
99
+ */
100
+ run_command_v_opt(argv_gc_auto, RUN_GIT_CMD);
101
+ strbuf_addstr(&dir, opts->state_dir);
102
+ remove_dir_recursively(&dir, 0);
103
+ strbuf_release(&dir);
104
+
105
+ return 0;
106
+}
107
+
108
+static struct commit *peel_committish(const char *name)
109
+{
110
+ struct object *obj;
111
+ struct object_id oid;
112
+
113
+ if (get_oid(name, &oid))
114
+ return NULL;
115
+ obj = parse_object(the_repository, &oid);
116
+ return (struct commit *)peel_to_type(name, 0, obj, OBJ_COMMIT);
117
+}
118
+
119
+static void add_var(struct strbuf *buf, const char *name, const char *value)
120
+{
121
+ if (!value)
122
+ strbuf_addf(buf, "unset %s; ", name);
123
+ else {
124
+ strbuf_addf(buf, "%s=", name);
125
+ sq_quote_buf(buf, value);
126
+ strbuf_addstr(buf, "; ");
127
+ }
128
+}
129
+
130
+static int run_specific_rebase(struct rebase_options *opts)
131
+{
132
+ const char *argv[] = { NULL, NULL };
133
+ struct strbuf script_snippet = STRBUF_INIT;
134
+ int status;
135
+ const char *backend, *backend_func;
136
+
137
+ add_var(&script_snippet, "GIT_DIR", absolute_path(get_git_dir()));
138
+ add_var(&script_snippet, "state_dir", opts->state_dir);
139
+
140
+ add_var(&script_snippet, "upstream_name", opts->upstream_name);
141
+ add_var(&script_snippet, "upstream",
142
+ oid_to_hex(&opts->upstream->object.oid));
143
+ add_var(&script_snippet, "head_name", opts->head_name);
144
+ add_var(&script_snippet, "orig_head", oid_to_hex(&opts->orig_head));
145
+ add_var(&script_snippet, "onto", oid_to_hex(&opts->onto->object.oid));
146
+ add_var(&script_snippet, "onto_name", opts->onto_name);
147
+ add_var(&script_snippet, "revisions", opts->revisions);
148
+ add_var(&script_snippet, "restrict_revision", opts->restrict_revision ?
149
+ oid_to_hex(&opts->restrict_revision->object.oid) : NULL);
150
+
151
+ switch (opts->type) {
152
+ case REBASE_AM:
153
+ backend = "git-rebase--am";
154
+ backend_func = "git_rebase__am";
155
+ break;
156
+ case REBASE_INTERACTIVE:
157
+ backend = "git-rebase--interactive";
158
+ backend_func = "git_rebase__interactive";
159
+ break;
160
+ case REBASE_MERGE:
161
+ backend = "git-rebase--merge";
162
+ backend_func = "git_rebase__merge";
163
+ break;
164
+ case REBASE_PRESERVE_MERGES:
165
+ backend = "git-rebase--preserve-merges";
166
+ backend_func = "git_rebase__preserve_merges";
167
+ break;
168
+ default:
169
+ BUG("Unhandled rebase type %d", opts->type);
170
+ break;
171
+ }
172
+
173
+ strbuf_addf(&script_snippet,
174
+ ". git-sh-setup && . git-rebase--common &&"
175
+ " . %s && %s", backend, backend_func);
176
+ argv[0] = script_snippet.buf;
177
+
178
+ status = run_command_v_opt(argv, RUN_USING_SHELL);
179
+ if (opts->dont_finish_rebase)
180
+ ; /* do nothing */
181
+ else if (status == 0) {
182
+ if (!file_exists(state_dir_path("stopped-sha", opts)))
183
+ finish_rebase(opts);
184
+ } else if (status == 2) {
185
+ struct strbuf dir = STRBUF_INIT;
186
+
187
+ apply_autostash();
188
+ strbuf_addstr(&dir, opts->state_dir);
189
+ remove_dir_recursively(&dir, 0);
190
+ strbuf_release(&dir);
191
+ die("Nothing to do");
192
+ }
193
+
194
+ strbuf_release(&script_snippet);
195
+
196
+ return status ? -1 : 0;
197
+}
198
+
199
+#define GIT_REFLOG_ACTION_ENVIRONMENT "GIT_REFLOG_ACTION"
200
+
201
+static int reset_head(struct object_id *oid, const char *action,
202
+ const char *switch_to_branch, int detach_head)
203
+{
204
+ struct object_id head_oid;
205
+ struct tree_desc desc;
206
+ struct lock_file lock = LOCK_INIT;
207
+ struct unpack_trees_options unpack_tree_opts;
208
+ struct tree *tree;
209
+ const char *reflog_action;
210
+ struct strbuf msg = STRBUF_INIT;
211
+ size_t prefix_len;
212
+ struct object_id *orig = NULL, oid_orig,
213
+ *old_orig = NULL, oid_old_orig;
214
+ int ret = 0;
215
+
216
+ if (hold_locked_index(&lock, LOCK_REPORT_ON_ERROR) < 0)
217
+ return -1;
218
+
219
+ if (!oid) {
220
+ if (get_oid("HEAD", &head_oid)) {
221
+ rollback_lock_file(&lock);
222
+ return error(_("could not determine HEAD revision"));
223
+ }
224
+ oid = &head_oid;
225
+ }
226
+
227
+ memset(&unpack_tree_opts, 0, sizeof(unpack_tree_opts));
228
+ setup_unpack_trees_porcelain(&unpack_tree_opts, action);
229
+ unpack_tree_opts.head_idx = 1;
230
+ unpack_tree_opts.src_index = the_repository->index;
231
+ unpack_tree_opts.dst_index = the_repository->index;
232
+ unpack_tree_opts.fn = oneway_merge;
233
+ unpack_tree_opts.update = 1;
234
+ unpack_tree_opts.merge = 1;
235
+ if (!detach_head)
236
+ unpack_tree_opts.reset = 1;
237
+
238
+ if (read_index_unmerged(the_repository->index) < 0) {
239
+ rollback_lock_file(&lock);
240
+ return error(_("could not read index"));
241
+ }
242
+
243
+ if (!fill_tree_descriptor(&desc, oid)) {
244
+ error(_("failed to find tree of %s"), oid_to_hex(oid));
245
+ rollback_lock_file(&lock);
246
+ free((void *)desc.buffer);
247
+ return -1;
248
+ }
249
+
250
+ if (unpack_trees(1, &desc, &unpack_tree_opts)) {
251
+ rollback_lock_file(&lock);
252
+ free((void *)desc.buffer);
253
+ return -1;
254
+ }
255
+
256
+ tree = parse_tree_indirect(oid);
257
+ prime_cache_tree(the_repository->index, tree);
258
+
259
+ if (write_locked_index(the_repository->index, &lock, COMMIT_LOCK) < 0)
260
+ ret = error(_("could not write index"));
261
+ free((void *)desc.buffer);
262
+
263
+ if (ret)
264
+ return ret;
265
+
266
+ reflog_action = getenv(GIT_REFLOG_ACTION_ENVIRONMENT);
267
+ strbuf_addf(&msg, "%s: ", reflog_action ? reflog_action : "rebase");
268
+ prefix_len = msg.len;
269
+
270
+ if (!get_oid("ORIG_HEAD", &oid_old_orig))
271
+ old_orig = &oid_old_orig;
272
+ if (!get_oid("HEAD", &oid_orig)) {
273
+ orig = &oid_orig;
274
+ strbuf_addstr(&msg, "updating ORIG_HEAD");
275
+ update_ref(msg.buf, "ORIG_HEAD", orig, old_orig, 0,
276
+ UPDATE_REFS_MSG_ON_ERR);
277
+ } else if (old_orig)
278
+ delete_ref(NULL, "ORIG_HEAD", old_orig, 0);
279
+ strbuf_setlen(&msg, prefix_len);
280
+ strbuf_addstr(&msg, "updating HEAD");
281
+ if (!switch_to_branch)
282
+ ret = update_ref(msg.buf, "HEAD", oid, orig, REF_NO_DEREF,
283
+ UPDATE_REFS_MSG_ON_ERR);
284
+ else {
285
+ ret = create_symref("HEAD", switch_to_branch, msg.buf);
286
+ if (!ret)
287
+ ret = update_ref(msg.buf, "HEAD", oid, NULL, 0,
288
+ UPDATE_REFS_MSG_ON_ERR);
289
+ }
290
+
291
+ strbuf_release(&msg);
292
+ return ret;
293
+}
294
+
295
int cmd_rebase(int argc, const char **argv, const char *prefix)
296
{
297
+ struct rebase_options options = {
298
+ .type = REBASE_UNSPECIFIED,
299
+ };
300
+ const char *branch_name;
301
+ int ret, flags;
302
+ struct strbuf msg = STRBUF_INIT;
303
+ struct strbuf revisions = STRBUF_INIT;
304
+
305
/*
306
* NEEDSWORK: Once the builtin rebase has been tested enough
307
* and git-legacy-rebase.sh is retired to contrib/, this preamble
324
trace_repo_setup(prefix);
325
setup_work_tree();
326
57
- die("TODO");
327
+ git_config(git_default_config, NULL);
328
+
329
+ switch (options.type) {
330
+ case REBASE_MERGE:
331
+ case REBASE_INTERACTIVE:
332
+ case REBASE_PRESERVE_MERGES:
333
+ options.state_dir = merge_dir();
334
+ break;
335
+ case REBASE_AM:
336
+ options.state_dir = apply_dir();
337
+ break;
338
+ default:
339
+ /* the default rebase backend is `--am` */
340
+ options.type = REBASE_AM;
341
+ options.state_dir = apply_dir();
342
+ break;
343
+ }
344
+
345
+ if (!options.root) {
346
+ if (argc < 2)
347
+ die("TODO: handle @{upstream}");
348
+ else {
349
+ options.upstream_name = argv[1];
350
+ argc--;
351
+ argv++;
352
+ if (!strcmp(options.upstream_name, "-"))
353
+ options.upstream_name = "@{-1}";
354
+ }
355
+ options.upstream = peel_committish(options.upstream_name);
356
+ if (!options.upstream)
357
+ die(_("invalid upstream '%s'"), options.upstream_name);
358
+ } else
359
+ die("TODO: upstream for --root");
360
+
361
+ /* Make sure the branch to rebase onto is valid. */
362
+ if (!options.onto_name)
363
+ options.onto_name = options.upstream_name;
364
+ if (strstr(options.onto_name, "...")) {
365
+ die("TODO");
366
+ } else {
367
+ options.onto = peel_committish(options.onto_name);
368
+ if (!options.onto)
369
+ die(_("Does not point to a valid commit '%s'"),
370
+ options.onto_name);
371
+ }
372
+
373
+ /*
374
+ * If the branch to rebase is given, that is the branch we will rebase
375
+ * branch_name -- branch/commit being rebased, or
376
+ * HEAD (already detached)
377
+ * orig_head -- commit object name of tip of the branch before rebasing
378
+ * head_name -- refs/heads/<that-branch> or "detached HEAD"
379
+ */
380
+ if (argc > 1)
381
+ die("TODO: handle switch_to");
382
+ else {
383
+ /* Do not need to switch branches, we are already on it. */
384
+ options.head_name =
385
+ xstrdup_or_null(resolve_ref_unsafe("HEAD", 0, NULL,
386
+ &flags));
387
+ if (!options.head_name)
388
+ die(_("No such ref: %s"), "HEAD");
389
+ if (flags & REF_ISSYMREF) {
390
+ if (!skip_prefix(options.head_name,
391
+ "refs/heads/", &branch_name))
392
+ branch_name = options.head_name;
393
+
394
+ } else {
395
+ options.head_name = xstrdup("detached HEAD");
396
+ branch_name = "HEAD";
397
+ }
398
+ if (get_oid("HEAD", &options.orig_head))
399
+ die(_("Could not resolve HEAD to a revision"));
400
+ }
401
+
402
+ strbuf_addf(&msg, "rebase: checkout %s", options.onto_name);
403
+ if (reset_head(&options.onto->object.oid, "checkout", NULL, 1))
404
+ die(_("Could not detach HEAD"));
405
+ strbuf_release(&msg);
406
+
407
+ strbuf_addf(&revisions, "%s..%s",
408
+ options.root ? oid_to_hex(&options.onto->object.oid) :
409
+ (options.restrict_revision ?
410
+ oid_to_hex(&options.restrict_revision->object.oid) :
411
+ oid_to_hex(&options.upstream->object.oid)),
412
+ oid_to_hex(&options.orig_head));
413
+
414
+ options.revisions = revisions.buf;
415
+
416
+ ret = !!run_specific_rebase(&options);
417
+
418
+ strbuf_release(&revisions);
419
+ free(options.head_name);
420
+ return ret;
421
}