Pratik Karki | 55071ea | 2018-08-07 01:16:09 +0545 | [diff] [blame] | 1 | /* |
| 2 | * "git rebase" builtin command |
| 3 | * |
| 4 | * Copyright (c) 2018 Pratik Karki |
| 5 | */ |
| 6 | |
| 7 | #include "builtin.h" |
| 8 | #include "run-command.h" |
| 9 | #include "exec-cmd.h" |
| 10 | #include "argv-array.h" |
| 11 | #include "dir.h" |
Pratik Karki | ac7f467 | 2018-08-07 01:16:11 +0545 | [diff] [blame] | 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" |
Pratik Karki | f28d40d | 2018-09-04 14:27:07 -0700 | [diff] [blame] | 19 | #include "parse-options.h" |
Pratik Karki | 075bc85 | 2018-09-04 14:27:09 -0700 | [diff] [blame] | 20 | #include "commit.h" |
Pratik Karki | bff014d | 2018-09-04 14:27:13 -0700 | [diff] [blame] | 21 | #include "diff.h" |
Pratik Karki | e0333e5 | 2018-09-04 14:27:14 -0700 | [diff] [blame] | 22 | #include "wt-status.h" |
Pratik Karki | 9a48a61 | 2018-09-04 14:27:16 -0700 | [diff] [blame] | 23 | #include "revision.h" |
Pratik Karki | 122420c | 2018-08-08 20:51:17 +0545 | [diff] [blame] | 24 | #include "rerere.h" |
Pratik Karki | f28d40d | 2018-09-04 14:27:07 -0700 | [diff] [blame] | 25 | |
| 26 | static char const * const builtin_rebase_usage[] = { |
| 27 | N_("git rebase [-i] [options] [--exec <cmd>] [--onto <newbase>] " |
| 28 | "[<upstream>] [<branch>]"), |
| 29 | N_("git rebase [-i] [options] [--exec <cmd>] [--onto <newbase>] " |
| 30 | "--root [<branch>]"), |
| 31 | N_("git rebase --continue | --abort | --skip | --edit-todo"), |
| 32 | NULL |
| 33 | }; |
Pratik Karki | ac7f467 | 2018-08-07 01:16:11 +0545 | [diff] [blame] | 34 | |
| 35 | static GIT_PATH_FUNC(apply_dir, "rebase-apply") |
| 36 | static GIT_PATH_FUNC(merge_dir, "rebase-merge") |
| 37 | |
| 38 | enum rebase_type { |
| 39 | REBASE_UNSPECIFIED = -1, |
| 40 | REBASE_AM, |
| 41 | REBASE_MERGE, |
| 42 | REBASE_INTERACTIVE, |
| 43 | REBASE_PRESERVE_MERGES |
| 44 | }; |
Pratik Karki | 55071ea | 2018-08-07 01:16:09 +0545 | [diff] [blame] | 45 | |
| 46 | static int use_builtin_rebase(void) |
| 47 | { |
| 48 | struct child_process cp = CHILD_PROCESS_INIT; |
| 49 | struct strbuf out = STRBUF_INIT; |
| 50 | int ret; |
| 51 | |
| 52 | argv_array_pushl(&cp.args, |
| 53 | "config", "--bool", "rebase.usebuiltin", NULL); |
| 54 | cp.git_cmd = 1; |
| 55 | if (capture_command(&cp, &out, 6)) { |
| 56 | strbuf_release(&out); |
| 57 | return 0; |
| 58 | } |
| 59 | |
| 60 | strbuf_trim(&out); |
| 61 | ret = !strcmp("true", out.buf); |
| 62 | strbuf_release(&out); |
| 63 | return ret; |
| 64 | } |
| 65 | |
Pratik Karki | ac7f467 | 2018-08-07 01:16:11 +0545 | [diff] [blame] | 66 | static int apply_autostash(void) |
| 67 | { |
| 68 | warning("TODO"); |
| 69 | return 0; |
| 70 | } |
| 71 | |
| 72 | struct rebase_options { |
| 73 | enum rebase_type type; |
| 74 | const char *state_dir; |
| 75 | struct commit *upstream; |
| 76 | const char *upstream_name; |
Pratik Karki | 06e4775 | 2018-09-04 14:27:10 -0700 | [diff] [blame] | 77 | const char *upstream_arg; |
Pratik Karki | ac7f467 | 2018-08-07 01:16:11 +0545 | [diff] [blame] | 78 | char *head_name; |
| 79 | struct object_id orig_head; |
| 80 | struct commit *onto; |
| 81 | const char *onto_name; |
| 82 | const char *revisions; |
Pratik Karki | e65123a | 2018-09-04 14:27:21 -0700 | [diff] [blame] | 83 | const char *switch_to; |
Pratik Karki | ac7f467 | 2018-08-07 01:16:11 +0545 | [diff] [blame] | 84 | int root; |
| 85 | struct commit *restrict_revision; |
| 86 | int dont_finish_rebase; |
Pratik Karki | b4c8eb0 | 2018-09-04 14:27:12 -0700 | [diff] [blame] | 87 | enum { |
| 88 | REBASE_NO_QUIET = 1<<0, |
Pratik Karki | bff014d | 2018-09-04 14:27:13 -0700 | [diff] [blame] | 89 | REBASE_VERBOSE = 1<<1, |
| 90 | REBASE_DIFFSTAT = 1<<2, |
Pratik Karki | 1ed9c14 | 2018-09-04 14:27:17 -0700 | [diff] [blame] | 91 | REBASE_FORCE = 1<<3, |
Pratik Karki | c54dacb | 2018-09-04 14:27:18 -0700 | [diff] [blame] | 92 | REBASE_INTERACTIVE_EXPLICIT = 1<<4, |
Pratik Karki | b4c8eb0 | 2018-09-04 14:27:12 -0700 | [diff] [blame] | 93 | } flags; |
| 94 | struct strbuf git_am_opt; |
Pratik Karki | f957362 | 2018-08-08 20:51:16 +0545 | [diff] [blame] | 95 | const char *action; |
Pratik Karki | 73d51ed | 2018-09-04 14:59:50 -0700 | [diff] [blame] | 96 | int signoff; |
Pratik Karki | ead98c1 | 2018-09-04 14:59:52 -0700 | [diff] [blame] | 97 | int allow_rerere_autoupdate; |
Pratik Karki | ac7f467 | 2018-08-07 01:16:11 +0545 | [diff] [blame] | 98 | }; |
| 99 | |
Pratik Karki | 9a48a61 | 2018-09-04 14:27:16 -0700 | [diff] [blame] | 100 | static int is_interactive(struct rebase_options *opts) |
| 101 | { |
| 102 | return opts->type == REBASE_INTERACTIVE || |
| 103 | opts->type == REBASE_PRESERVE_MERGES; |
| 104 | } |
| 105 | |
Pratik Karki | ac7f467 | 2018-08-07 01:16:11 +0545 | [diff] [blame] | 106 | /* Returns the filename prefixed by the state_dir */ |
| 107 | static const char *state_dir_path(const char *filename, struct rebase_options *opts) |
| 108 | { |
| 109 | static struct strbuf path = STRBUF_INIT; |
| 110 | static size_t prefix_len; |
| 111 | |
| 112 | if (!prefix_len) { |
| 113 | strbuf_addf(&path, "%s/", opts->state_dir); |
| 114 | prefix_len = path.len; |
| 115 | } |
| 116 | |
| 117 | strbuf_setlen(&path, prefix_len); |
| 118 | strbuf_addstr(&path, filename); |
| 119 | return path.buf; |
| 120 | } |
| 121 | |
Pratik Karki | f957362 | 2018-08-08 20:51:16 +0545 | [diff] [blame] | 122 | /* Read one file, then strip line endings */ |
| 123 | static int read_one(const char *path, struct strbuf *buf) |
| 124 | { |
| 125 | if (strbuf_read_file(buf, path, 0) < 0) |
| 126 | return error_errno(_("could not read '%s'"), path); |
| 127 | strbuf_trim_trailing_newline(buf); |
| 128 | return 0; |
| 129 | } |
| 130 | |
| 131 | /* Initialize the rebase options from the state directory. */ |
| 132 | static int read_basic_state(struct rebase_options *opts) |
| 133 | { |
| 134 | struct strbuf head_name = STRBUF_INIT; |
| 135 | struct strbuf buf = STRBUF_INIT; |
| 136 | struct object_id oid; |
| 137 | |
| 138 | if (read_one(state_dir_path("head-name", opts), &head_name) || |
| 139 | read_one(state_dir_path("onto", opts), &buf)) |
| 140 | return -1; |
| 141 | opts->head_name = starts_with(head_name.buf, "refs/") ? |
| 142 | xstrdup(head_name.buf) : NULL; |
| 143 | strbuf_release(&head_name); |
| 144 | if (get_oid(buf.buf, &oid)) |
| 145 | return error(_("could not get 'onto': '%s'"), buf.buf); |
| 146 | opts->onto = lookup_commit_or_die(&oid, buf.buf); |
| 147 | |
| 148 | /* |
| 149 | * We always write to orig-head, but interactive rebase used to write to |
| 150 | * head. Fall back to reading from head to cover for the case that the |
| 151 | * user upgraded git with an ongoing interactive rebase. |
| 152 | */ |
| 153 | strbuf_reset(&buf); |
| 154 | if (file_exists(state_dir_path("orig-head", opts))) { |
| 155 | if (read_one(state_dir_path("orig-head", opts), &buf)) |
| 156 | return -1; |
| 157 | } else if (read_one(state_dir_path("head", opts), &buf)) |
| 158 | return -1; |
| 159 | if (get_oid(buf.buf, &opts->orig_head)) |
| 160 | return error(_("invalid orig-head: '%s'"), buf.buf); |
| 161 | |
| 162 | strbuf_reset(&buf); |
| 163 | if (read_one(state_dir_path("quiet", opts), &buf)) |
| 164 | return -1; |
| 165 | if (buf.len) |
| 166 | opts->flags &= ~REBASE_NO_QUIET; |
| 167 | else |
| 168 | opts->flags |= REBASE_NO_QUIET; |
| 169 | |
| 170 | if (file_exists(state_dir_path("verbose", opts))) |
| 171 | opts->flags |= REBASE_VERBOSE; |
| 172 | |
Pratik Karki | 73d51ed | 2018-09-04 14:59:50 -0700 | [diff] [blame] | 173 | if (file_exists(state_dir_path("signoff", opts))) { |
| 174 | opts->signoff = 1; |
| 175 | opts->flags |= REBASE_FORCE; |
| 176 | } |
| 177 | |
Pratik Karki | ead98c1 | 2018-09-04 14:59:52 -0700 | [diff] [blame] | 178 | if (file_exists(state_dir_path("allow_rerere_autoupdate", opts))) { |
| 179 | strbuf_reset(&buf); |
| 180 | if (read_one(state_dir_path("allow_rerere_autoupdate", opts), |
| 181 | &buf)) |
| 182 | return -1; |
| 183 | if (!strcmp(buf.buf, "--rerere-autoupdate")) |
| 184 | opts->allow_rerere_autoupdate = 1; |
| 185 | else if (!strcmp(buf.buf, "--no-rerere-autoupdate")) |
| 186 | opts->allow_rerere_autoupdate = 0; |
| 187 | else |
| 188 | warning(_("ignoring invalid allow_rerere_autoupdate: " |
| 189 | "'%s'"), buf.buf); |
| 190 | } else |
| 191 | opts->allow_rerere_autoupdate = -1; |
| 192 | |
Pratik Karki | f957362 | 2018-08-08 20:51:16 +0545 | [diff] [blame] | 193 | strbuf_release(&buf); |
| 194 | |
| 195 | return 0; |
| 196 | } |
| 197 | |
Pratik Karki | ac7f467 | 2018-08-07 01:16:11 +0545 | [diff] [blame] | 198 | static int finish_rebase(struct rebase_options *opts) |
| 199 | { |
| 200 | struct strbuf dir = STRBUF_INIT; |
| 201 | const char *argv_gc_auto[] = { "gc", "--auto", NULL }; |
| 202 | |
| 203 | delete_ref(NULL, "REBASE_HEAD", NULL, REF_NO_DEREF); |
| 204 | apply_autostash(); |
| 205 | close_all_packs(the_repository->objects); |
| 206 | /* |
| 207 | * We ignore errors in 'gc --auto', since the |
| 208 | * user should see them. |
| 209 | */ |
| 210 | run_command_v_opt(argv_gc_auto, RUN_GIT_CMD); |
| 211 | strbuf_addstr(&dir, opts->state_dir); |
| 212 | remove_dir_recursively(&dir, 0); |
| 213 | strbuf_release(&dir); |
| 214 | |
| 215 | return 0; |
| 216 | } |
| 217 | |
| 218 | static struct commit *peel_committish(const char *name) |
| 219 | { |
| 220 | struct object *obj; |
| 221 | struct object_id oid; |
| 222 | |
| 223 | if (get_oid(name, &oid)) |
| 224 | return NULL; |
| 225 | obj = parse_object(the_repository, &oid); |
| 226 | return (struct commit *)peel_to_type(name, 0, obj, OBJ_COMMIT); |
| 227 | } |
| 228 | |
| 229 | static void add_var(struct strbuf *buf, const char *name, const char *value) |
| 230 | { |
| 231 | if (!value) |
| 232 | strbuf_addf(buf, "unset %s; ", name); |
| 233 | else { |
| 234 | strbuf_addf(buf, "%s=", name); |
| 235 | sq_quote_buf(buf, value); |
| 236 | strbuf_addstr(buf, "; "); |
| 237 | } |
| 238 | } |
| 239 | |
| 240 | static int run_specific_rebase(struct rebase_options *opts) |
| 241 | { |
| 242 | const char *argv[] = { NULL, NULL }; |
| 243 | struct strbuf script_snippet = STRBUF_INIT; |
| 244 | int status; |
| 245 | const char *backend, *backend_func; |
| 246 | |
| 247 | add_var(&script_snippet, "GIT_DIR", absolute_path(get_git_dir())); |
| 248 | add_var(&script_snippet, "state_dir", opts->state_dir); |
| 249 | |
| 250 | add_var(&script_snippet, "upstream_name", opts->upstream_name); |
Pratik Karki | f957362 | 2018-08-08 20:51:16 +0545 | [diff] [blame] | 251 | add_var(&script_snippet, "upstream", opts->upstream ? |
| 252 | oid_to_hex(&opts->upstream->object.oid) : NULL); |
Pratik Karki | d4c569f | 2018-09-04 14:27:20 -0700 | [diff] [blame] | 253 | add_var(&script_snippet, "head_name", |
| 254 | opts->head_name ? opts->head_name : "detached HEAD"); |
Pratik Karki | ac7f467 | 2018-08-07 01:16:11 +0545 | [diff] [blame] | 255 | add_var(&script_snippet, "orig_head", oid_to_hex(&opts->orig_head)); |
Pratik Karki | f957362 | 2018-08-08 20:51:16 +0545 | [diff] [blame] | 256 | add_var(&script_snippet, "onto", opts->onto ? |
| 257 | oid_to_hex(&opts->onto->object.oid) : NULL); |
Pratik Karki | ac7f467 | 2018-08-07 01:16:11 +0545 | [diff] [blame] | 258 | add_var(&script_snippet, "onto_name", opts->onto_name); |
| 259 | add_var(&script_snippet, "revisions", opts->revisions); |
| 260 | add_var(&script_snippet, "restrict_revision", opts->restrict_revision ? |
| 261 | oid_to_hex(&opts->restrict_revision->object.oid) : NULL); |
Pratik Karki | b4c8eb0 | 2018-09-04 14:27:12 -0700 | [diff] [blame] | 262 | add_var(&script_snippet, "GIT_QUIET", |
| 263 | opts->flags & REBASE_NO_QUIET ? "" : "t"); |
| 264 | add_var(&script_snippet, "git_am_opt", opts->git_am_opt.buf); |
Pratik Karki | bff014d | 2018-09-04 14:27:13 -0700 | [diff] [blame] | 265 | add_var(&script_snippet, "verbose", |
| 266 | opts->flags & REBASE_VERBOSE ? "t" : ""); |
| 267 | add_var(&script_snippet, "diffstat", |
| 268 | opts->flags & REBASE_DIFFSTAT ? "t" : ""); |
Pratik Karki | 1ed9c14 | 2018-09-04 14:27:17 -0700 | [diff] [blame] | 269 | add_var(&script_snippet, "force_rebase", |
| 270 | opts->flags & REBASE_FORCE ? "t" : ""); |
Pratik Karki | e65123a | 2018-09-04 14:27:21 -0700 | [diff] [blame] | 271 | if (opts->switch_to) |
| 272 | add_var(&script_snippet, "switch_to", opts->switch_to); |
Pratik Karki | f957362 | 2018-08-08 20:51:16 +0545 | [diff] [blame] | 273 | add_var(&script_snippet, "action", opts->action ? opts->action : ""); |
Pratik Karki | 73d51ed | 2018-09-04 14:59:50 -0700 | [diff] [blame] | 274 | add_var(&script_snippet, "signoff", opts->signoff ? "--signoff" : ""); |
Pratik Karki | ead98c1 | 2018-09-04 14:59:52 -0700 | [diff] [blame] | 275 | add_var(&script_snippet, "allow_rerere_autoupdate", |
| 276 | opts->allow_rerere_autoupdate < 0 ? "" : |
| 277 | opts->allow_rerere_autoupdate ? |
| 278 | "--rerere-autoupdate" : "--no-rerere-autoupdate"); |
Pratik Karki | ac7f467 | 2018-08-07 01:16:11 +0545 | [diff] [blame] | 279 | |
| 280 | switch (opts->type) { |
| 281 | case REBASE_AM: |
| 282 | backend = "git-rebase--am"; |
| 283 | backend_func = "git_rebase__am"; |
| 284 | break; |
| 285 | case REBASE_INTERACTIVE: |
| 286 | backend = "git-rebase--interactive"; |
| 287 | backend_func = "git_rebase__interactive"; |
| 288 | break; |
| 289 | case REBASE_MERGE: |
| 290 | backend = "git-rebase--merge"; |
| 291 | backend_func = "git_rebase__merge"; |
| 292 | break; |
| 293 | case REBASE_PRESERVE_MERGES: |
| 294 | backend = "git-rebase--preserve-merges"; |
| 295 | backend_func = "git_rebase__preserve_merges"; |
| 296 | break; |
| 297 | default: |
| 298 | BUG("Unhandled rebase type %d", opts->type); |
| 299 | break; |
| 300 | } |
| 301 | |
| 302 | strbuf_addf(&script_snippet, |
| 303 | ". git-sh-setup && . git-rebase--common &&" |
| 304 | " . %s && %s", backend, backend_func); |
| 305 | argv[0] = script_snippet.buf; |
| 306 | |
| 307 | status = run_command_v_opt(argv, RUN_USING_SHELL); |
| 308 | if (opts->dont_finish_rebase) |
| 309 | ; /* do nothing */ |
| 310 | else if (status == 0) { |
| 311 | if (!file_exists(state_dir_path("stopped-sha", opts))) |
| 312 | finish_rebase(opts); |
| 313 | } else if (status == 2) { |
| 314 | struct strbuf dir = STRBUF_INIT; |
| 315 | |
| 316 | apply_autostash(); |
| 317 | strbuf_addstr(&dir, opts->state_dir); |
| 318 | remove_dir_recursively(&dir, 0); |
| 319 | strbuf_release(&dir); |
| 320 | die("Nothing to do"); |
| 321 | } |
| 322 | |
| 323 | strbuf_release(&script_snippet); |
| 324 | |
| 325 | return status ? -1 : 0; |
| 326 | } |
| 327 | |
| 328 | #define GIT_REFLOG_ACTION_ENVIRONMENT "GIT_REFLOG_ACTION" |
| 329 | |
| 330 | static int reset_head(struct object_id *oid, const char *action, |
| 331 | const char *switch_to_branch, int detach_head) |
| 332 | { |
| 333 | struct object_id head_oid; |
| 334 | struct tree_desc desc; |
| 335 | struct lock_file lock = LOCK_INIT; |
| 336 | struct unpack_trees_options unpack_tree_opts; |
| 337 | struct tree *tree; |
| 338 | const char *reflog_action; |
| 339 | struct strbuf msg = STRBUF_INIT; |
| 340 | size_t prefix_len; |
| 341 | struct object_id *orig = NULL, oid_orig, |
| 342 | *old_orig = NULL, oid_old_orig; |
| 343 | int ret = 0; |
| 344 | |
Pratik Karki | d4c569f | 2018-09-04 14:27:20 -0700 | [diff] [blame] | 345 | if (switch_to_branch && !starts_with(switch_to_branch, "refs/")) |
| 346 | BUG("Not a fully qualified branch: '%s'", switch_to_branch); |
| 347 | |
Pratik Karki | ac7f467 | 2018-08-07 01:16:11 +0545 | [diff] [blame] | 348 | if (hold_locked_index(&lock, LOCK_REPORT_ON_ERROR) < 0) |
| 349 | return -1; |
| 350 | |
| 351 | if (!oid) { |
| 352 | if (get_oid("HEAD", &head_oid)) { |
| 353 | rollback_lock_file(&lock); |
| 354 | return error(_("could not determine HEAD revision")); |
| 355 | } |
| 356 | oid = &head_oid; |
| 357 | } |
| 358 | |
| 359 | memset(&unpack_tree_opts, 0, sizeof(unpack_tree_opts)); |
| 360 | setup_unpack_trees_porcelain(&unpack_tree_opts, action); |
| 361 | unpack_tree_opts.head_idx = 1; |
| 362 | unpack_tree_opts.src_index = the_repository->index; |
| 363 | unpack_tree_opts.dst_index = the_repository->index; |
| 364 | unpack_tree_opts.fn = oneway_merge; |
| 365 | unpack_tree_opts.update = 1; |
| 366 | unpack_tree_opts.merge = 1; |
| 367 | if (!detach_head) |
| 368 | unpack_tree_opts.reset = 1; |
| 369 | |
| 370 | if (read_index_unmerged(the_repository->index) < 0) { |
| 371 | rollback_lock_file(&lock); |
| 372 | return error(_("could not read index")); |
| 373 | } |
| 374 | |
| 375 | if (!fill_tree_descriptor(&desc, oid)) { |
| 376 | error(_("failed to find tree of %s"), oid_to_hex(oid)); |
| 377 | rollback_lock_file(&lock); |
| 378 | free((void *)desc.buffer); |
| 379 | return -1; |
| 380 | } |
| 381 | |
| 382 | if (unpack_trees(1, &desc, &unpack_tree_opts)) { |
| 383 | rollback_lock_file(&lock); |
| 384 | free((void *)desc.buffer); |
| 385 | return -1; |
| 386 | } |
| 387 | |
| 388 | tree = parse_tree_indirect(oid); |
| 389 | prime_cache_tree(the_repository->index, tree); |
| 390 | |
| 391 | if (write_locked_index(the_repository->index, &lock, COMMIT_LOCK) < 0) |
| 392 | ret = error(_("could not write index")); |
| 393 | free((void *)desc.buffer); |
| 394 | |
| 395 | if (ret) |
| 396 | return ret; |
| 397 | |
| 398 | reflog_action = getenv(GIT_REFLOG_ACTION_ENVIRONMENT); |
| 399 | strbuf_addf(&msg, "%s: ", reflog_action ? reflog_action : "rebase"); |
| 400 | prefix_len = msg.len; |
| 401 | |
| 402 | if (!get_oid("ORIG_HEAD", &oid_old_orig)) |
| 403 | old_orig = &oid_old_orig; |
| 404 | if (!get_oid("HEAD", &oid_orig)) { |
| 405 | orig = &oid_orig; |
| 406 | strbuf_addstr(&msg, "updating ORIG_HEAD"); |
| 407 | update_ref(msg.buf, "ORIG_HEAD", orig, old_orig, 0, |
| 408 | UPDATE_REFS_MSG_ON_ERR); |
| 409 | } else if (old_orig) |
| 410 | delete_ref(NULL, "ORIG_HEAD", old_orig, 0); |
| 411 | strbuf_setlen(&msg, prefix_len); |
| 412 | strbuf_addstr(&msg, "updating HEAD"); |
| 413 | if (!switch_to_branch) |
| 414 | ret = update_ref(msg.buf, "HEAD", oid, orig, REF_NO_DEREF, |
| 415 | UPDATE_REFS_MSG_ON_ERR); |
| 416 | else { |
| 417 | ret = create_symref("HEAD", switch_to_branch, msg.buf); |
| 418 | if (!ret) |
| 419 | ret = update_ref(msg.buf, "HEAD", oid, NULL, 0, |
| 420 | UPDATE_REFS_MSG_ON_ERR); |
| 421 | } |
| 422 | |
| 423 | strbuf_release(&msg); |
| 424 | return ret; |
| 425 | } |
| 426 | |
Pratik Karki | bff014d | 2018-09-04 14:27:13 -0700 | [diff] [blame] | 427 | static int rebase_config(const char *var, const char *value, void *data) |
| 428 | { |
| 429 | struct rebase_options *opts = data; |
| 430 | |
| 431 | if (!strcmp(var, "rebase.stat")) { |
| 432 | if (git_config_bool(var, value)) |
| 433 | opts->flags |= REBASE_DIFFSTAT; |
| 434 | else |
| 435 | opts->flags &= !REBASE_DIFFSTAT; |
| 436 | return 0; |
| 437 | } |
| 438 | |
| 439 | return git_default_config(var, value, data); |
| 440 | } |
| 441 | |
Pratik Karki | 9a48a61 | 2018-09-04 14:27:16 -0700 | [diff] [blame] | 442 | /* |
| 443 | * Determines whether the commits in from..to are linear, i.e. contain |
| 444 | * no merge commits. This function *expects* `from` to be an ancestor of |
| 445 | * `to`. |
| 446 | */ |
| 447 | static int is_linear_history(struct commit *from, struct commit *to) |
| 448 | { |
| 449 | while (to && to != from) { |
| 450 | parse_commit(to); |
| 451 | if (!to->parents) |
| 452 | return 1; |
| 453 | if (to->parents->next) |
| 454 | return 0; |
| 455 | to = to->parents->item; |
| 456 | } |
| 457 | return 1; |
| 458 | } |
| 459 | |
| 460 | static int can_fast_forward(struct commit *onto, struct object_id *head_oid, |
| 461 | struct object_id *merge_base) |
| 462 | { |
| 463 | struct commit *head = lookup_commit(the_repository, head_oid); |
| 464 | struct commit_list *merge_bases; |
| 465 | int res; |
| 466 | |
| 467 | if (!head) |
| 468 | return 0; |
| 469 | |
| 470 | merge_bases = get_merge_bases(onto, head); |
| 471 | if (merge_bases && !merge_bases->next) { |
| 472 | oidcpy(merge_base, &merge_bases->item->object.oid); |
| 473 | res = !oidcmp(merge_base, &onto->object.oid); |
| 474 | } else { |
| 475 | oidcpy(merge_base, &null_oid); |
| 476 | res = 0; |
| 477 | } |
| 478 | free_commit_list(merge_bases); |
| 479 | return res && is_linear_history(onto, head); |
| 480 | } |
| 481 | |
Pratik Karki | 361badd | 2018-09-04 14:59:49 -0700 | [diff] [blame] | 482 | /* -i followed by -m is still -i */ |
| 483 | static int parse_opt_merge(const struct option *opt, const char *arg, int unset) |
| 484 | { |
| 485 | struct rebase_options *opts = opt->value; |
| 486 | |
| 487 | if (!is_interactive(opts)) |
| 488 | opts->type = REBASE_MERGE; |
| 489 | |
| 490 | return 0; |
| 491 | } |
| 492 | |
| 493 | /* -i followed by -p is still explicitly interactive, but -p alone is not */ |
| 494 | static int parse_opt_interactive(const struct option *opt, const char *arg, |
| 495 | int unset) |
| 496 | { |
| 497 | struct rebase_options *opts = opt->value; |
| 498 | |
| 499 | opts->type = REBASE_INTERACTIVE; |
| 500 | opts->flags |= REBASE_INTERACTIVE_EXPLICIT; |
| 501 | |
| 502 | return 0; |
| 503 | } |
| 504 | |
Pratik Karki | 55071ea | 2018-08-07 01:16:09 +0545 | [diff] [blame] | 505 | int cmd_rebase(int argc, const char **argv, const char *prefix) |
| 506 | { |
Pratik Karki | ac7f467 | 2018-08-07 01:16:11 +0545 | [diff] [blame] | 507 | struct rebase_options options = { |
| 508 | .type = REBASE_UNSPECIFIED, |
Pratik Karki | b4c8eb0 | 2018-09-04 14:27:12 -0700 | [diff] [blame] | 509 | .flags = REBASE_NO_QUIET, |
| 510 | .git_am_opt = STRBUF_INIT, |
Pratik Karki | ead98c1 | 2018-09-04 14:59:52 -0700 | [diff] [blame] | 511 | .allow_rerere_autoupdate = -1, |
Pratik Karki | ac7f467 | 2018-08-07 01:16:11 +0545 | [diff] [blame] | 512 | }; |
| 513 | const char *branch_name; |
Pratik Karki | f957362 | 2018-08-08 20:51:16 +0545 | [diff] [blame] | 514 | int ret, flags, total_argc, in_progress = 0; |
Pratik Karki | 06e4775 | 2018-09-04 14:27:10 -0700 | [diff] [blame] | 515 | int ok_to_skip_pre_rebase = 0; |
Pratik Karki | ac7f467 | 2018-08-07 01:16:11 +0545 | [diff] [blame] | 516 | struct strbuf msg = STRBUF_INIT; |
| 517 | struct strbuf revisions = STRBUF_INIT; |
Pratik Karki | c54dacb | 2018-09-04 14:27:18 -0700 | [diff] [blame] | 518 | struct strbuf buf = STRBUF_INIT; |
Pratik Karki | 075bc85 | 2018-09-04 14:27:09 -0700 | [diff] [blame] | 519 | struct object_id merge_base; |
Pratik Karki | f957362 | 2018-08-08 20:51:16 +0545 | [diff] [blame] | 520 | enum { |
| 521 | NO_ACTION, |
| 522 | ACTION_CONTINUE, |
Pratik Karki | 122420c | 2018-08-08 20:51:17 +0545 | [diff] [blame] | 523 | ACTION_SKIP, |
Pratik Karki | 5e5d961 | 2018-08-08 20:51:18 +0545 | [diff] [blame] | 524 | ACTION_ABORT, |
Pratik Karki | 5a61494 | 2018-08-08 20:51:19 +0545 | [diff] [blame] | 525 | ACTION_QUIT, |
Pratik Karki | 51e9ea6 | 2018-08-08 20:51:20 +0545 | [diff] [blame] | 526 | ACTION_EDIT_TODO, |
| 527 | ACTION_SHOW_CURRENT_PATCH, |
Pratik Karki | f957362 | 2018-08-08 20:51:16 +0545 | [diff] [blame] | 528 | } action = NO_ACTION; |
Pratik Karki | 38dbcef | 2018-09-04 14:59:53 -0700 | [diff] [blame] | 529 | int committer_date_is_author_date = 0; |
Pratik Karki | 99d8cc7 | 2018-09-04 14:59:54 -0700 | [diff] [blame^] | 530 | int ignore_whitespace = 0; |
Pratik Karki | f28d40d | 2018-09-04 14:27:07 -0700 | [diff] [blame] | 531 | struct option builtin_rebase_options[] = { |
| 532 | OPT_STRING(0, "onto", &options.onto_name, |
| 533 | N_("revision"), |
| 534 | N_("rebase onto given branch instead of upstream")), |
Pratik Karki | 06e4775 | 2018-09-04 14:27:10 -0700 | [diff] [blame] | 535 | OPT_BOOL(0, "no-verify", &ok_to_skip_pre_rebase, |
| 536 | N_("allow pre-rebase hook to run")), |
Pratik Karki | b4c8eb0 | 2018-09-04 14:27:12 -0700 | [diff] [blame] | 537 | OPT_NEGBIT('q', "quiet", &options.flags, |
| 538 | N_("be quiet. implies --no-stat"), |
Pratik Karki | bff014d | 2018-09-04 14:27:13 -0700 | [diff] [blame] | 539 | REBASE_NO_QUIET| REBASE_VERBOSE | REBASE_DIFFSTAT), |
| 540 | OPT_BIT('v', "verbose", &options.flags, |
| 541 | N_("display a diffstat of what changed upstream"), |
| 542 | REBASE_NO_QUIET | REBASE_VERBOSE | REBASE_DIFFSTAT), |
| 543 | {OPTION_NEGBIT, 'n', "no-stat", &options.flags, NULL, |
| 544 | N_("do not show diffstat of what changed upstream"), |
| 545 | PARSE_OPT_NOARG, NULL, REBASE_DIFFSTAT }, |
Pratik Karki | 99d8cc7 | 2018-09-04 14:59:54 -0700 | [diff] [blame^] | 546 | OPT_BOOL(0, "ignore-whitespace", &ignore_whitespace, |
| 547 | N_("passed to 'git apply'")), |
Pratik Karki | 73d51ed | 2018-09-04 14:59:50 -0700 | [diff] [blame] | 548 | OPT_BOOL(0, "signoff", &options.signoff, |
| 549 | N_("add a Signed-off-by: line to each commit")), |
Pratik Karki | 38dbcef | 2018-09-04 14:59:53 -0700 | [diff] [blame] | 550 | OPT_BOOL(0, "committer-date-is-author-date", |
| 551 | &committer_date_is_author_date, |
| 552 | N_("passed to 'git am'")), |
Pratik Karki | 1ed9c14 | 2018-09-04 14:27:17 -0700 | [diff] [blame] | 553 | OPT_BIT('f', "force-rebase", &options.flags, |
| 554 | N_("cherry-pick all commits, even if unchanged"), |
| 555 | REBASE_FORCE), |
| 556 | OPT_BIT(0, "no-ff", &options.flags, |
| 557 | N_("cherry-pick all commits, even if unchanged"), |
| 558 | REBASE_FORCE), |
Pratik Karki | f957362 | 2018-08-08 20:51:16 +0545 | [diff] [blame] | 559 | OPT_CMDMODE(0, "continue", &action, N_("continue"), |
| 560 | ACTION_CONTINUE), |
Pratik Karki | 122420c | 2018-08-08 20:51:17 +0545 | [diff] [blame] | 561 | OPT_CMDMODE(0, "skip", &action, |
| 562 | N_("skip current patch and continue"), ACTION_SKIP), |
Pratik Karki | 5e5d961 | 2018-08-08 20:51:18 +0545 | [diff] [blame] | 563 | OPT_CMDMODE(0, "abort", &action, |
| 564 | N_("abort and check out the original branch"), |
| 565 | ACTION_ABORT), |
Pratik Karki | 5a61494 | 2018-08-08 20:51:19 +0545 | [diff] [blame] | 566 | OPT_CMDMODE(0, "quit", &action, |
| 567 | N_("abort but keep HEAD where it is"), ACTION_QUIT), |
Pratik Karki | 51e9ea6 | 2018-08-08 20:51:20 +0545 | [diff] [blame] | 568 | OPT_CMDMODE(0, "edit-todo", &action, N_("edit the todo list " |
| 569 | "during an interactive rebase"), ACTION_EDIT_TODO), |
| 570 | OPT_CMDMODE(0, "show-current-patch", &action, |
| 571 | N_("show the patch file being applied or merged"), |
| 572 | ACTION_SHOW_CURRENT_PATCH), |
Pratik Karki | 361badd | 2018-09-04 14:59:49 -0700 | [diff] [blame] | 573 | { OPTION_CALLBACK, 'm', "merge", &options, NULL, |
| 574 | N_("use merging strategies to rebase"), |
| 575 | PARSE_OPT_NOARG | PARSE_OPT_NONEG, |
| 576 | parse_opt_merge }, |
| 577 | { OPTION_CALLBACK, 'i', "interactive", &options, NULL, |
| 578 | N_("let the user edit the list of commits to rebase"), |
| 579 | PARSE_OPT_NOARG | PARSE_OPT_NONEG, |
| 580 | parse_opt_interactive }, |
| 581 | OPT_SET_INT('p', "preserve-merges", &options.type, |
| 582 | N_("try to recreate merges instead of ignoring " |
| 583 | "them"), REBASE_PRESERVE_MERGES), |
Pratik Karki | ead98c1 | 2018-09-04 14:59:52 -0700 | [diff] [blame] | 584 | OPT_BOOL(0, "rerere-autoupdate", |
| 585 | &options.allow_rerere_autoupdate, |
| 586 | N_("allow rerere to update index with resolved " |
| 587 | "conflict")), |
Pratik Karki | f28d40d | 2018-09-04 14:27:07 -0700 | [diff] [blame] | 588 | OPT_END(), |
| 589 | }; |
Pratik Karki | ac7f467 | 2018-08-07 01:16:11 +0545 | [diff] [blame] | 590 | |
Pratik Karki | 55071ea | 2018-08-07 01:16:09 +0545 | [diff] [blame] | 591 | /* |
| 592 | * NEEDSWORK: Once the builtin rebase has been tested enough |
| 593 | * and git-legacy-rebase.sh is retired to contrib/, this preamble |
| 594 | * can be removed. |
| 595 | */ |
| 596 | |
| 597 | if (!use_builtin_rebase()) { |
| 598 | const char *path = mkpath("%s/git-legacy-rebase", |
| 599 | git_exec_path()); |
| 600 | |
| 601 | if (sane_execvp(path, (char **)argv) < 0) |
| 602 | die_errno(_("could not exec %s"), path); |
| 603 | else |
| 604 | BUG("sane_execvp() returned???"); |
| 605 | } |
| 606 | |
Pratik Karki | f28d40d | 2018-09-04 14:27:07 -0700 | [diff] [blame] | 607 | if (argc == 2 && !strcmp(argv[1], "-h")) |
| 608 | usage_with_options(builtin_rebase_usage, |
| 609 | builtin_rebase_options); |
| 610 | |
Pratik Karki | 55071ea | 2018-08-07 01:16:09 +0545 | [diff] [blame] | 611 | prefix = setup_git_directory(); |
| 612 | trace_repo_setup(prefix); |
| 613 | setup_work_tree(); |
| 614 | |
Pratik Karki | bff014d | 2018-09-04 14:27:13 -0700 | [diff] [blame] | 615 | git_config(rebase_config, &options); |
| 616 | |
Pratik Karki | 0eabf4b | 2018-08-08 20:51:22 +0545 | [diff] [blame] | 617 | strbuf_reset(&buf); |
| 618 | strbuf_addf(&buf, "%s/applying", apply_dir()); |
| 619 | if(file_exists(buf.buf)) |
| 620 | die(_("It looks like 'git am' is in progress. Cannot rebase.")); |
| 621 | |
Pratik Karki | c54dacb | 2018-09-04 14:27:18 -0700 | [diff] [blame] | 622 | if (is_directory(apply_dir())) { |
| 623 | options.type = REBASE_AM; |
| 624 | options.state_dir = apply_dir(); |
| 625 | } else if (is_directory(merge_dir())) { |
| 626 | strbuf_reset(&buf); |
| 627 | strbuf_addf(&buf, "%s/rewritten", merge_dir()); |
| 628 | if (is_directory(buf.buf)) { |
| 629 | options.type = REBASE_PRESERVE_MERGES; |
| 630 | options.flags |= REBASE_INTERACTIVE_EXPLICIT; |
| 631 | } else { |
| 632 | strbuf_reset(&buf); |
| 633 | strbuf_addf(&buf, "%s/interactive", merge_dir()); |
| 634 | if(file_exists(buf.buf)) { |
| 635 | options.type = REBASE_INTERACTIVE; |
| 636 | options.flags |= REBASE_INTERACTIVE_EXPLICIT; |
| 637 | } else |
| 638 | options.type = REBASE_MERGE; |
| 639 | } |
| 640 | options.state_dir = merge_dir(); |
| 641 | } |
| 642 | |
| 643 | if (options.type != REBASE_UNSPECIFIED) |
| 644 | in_progress = 1; |
| 645 | |
Pratik Karki | f957362 | 2018-08-08 20:51:16 +0545 | [diff] [blame] | 646 | total_argc = argc; |
Pratik Karki | f28d40d | 2018-09-04 14:27:07 -0700 | [diff] [blame] | 647 | argc = parse_options(argc, argv, prefix, |
| 648 | builtin_rebase_options, |
| 649 | builtin_rebase_usage, 0); |
| 650 | |
Pratik Karki | f957362 | 2018-08-08 20:51:16 +0545 | [diff] [blame] | 651 | if (action != NO_ACTION && total_argc != 2) { |
| 652 | usage_with_options(builtin_rebase_usage, |
| 653 | builtin_rebase_options); |
| 654 | } |
| 655 | |
Pratik Karki | f28d40d | 2018-09-04 14:27:07 -0700 | [diff] [blame] | 656 | if (argc > 2) |
| 657 | usage_with_options(builtin_rebase_usage, |
| 658 | builtin_rebase_options); |
Pratik Karki | ac7f467 | 2018-08-07 01:16:11 +0545 | [diff] [blame] | 659 | |
Pratik Karki | d732a57 | 2018-08-08 20:51:21 +0545 | [diff] [blame] | 660 | if (action != NO_ACTION && !in_progress) |
| 661 | die(_("No rebase in progress?")); |
| 662 | |
Pratik Karki | 51e9ea6 | 2018-08-08 20:51:20 +0545 | [diff] [blame] | 663 | if (action == ACTION_EDIT_TODO && !is_interactive(&options)) |
| 664 | die(_("The --edit-todo action can only be used during " |
| 665 | "interactive rebase.")); |
| 666 | |
Pratik Karki | f957362 | 2018-08-08 20:51:16 +0545 | [diff] [blame] | 667 | switch (action) { |
| 668 | case ACTION_CONTINUE: { |
| 669 | struct object_id head; |
| 670 | struct lock_file lock_file = LOCK_INIT; |
| 671 | int fd; |
| 672 | |
| 673 | options.action = "continue"; |
| 674 | |
| 675 | /* Sanity check */ |
| 676 | if (get_oid("HEAD", &head)) |
| 677 | die(_("Cannot read HEAD")); |
| 678 | |
| 679 | fd = hold_locked_index(&lock_file, 0); |
| 680 | if (read_index(the_repository->index) < 0) |
| 681 | die(_("could not read index")); |
| 682 | refresh_index(the_repository->index, REFRESH_QUIET, NULL, NULL, |
| 683 | NULL); |
| 684 | if (0 <= fd) |
| 685 | update_index_if_able(the_repository->index, |
| 686 | &lock_file); |
| 687 | rollback_lock_file(&lock_file); |
| 688 | |
| 689 | if (has_unstaged_changes(1)) { |
| 690 | puts(_("You must edit all merge conflicts and then\n" |
| 691 | "mark them as resolved using git add")); |
| 692 | exit(1); |
| 693 | } |
| 694 | if (read_basic_state(&options)) |
| 695 | exit(1); |
| 696 | goto run_rebase; |
| 697 | } |
Pratik Karki | 122420c | 2018-08-08 20:51:17 +0545 | [diff] [blame] | 698 | case ACTION_SKIP: { |
| 699 | struct string_list merge_rr = STRING_LIST_INIT_DUP; |
| 700 | |
| 701 | options.action = "skip"; |
| 702 | |
| 703 | rerere_clear(&merge_rr); |
| 704 | string_list_clear(&merge_rr, 1); |
| 705 | |
| 706 | if (reset_head(NULL, "reset", NULL, 0) < 0) |
| 707 | die(_("could not discard worktree changes")); |
| 708 | if (read_basic_state(&options)) |
| 709 | exit(1); |
| 710 | goto run_rebase; |
| 711 | } |
Pratik Karki | 5e5d961 | 2018-08-08 20:51:18 +0545 | [diff] [blame] | 712 | case ACTION_ABORT: { |
| 713 | struct string_list merge_rr = STRING_LIST_INIT_DUP; |
| 714 | options.action = "abort"; |
| 715 | |
| 716 | rerere_clear(&merge_rr); |
| 717 | string_list_clear(&merge_rr, 1); |
| 718 | |
| 719 | if (read_basic_state(&options)) |
| 720 | exit(1); |
| 721 | if (reset_head(&options.orig_head, "reset", |
| 722 | options.head_name, 0) < 0) |
| 723 | die(_("could not move back to %s"), |
| 724 | oid_to_hex(&options.orig_head)); |
| 725 | ret = finish_rebase(&options); |
| 726 | goto cleanup; |
| 727 | } |
Pratik Karki | 5a61494 | 2018-08-08 20:51:19 +0545 | [diff] [blame] | 728 | case ACTION_QUIT: { |
| 729 | strbuf_reset(&buf); |
| 730 | strbuf_addstr(&buf, options.state_dir); |
| 731 | ret = !!remove_dir_recursively(&buf, 0); |
| 732 | if (ret) |
| 733 | die(_("could not remove '%s'"), options.state_dir); |
| 734 | goto cleanup; |
| 735 | } |
Pratik Karki | 51e9ea6 | 2018-08-08 20:51:20 +0545 | [diff] [blame] | 736 | case ACTION_EDIT_TODO: |
| 737 | options.action = "edit-todo"; |
| 738 | options.dont_finish_rebase = 1; |
| 739 | goto run_rebase; |
| 740 | case ACTION_SHOW_CURRENT_PATCH: |
| 741 | options.action = "show-current-patch"; |
| 742 | options.dont_finish_rebase = 1; |
| 743 | goto run_rebase; |
| 744 | case NO_ACTION: |
| 745 | break; |
Pratik Karki | f957362 | 2018-08-08 20:51:16 +0545 | [diff] [blame] | 746 | default: |
Pratik Karki | 51e9ea6 | 2018-08-08 20:51:20 +0545 | [diff] [blame] | 747 | BUG("action: %d", action); |
Pratik Karki | f957362 | 2018-08-08 20:51:16 +0545 | [diff] [blame] | 748 | } |
| 749 | |
Pratik Karki | c54dacb | 2018-09-04 14:27:18 -0700 | [diff] [blame] | 750 | /* Make sure no rebase is in progress */ |
| 751 | if (in_progress) { |
| 752 | const char *last_slash = strrchr(options.state_dir, '/'); |
| 753 | const char *state_dir_base = |
| 754 | last_slash ? last_slash + 1 : options.state_dir; |
| 755 | const char *cmd_live_rebase = |
| 756 | "git rebase (--continue | --abort | --skip)"; |
| 757 | strbuf_reset(&buf); |
| 758 | strbuf_addf(&buf, "rm -fr \"%s\"", options.state_dir); |
| 759 | die(_("It seems that there is already a %s directory, and\n" |
| 760 | "I wonder if you are in the middle of another rebase. " |
| 761 | "If that is the\n" |
| 762 | "case, please try\n\t%s\n" |
| 763 | "If that is not the case, please\n\t%s\n" |
| 764 | "and run me again. I am stopping in case you still " |
| 765 | "have something\n" |
| 766 | "valuable there.\n"), |
| 767 | state_dir_base, cmd_live_rebase, buf.buf); |
| 768 | } |
| 769 | |
Pratik Karki | b4c8eb0 | 2018-09-04 14:27:12 -0700 | [diff] [blame] | 770 | if (!(options.flags & REBASE_NO_QUIET)) |
| 771 | strbuf_addstr(&options.git_am_opt, " -q"); |
| 772 | |
Pratik Karki | 38dbcef | 2018-09-04 14:59:53 -0700 | [diff] [blame] | 773 | if (committer_date_is_author_date) { |
| 774 | strbuf_addstr(&options.git_am_opt, |
| 775 | " --committer-date-is-author-date"); |
| 776 | options.flags |= REBASE_FORCE; |
| 777 | } |
| 778 | |
Pratik Karki | 99d8cc7 | 2018-09-04 14:59:54 -0700 | [diff] [blame^] | 779 | if (ignore_whitespace) |
| 780 | strbuf_addstr(&options.git_am_opt, " --ignore-whitespace"); |
| 781 | |
Pratik Karki | ac7f467 | 2018-08-07 01:16:11 +0545 | [diff] [blame] | 782 | switch (options.type) { |
| 783 | case REBASE_MERGE: |
| 784 | case REBASE_INTERACTIVE: |
| 785 | case REBASE_PRESERVE_MERGES: |
| 786 | options.state_dir = merge_dir(); |
| 787 | break; |
| 788 | case REBASE_AM: |
| 789 | options.state_dir = apply_dir(); |
| 790 | break; |
| 791 | default: |
| 792 | /* the default rebase backend is `--am` */ |
| 793 | options.type = REBASE_AM; |
| 794 | options.state_dir = apply_dir(); |
| 795 | break; |
| 796 | } |
| 797 | |
Pratik Karki | 73d51ed | 2018-09-04 14:59:50 -0700 | [diff] [blame] | 798 | if (options.signoff) { |
| 799 | if (options.type == REBASE_PRESERVE_MERGES) |
| 800 | die("cannot combine '--signoff' with " |
| 801 | "'--preserve-merges'"); |
| 802 | strbuf_addstr(&options.git_am_opt, " --signoff"); |
| 803 | options.flags |= REBASE_FORCE; |
| 804 | } |
| 805 | |
Pratik Karki | ac7f467 | 2018-08-07 01:16:11 +0545 | [diff] [blame] | 806 | if (!options.root) { |
Pratik Karki | f28d40d | 2018-09-04 14:27:07 -0700 | [diff] [blame] | 807 | if (argc < 1) |
Pratik Karki | ac7f467 | 2018-08-07 01:16:11 +0545 | [diff] [blame] | 808 | die("TODO: handle @{upstream}"); |
| 809 | else { |
Pratik Karki | f28d40d | 2018-09-04 14:27:07 -0700 | [diff] [blame] | 810 | options.upstream_name = argv[0]; |
Pratik Karki | ac7f467 | 2018-08-07 01:16:11 +0545 | [diff] [blame] | 811 | argc--; |
| 812 | argv++; |
| 813 | if (!strcmp(options.upstream_name, "-")) |
| 814 | options.upstream_name = "@{-1}"; |
| 815 | } |
| 816 | options.upstream = peel_committish(options.upstream_name); |
| 817 | if (!options.upstream) |
| 818 | die(_("invalid upstream '%s'"), options.upstream_name); |
Pratik Karki | 06e4775 | 2018-09-04 14:27:10 -0700 | [diff] [blame] | 819 | options.upstream_arg = options.upstream_name; |
Pratik Karki | ac7f467 | 2018-08-07 01:16:11 +0545 | [diff] [blame] | 820 | } else |
| 821 | die("TODO: upstream for --root"); |
| 822 | |
| 823 | /* Make sure the branch to rebase onto is valid. */ |
| 824 | if (!options.onto_name) |
| 825 | options.onto_name = options.upstream_name; |
| 826 | if (strstr(options.onto_name, "...")) { |
Pratik Karki | 075bc85 | 2018-09-04 14:27:09 -0700 | [diff] [blame] | 827 | if (get_oid_mb(options.onto_name, &merge_base) < 0) |
| 828 | die(_("'%s': need exactly one merge base"), |
| 829 | options.onto_name); |
| 830 | options.onto = lookup_commit_or_die(&merge_base, |
| 831 | options.onto_name); |
Pratik Karki | ac7f467 | 2018-08-07 01:16:11 +0545 | [diff] [blame] | 832 | } else { |
| 833 | options.onto = peel_committish(options.onto_name); |
| 834 | if (!options.onto) |
| 835 | die(_("Does not point to a valid commit '%s'"), |
| 836 | options.onto_name); |
| 837 | } |
| 838 | |
| 839 | /* |
| 840 | * If the branch to rebase is given, that is the branch we will rebase |
| 841 | * branch_name -- branch/commit being rebased, or |
| 842 | * HEAD (already detached) |
| 843 | * orig_head -- commit object name of tip of the branch before rebasing |
Pratik Karki | d4c569f | 2018-09-04 14:27:20 -0700 | [diff] [blame] | 844 | * head_name -- refs/heads/<that-branch> or NULL (detached HEAD) |
Pratik Karki | ac7f467 | 2018-08-07 01:16:11 +0545 | [diff] [blame] | 845 | */ |
Pratik Karki | e65123a | 2018-09-04 14:27:21 -0700 | [diff] [blame] | 846 | if (argc == 1) { |
| 847 | /* Is it "rebase other branchname" or "rebase other commit"? */ |
| 848 | branch_name = argv[0]; |
| 849 | options.switch_to = argv[0]; |
| 850 | |
| 851 | /* Is it a local branch? */ |
| 852 | strbuf_reset(&buf); |
| 853 | strbuf_addf(&buf, "refs/heads/%s", branch_name); |
| 854 | if (!read_ref(buf.buf, &options.orig_head)) |
| 855 | options.head_name = xstrdup(buf.buf); |
| 856 | /* If not is it a valid ref (branch or commit)? */ |
| 857 | else if (!get_oid(branch_name, &options.orig_head)) |
| 858 | options.head_name = NULL; |
| 859 | else |
| 860 | die(_("fatal: no such branch/commit '%s'"), |
| 861 | branch_name); |
| 862 | } else if (argc == 0) { |
Pratik Karki | ac7f467 | 2018-08-07 01:16:11 +0545 | [diff] [blame] | 863 | /* Do not need to switch branches, we are already on it. */ |
| 864 | options.head_name = |
| 865 | xstrdup_or_null(resolve_ref_unsafe("HEAD", 0, NULL, |
| 866 | &flags)); |
| 867 | if (!options.head_name) |
| 868 | die(_("No such ref: %s"), "HEAD"); |
| 869 | if (flags & REF_ISSYMREF) { |
| 870 | if (!skip_prefix(options.head_name, |
| 871 | "refs/heads/", &branch_name)) |
| 872 | branch_name = options.head_name; |
| 873 | |
| 874 | } else { |
Pratik Karki | d4c569f | 2018-09-04 14:27:20 -0700 | [diff] [blame] | 875 | free(options.head_name); |
| 876 | options.head_name = NULL; |
Pratik Karki | ac7f467 | 2018-08-07 01:16:11 +0545 | [diff] [blame] | 877 | branch_name = "HEAD"; |
| 878 | } |
| 879 | if (get_oid("HEAD", &options.orig_head)) |
| 880 | die(_("Could not resolve HEAD to a revision")); |
Pratik Karki | e65123a | 2018-09-04 14:27:21 -0700 | [diff] [blame] | 881 | } else |
| 882 | BUG("unexpected number of arguments left to parse"); |
Pratik Karki | ac7f467 | 2018-08-07 01:16:11 +0545 | [diff] [blame] | 883 | |
Pratik Karki | e0333e5 | 2018-09-04 14:27:14 -0700 | [diff] [blame] | 884 | if (read_index(the_repository->index) < 0) |
| 885 | die(_("could not read index")); |
| 886 | |
| 887 | if (require_clean_work_tree("rebase", |
| 888 | _("Please commit or stash them."), 1, 1)) { |
| 889 | ret = 1; |
| 890 | goto cleanup; |
| 891 | } |
| 892 | |
Pratik Karki | 9a48a61 | 2018-09-04 14:27:16 -0700 | [diff] [blame] | 893 | /* |
| 894 | * Now we are rebasing commits upstream..orig_head (or with --root, |
| 895 | * everything leading up to orig_head) on top of onto. |
| 896 | */ |
| 897 | |
| 898 | /* |
| 899 | * Check if we are already based on onto with linear history, |
| 900 | * but this should be done only when upstream and onto are the same |
| 901 | * and if this is not an interactive rebase. |
| 902 | */ |
| 903 | if (can_fast_forward(options.onto, &options.orig_head, &merge_base) && |
| 904 | !is_interactive(&options) && !options.restrict_revision && |
| 905 | !oidcmp(&options.upstream->object.oid, &options.onto->object.oid)) { |
| 906 | int flag; |
| 907 | |
Pratik Karki | 1ed9c14 | 2018-09-04 14:27:17 -0700 | [diff] [blame] | 908 | if (!(options.flags & REBASE_FORCE)) { |
Pratik Karki | e65123a | 2018-09-04 14:27:21 -0700 | [diff] [blame] | 909 | /* Lazily switch to the target branch if needed... */ |
| 910 | if (options.switch_to) { |
| 911 | struct object_id oid; |
| 912 | |
| 913 | if (get_oid(options.switch_to, &oid) < 0) { |
| 914 | ret = !!error(_("could not parse '%s'"), |
| 915 | options.switch_to); |
| 916 | goto cleanup; |
| 917 | } |
| 918 | |
| 919 | strbuf_reset(&buf); |
| 920 | strbuf_addf(&buf, "rebase: checkout %s", |
| 921 | options.switch_to); |
| 922 | if (reset_head(&oid, "checkout", |
| 923 | options.head_name, 0) < 0) { |
| 924 | ret = !!error(_("could not switch to " |
| 925 | "%s"), |
| 926 | options.switch_to); |
| 927 | goto cleanup; |
| 928 | } |
| 929 | } |
| 930 | |
Pratik Karki | 1ed9c14 | 2018-09-04 14:27:17 -0700 | [diff] [blame] | 931 | if (!(options.flags & REBASE_NO_QUIET)) |
| 932 | ; /* be quiet */ |
| 933 | else if (!strcmp(branch_name, "HEAD") && |
| 934 | resolve_ref_unsafe("HEAD", 0, NULL, &flag)) |
| 935 | puts(_("HEAD is up to date.")); |
| 936 | else |
| 937 | printf(_("Current branch %s is up to date.\n"), |
| 938 | branch_name); |
| 939 | ret = !!finish_rebase(&options); |
| 940 | goto cleanup; |
| 941 | } else if (!(options.flags & REBASE_NO_QUIET)) |
Pratik Karki | 9a48a61 | 2018-09-04 14:27:16 -0700 | [diff] [blame] | 942 | ; /* be quiet */ |
| 943 | else if (!strcmp(branch_name, "HEAD") && |
| 944 | resolve_ref_unsafe("HEAD", 0, NULL, &flag)) |
| 945 | puts(_("HEAD is up to date, rebase forced.")); |
| 946 | else |
| 947 | printf(_("Current branch %s is up to date, rebase " |
| 948 | "forced.\n"), branch_name); |
| 949 | } |
| 950 | |
Pratik Karki | 06e4775 | 2018-09-04 14:27:10 -0700 | [diff] [blame] | 951 | /* If a hook exists, give it a chance to interrupt*/ |
| 952 | if (!ok_to_skip_pre_rebase && |
| 953 | run_hook_le(NULL, "pre-rebase", options.upstream_arg, |
| 954 | argc ? argv[0] : NULL, NULL)) |
| 955 | die(_("The pre-rebase hook refused to rebase.")); |
| 956 | |
Pratik Karki | bff014d | 2018-09-04 14:27:13 -0700 | [diff] [blame] | 957 | if (options.flags & REBASE_DIFFSTAT) { |
| 958 | struct diff_options opts; |
| 959 | |
| 960 | if (options.flags & REBASE_VERBOSE) |
| 961 | printf(_("Changes from %s to %s:\n"), |
| 962 | oid_to_hex(&merge_base), |
| 963 | oid_to_hex(&options.onto->object.oid)); |
| 964 | |
| 965 | /* We want color (if set), but no pager */ |
| 966 | diff_setup(&opts); |
| 967 | opts.stat_width = -1; /* use full terminal width */ |
| 968 | opts.stat_graph_width = -1; /* respect statGraphWidth config */ |
| 969 | opts.output_format |= |
| 970 | DIFF_FORMAT_SUMMARY | DIFF_FORMAT_DIFFSTAT; |
| 971 | opts.detect_rename = DIFF_DETECT_RENAME; |
| 972 | diff_setup_done(&opts); |
| 973 | diff_tree_oid(&merge_base, &options.onto->object.oid, |
| 974 | "", &opts); |
| 975 | diffcore_std(&opts); |
| 976 | diff_flush(&opts); |
| 977 | } |
| 978 | |
Pratik Karki | 361badd | 2018-09-04 14:59:49 -0700 | [diff] [blame] | 979 | if (is_interactive(&options)) |
| 980 | goto run_rebase; |
| 981 | |
Pratik Karki | bff014d | 2018-09-04 14:27:13 -0700 | [diff] [blame] | 982 | /* Detach HEAD and reset the tree */ |
| 983 | if (options.flags & REBASE_NO_QUIET) |
| 984 | printf(_("First, rewinding head to replay your work on top of " |
| 985 | "it...\n")); |
| 986 | |
Pratik Karki | ac7f467 | 2018-08-07 01:16:11 +0545 | [diff] [blame] | 987 | strbuf_addf(&msg, "rebase: checkout %s", options.onto_name); |
| 988 | if (reset_head(&options.onto->object.oid, "checkout", NULL, 1)) |
| 989 | die(_("Could not detach HEAD")); |
| 990 | strbuf_release(&msg); |
| 991 | |
| 992 | strbuf_addf(&revisions, "%s..%s", |
| 993 | options.root ? oid_to_hex(&options.onto->object.oid) : |
| 994 | (options.restrict_revision ? |
| 995 | oid_to_hex(&options.restrict_revision->object.oid) : |
| 996 | oid_to_hex(&options.upstream->object.oid)), |
| 997 | oid_to_hex(&options.orig_head)); |
| 998 | |
| 999 | options.revisions = revisions.buf; |
| 1000 | |
Pratik Karki | f957362 | 2018-08-08 20:51:16 +0545 | [diff] [blame] | 1001 | run_rebase: |
Pratik Karki | ac7f467 | 2018-08-07 01:16:11 +0545 | [diff] [blame] | 1002 | ret = !!run_specific_rebase(&options); |
| 1003 | |
Pratik Karki | e0333e5 | 2018-09-04 14:27:14 -0700 | [diff] [blame] | 1004 | cleanup: |
Pratik Karki | ac7f467 | 2018-08-07 01:16:11 +0545 | [diff] [blame] | 1005 | strbuf_release(&revisions); |
| 1006 | free(options.head_name); |
| 1007 | return ret; |
Pratik Karki | 55071ea | 2018-08-07 01:16:09 +0545 | [diff] [blame] | 1008 | } |