int cmd_rebase()

in builtin/rebase.c [1032:1840]


int cmd_rebase(int argc, const char **argv, const char *prefix)
{
	struct rebase_options options = REBASE_OPTIONS_INIT;
	const char *branch_name;
	int ret, flags, total_argc, in_progress = 0;
	int keep_base = 0;
	int ok_to_skip_pre_rebase = 0;
	struct strbuf msg = STRBUF_INIT;
	struct strbuf revisions = STRBUF_INIT;
	struct strbuf buf = STRBUF_INIT;
	struct object_id merge_base;
	int ignore_whitespace = 0;
	enum action action = ACTION_NONE;
	const char *gpg_sign = NULL;
	struct string_list exec = STRING_LIST_INIT_NODUP;
	const char *rebase_merges = NULL;
	struct string_list strategy_options = STRING_LIST_INIT_NODUP;
	struct object_id squash_onto;
	char *squash_onto_name = NULL;
	int reschedule_failed_exec = -1;
	int allow_preemptive_ff = 1;
	int preserve_merges_selected = 0;
	struct reset_head_opts ropts = { 0 };
	struct option builtin_rebase_options[] = {
		OPT_STRING(0, "onto", &options.onto_name,
			   N_("revision"),
			   N_("rebase onto given branch instead of upstream")),
		OPT_BOOL(0, "keep-base", &keep_base,
			 N_("use the merge-base of upstream and branch as the current base")),
		OPT_BOOL(0, "no-verify", &ok_to_skip_pre_rebase,
			 N_("allow pre-rebase hook to run")),
		OPT_NEGBIT('q', "quiet", &options.flags,
			   N_("be quiet. implies --no-stat"),
			   REBASE_NO_QUIET | REBASE_VERBOSE | REBASE_DIFFSTAT),
		OPT_BIT('v', "verbose", &options.flags,
			N_("display a diffstat of what changed upstream"),
			REBASE_NO_QUIET | REBASE_VERBOSE | REBASE_DIFFSTAT),
		{OPTION_NEGBIT, 'n', "no-stat", &options.flags, NULL,
			N_("do not show diffstat of what changed upstream"),
			PARSE_OPT_NOARG, NULL, REBASE_DIFFSTAT },
		OPT_BOOL(0, "signoff", &options.signoff,
			 N_("add a Signed-off-by trailer to each commit")),
		OPT_BOOL(0, "committer-date-is-author-date",
			 &options.committer_date_is_author_date,
			 N_("make committer date match author date")),
		OPT_BOOL(0, "reset-author-date", &options.ignore_date,
			 N_("ignore author date and use current date")),
		OPT_HIDDEN_BOOL(0, "ignore-date", &options.ignore_date,
				N_("synonym of --reset-author-date")),
		OPT_PASSTHRU_ARGV('C', NULL, &options.git_am_opts, N_("n"),
				  N_("passed to 'git apply'"), 0),
		OPT_BOOL(0, "ignore-whitespace", &ignore_whitespace,
			 N_("ignore changes in whitespace")),
		OPT_PASSTHRU_ARGV(0, "whitespace", &options.git_am_opts,
				  N_("action"), N_("passed to 'git apply'"), 0),
		OPT_BIT('f', "force-rebase", &options.flags,
			N_("cherry-pick all commits, even if unchanged"),
			REBASE_FORCE),
		OPT_BIT(0, "no-ff", &options.flags,
			N_("cherry-pick all commits, even if unchanged"),
			REBASE_FORCE),
		OPT_CMDMODE(0, "continue", &action, N_("continue"),
			    ACTION_CONTINUE),
		OPT_CMDMODE(0, "skip", &action,
			    N_("skip current patch and continue"), ACTION_SKIP),
		OPT_CMDMODE(0, "abort", &action,
			    N_("abort and check out the original branch"),
			    ACTION_ABORT),
		OPT_CMDMODE(0, "quit", &action,
			    N_("abort but keep HEAD where it is"), ACTION_QUIT),
		OPT_CMDMODE(0, "edit-todo", &action, N_("edit the todo list "
			    "during an interactive rebase"), ACTION_EDIT_TODO),
		OPT_CMDMODE(0, "show-current-patch", &action,
			    N_("show the patch file being applied or merged"),
			    ACTION_SHOW_CURRENT_PATCH),
		OPT_CALLBACK_F(0, "apply", &options, NULL,
			N_("use apply strategies to rebase"),
			PARSE_OPT_NOARG | PARSE_OPT_NONEG,
			parse_opt_am),
		OPT_CALLBACK_F('m', "merge", &options, NULL,
			N_("use merging strategies to rebase"),
			PARSE_OPT_NOARG | PARSE_OPT_NONEG,
			parse_opt_merge),
		OPT_CALLBACK_F('i', "interactive", &options, NULL,
			N_("let the user edit the list of commits to rebase"),
			PARSE_OPT_NOARG | PARSE_OPT_NONEG,
			parse_opt_interactive),
		OPT_SET_INT_F('p', "preserve-merges", &preserve_merges_selected,
			      N_("(REMOVED) was: try to recreate merges "
				 "instead of ignoring them"),
			      1, PARSE_OPT_HIDDEN),
		OPT_RERERE_AUTOUPDATE(&options.allow_rerere_autoupdate),
		OPT_CALLBACK_F(0, "empty", &options, "{drop,keep,ask}",
			       N_("how to handle commits that become empty"),
			       PARSE_OPT_NONEG, parse_opt_empty),
		OPT_CALLBACK_F('k', "keep-empty", &options, NULL,
			N_("keep commits which start empty"),
			PARSE_OPT_NOARG | PARSE_OPT_HIDDEN,
			parse_opt_keep_empty),
		OPT_BOOL(0, "autosquash", &options.autosquash,
			 N_("move commits that begin with "
			    "squash!/fixup! under -i")),
		OPT_BOOL(0, "update-refs", &options.update_refs,
			 N_("update branches that point to commits "
			    "that are being rebased")),
		{ OPTION_STRING, 'S', "gpg-sign", &gpg_sign, N_("key-id"),
			N_("GPG-sign commits"),
			PARSE_OPT_OPTARG, NULL, (intptr_t) "" },
		OPT_AUTOSTASH(&options.autostash),
		OPT_STRING_LIST('x', "exec", &exec, N_("exec"),
				N_("add exec lines after each commit of the "
				   "editable list")),
		OPT_BOOL_F(0, "allow-empty-message",
			   &options.allow_empty_message,
			   N_("allow rebasing commits with empty messages"),
			   PARSE_OPT_HIDDEN),
		{OPTION_STRING, 'r', "rebase-merges", &rebase_merges,
			N_("mode"),
			N_("try to rebase merges instead of skipping them"),
			PARSE_OPT_OPTARG, NULL, (intptr_t)""},
		OPT_BOOL(0, "fork-point", &options.fork_point,
			 N_("use 'merge-base --fork-point' to refine upstream")),
		OPT_STRING('s', "strategy", &options.strategy,
			   N_("strategy"), N_("use the given merge strategy")),
		OPT_STRING_LIST('X', "strategy-option", &strategy_options,
				N_("option"),
				N_("pass the argument through to the merge "
				   "strategy")),
		OPT_BOOL(0, "root", &options.root,
			 N_("rebase all reachable commits up to the root(s)")),
		OPT_BOOL(0, "reschedule-failed-exec",
			 &reschedule_failed_exec,
			 N_("automatically re-schedule any `exec` that fails")),
		OPT_BOOL(0, "reapply-cherry-picks", &options.reapply_cherry_picks,
			 N_("apply all changes, even those already present upstream")),
		OPT_END(),
	};
	int i;

	if (argc == 2 && !strcmp(argv[1], "-h"))
		usage_with_options(builtin_rebase_usage,
				   builtin_rebase_options);

	prepare_repo_settings(the_repository);
	the_repository->settings.command_requires_full_index = 0;

	options.allow_empty_message = 1;
	git_config(rebase_config, &options);
	/* options.gpg_sign_opt will be either "-S" or NULL */
	gpg_sign = options.gpg_sign_opt ? "" : NULL;
	FREE_AND_NULL(options.gpg_sign_opt);

	strbuf_reset(&buf);
	strbuf_addf(&buf, "%s/applying", apply_dir());
	if(file_exists(buf.buf))
		die(_("It looks like 'git am' is in progress. Cannot rebase."));

	if (is_directory(apply_dir())) {
		options.type = REBASE_APPLY;
		options.state_dir = apply_dir();
	} else if (is_directory(merge_dir())) {
		strbuf_reset(&buf);
		strbuf_addf(&buf, "%s/rewritten", merge_dir());
		if (!(action == ACTION_ABORT) && is_directory(buf.buf)) {
			die(_("`rebase --preserve-merges` (-p) is no longer supported.\n"
			"Use `git rebase --abort` to terminate current rebase.\n"
			"Or downgrade to v2.33, or earlier, to complete the rebase."));
		} else {
			strbuf_reset(&buf);
			strbuf_addf(&buf, "%s/interactive", merge_dir());
			options.type = REBASE_MERGE;
			if (file_exists(buf.buf))
				options.flags |= REBASE_INTERACTIVE_EXPLICIT;
		}
		options.state_dir = merge_dir();
	}

	if (options.type != REBASE_UNSPECIFIED)
		in_progress = 1;

	total_argc = argc;
	argc = parse_options(argc, argv, prefix,
			     builtin_rebase_options,
			     builtin_rebase_usage, 0);

	if (preserve_merges_selected)
		die(_("--preserve-merges was replaced by --rebase-merges\n"
			"Note: Your `pull.rebase` configuration may also be set to 'preserve',\n"
			"which is no longer supported; use 'merges' instead"));

	if (action != ACTION_NONE && total_argc != 2) {
		usage_with_options(builtin_rebase_usage,
				   builtin_rebase_options);
	}

	if (argc > 2)
		usage_with_options(builtin_rebase_usage,
				   builtin_rebase_options);

	if (keep_base) {
		if (options.onto_name)
			die(_("options '%s' and '%s' cannot be used together"), "--keep-base", "--onto");
		if (options.root)
			die(_("options '%s' and '%s' cannot be used together"), "--keep-base", "--root");
	}

	if (options.root && options.fork_point > 0)
		die(_("options '%s' and '%s' cannot be used together"), "--root", "--fork-point");

	if (action != ACTION_NONE && !in_progress)
		die(_("No rebase in progress?"));
	setenv(GIT_REFLOG_ACTION_ENVIRONMENT, "rebase", 0);

	if (action == ACTION_EDIT_TODO && !is_merge(&options))
		die(_("The --edit-todo action can only be used during "
		      "interactive rebase."));

	if (trace2_is_enabled()) {
		if (is_merge(&options))
			trace2_cmd_mode("interactive");
		else if (exec.nr)
			trace2_cmd_mode("interactive-exec");
		else
			trace2_cmd_mode(action_names[action]);
	}

	switch (action) {
	case ACTION_CONTINUE: {
		struct object_id head;
		struct lock_file lock_file = LOCK_INIT;
		int fd;

		options.action = "continue";
		set_reflog_action(&options);

		/* Sanity check */
		if (get_oid("HEAD", &head))
			die(_("Cannot read HEAD"));

		fd = hold_locked_index(&lock_file, 0);
		if (repo_read_index(the_repository) < 0)
			die(_("could not read index"));
		refresh_index(the_repository->index, REFRESH_QUIET, NULL, NULL,
			      NULL);
		if (0 <= fd)
			repo_update_index_if_able(the_repository, &lock_file);
		rollback_lock_file(&lock_file);

		if (has_unstaged_changes(the_repository, 1)) {
			puts(_("You must edit all merge conflicts and then\n"
			       "mark them as resolved using git add"));
			exit(1);
		}
		if (read_basic_state(&options))
			exit(1);
		goto run_rebase;
	}
	case ACTION_SKIP: {
		struct string_list merge_rr = STRING_LIST_INIT_DUP;

		options.action = "skip";
		set_reflog_action(&options);

		rerere_clear(the_repository, &merge_rr);
		string_list_clear(&merge_rr, 1);
		ropts.flags = RESET_HEAD_HARD;
		if (reset_head(the_repository, &ropts) < 0)
			die(_("could not discard worktree changes"));
		remove_branch_state(the_repository, 0);
		if (read_basic_state(&options))
			exit(1);
		goto run_rebase;
	}
	case ACTION_ABORT: {
		struct string_list merge_rr = STRING_LIST_INIT_DUP;
		options.action = "abort";
		set_reflog_action(&options);

		rerere_clear(the_repository, &merge_rr);
		string_list_clear(&merge_rr, 1);

		if (read_basic_state(&options))
			exit(1);
		ropts.oid = &options.orig_head;
		ropts.branch = options.head_name;
		ropts.flags = RESET_HEAD_HARD;
		ropts.default_reflog_action = DEFAULT_REFLOG_ACTION;
		if (reset_head(the_repository, &ropts) < 0)
			die(_("could not move back to %s"),
			    oid_to_hex(&options.orig_head));
		remove_branch_state(the_repository, 0);
		ret = finish_rebase(&options);
		goto cleanup;
	}
	case ACTION_QUIT: {
		save_autostash(state_dir_path("autostash", &options));
		if (options.type == REBASE_MERGE) {
			struct replay_opts replay = REPLAY_OPTS_INIT;

			replay.action = REPLAY_INTERACTIVE_REBASE;
			ret = sequencer_remove_state(&replay);
		} else {
			strbuf_reset(&buf);
			strbuf_addstr(&buf, options.state_dir);
			ret = remove_dir_recursively(&buf, 0);
			if (ret)
				error(_("could not remove '%s'"),
				       options.state_dir);
		}
		goto cleanup;
	}
	case ACTION_EDIT_TODO:
		options.action = "edit-todo";
		options.dont_finish_rebase = 1;
		goto run_rebase;
	case ACTION_SHOW_CURRENT_PATCH:
		options.action = "show-current-patch";
		options.dont_finish_rebase = 1;
		goto run_rebase;
	case ACTION_NONE:
		break;
	default:
		BUG("action: %d", action);
	}

	/* Make sure no rebase is in progress */
	if (in_progress) {
		const char *last_slash = strrchr(options.state_dir, '/');
		const char *state_dir_base =
			last_slash ? last_slash + 1 : options.state_dir;
		const char *cmd_live_rebase =
			"git rebase (--continue | --abort | --skip)";
		strbuf_reset(&buf);
		strbuf_addf(&buf, "rm -fr \"%s\"", options.state_dir);
		die(_("It seems that there is already a %s directory, and\n"
		      "I wonder if you are in the middle of another rebase.  "
		      "If that is the\n"
		      "case, please try\n\t%s\n"
		      "If that is not the case, please\n\t%s\n"
		      "and run me again.  I am stopping in case you still "
		      "have something\n"
		      "valuable there.\n"),
		    state_dir_base, cmd_live_rebase, buf.buf);
	}

	if ((options.flags & REBASE_INTERACTIVE_EXPLICIT) ||
	    (action != ACTION_NONE) ||
	    (exec.nr > 0) ||
	    options.autosquash) {
		allow_preemptive_ff = 0;
	}
	if (options.committer_date_is_author_date || options.ignore_date)
		options.flags |= REBASE_FORCE;

	for (i = 0; i < options.git_am_opts.nr; i++) {
		const char *option = options.git_am_opts.v[i], *p;
		if (!strcmp(option, "--whitespace=fix") ||
		    !strcmp(option, "--whitespace=strip"))
			allow_preemptive_ff = 0;
		else if (skip_prefix(option, "-C", &p)) {
			while (*p)
				if (!isdigit(*(p++)))
					die(_("switch `C' expects a "
					      "numerical value"));
		} else if (skip_prefix(option, "--whitespace=", &p)) {
			if (*p && strcmp(p, "warn") && strcmp(p, "nowarn") &&
			    strcmp(p, "error") && strcmp(p, "error-all"))
				die("Invalid whitespace option: '%s'", p);
		}
	}

	for (i = 0; i < exec.nr; i++)
		if (check_exec_cmd(exec.items[i].string))
			exit(1);

	if (!(options.flags & REBASE_NO_QUIET))
		strvec_push(&options.git_am_opts, "-q");

	if (options.empty != EMPTY_UNSPECIFIED)
		imply_merge(&options, "--empty");

	if (options.reapply_cherry_picks)
		imply_merge(&options, "--reapply-cherry-picks");

	if (gpg_sign)
		options.gpg_sign_opt = xstrfmt("-S%s", gpg_sign);

	if (exec.nr) {
		int i;

		imply_merge(&options, "--exec");

		strbuf_reset(&buf);
		for (i = 0; i < exec.nr; i++)
			strbuf_addf(&buf, "exec %s\n", exec.items[i].string);
		options.cmd = xstrdup(buf.buf);
	}

	if (rebase_merges) {
		if (!*rebase_merges)
			; /* default mode; do nothing */
		else if (!strcmp("rebase-cousins", rebase_merges))
			options.rebase_cousins = 1;
		else if (strcmp("no-rebase-cousins", rebase_merges))
			die(_("Unknown mode: %s"), rebase_merges);
		options.rebase_merges = 1;
		imply_merge(&options, "--rebase-merges");
	}

	if (options.type == REBASE_APPLY) {
		if (ignore_whitespace)
			strvec_push(&options.git_am_opts,
				    "--ignore-whitespace");
		if (options.committer_date_is_author_date)
			strvec_push(&options.git_am_opts,
				    "--committer-date-is-author-date");
		if (options.ignore_date)
			strvec_push(&options.git_am_opts, "--ignore-date");
	} else {
		/* REBASE_MERGE */
		if (ignore_whitespace) {
			string_list_append(&strategy_options,
					   "ignore-space-change");
		}
	}

	if (strategy_options.nr) {
		int i;

		if (!options.strategy)
			options.strategy = "ort";

		strbuf_reset(&buf);
		for (i = 0; i < strategy_options.nr; i++)
			strbuf_addf(&buf, " --%s",
				    strategy_options.items[i].string);
		options.strategy_opts = xstrdup(buf.buf);
	}

	if (options.strategy) {
		options.strategy = xstrdup(options.strategy);
		switch (options.type) {
		case REBASE_APPLY:
			die(_("--strategy requires --merge or --interactive"));
		case REBASE_MERGE:
			/* compatible */
			break;
		case REBASE_UNSPECIFIED:
			options.type = REBASE_MERGE;
			break;
		default:
			BUG("unhandled rebase type (%d)", options.type);
		}
	}

	if (options.type == REBASE_MERGE)
		imply_merge(&options, "--merge");

	if (options.root && !options.onto_name)
		imply_merge(&options, "--root without --onto");

	if (isatty(2) && options.flags & REBASE_NO_QUIET)
		strbuf_addstr(&options.git_format_patch_opt, " --progress");

	if (options.git_am_opts.nr || options.type == REBASE_APPLY) {
		/* all am options except -q are compatible only with --apply */
		for (i = options.git_am_opts.nr - 1; i >= 0; i--)
			if (strcmp(options.git_am_opts.v[i], "-q"))
				break;

		if (i >= 0) {
			if (is_merge(&options))
				die(_("apply options and merge options "
					  "cannot be used together"));
			else
				options.type = REBASE_APPLY;
		}
	}

	if (options.type == REBASE_UNSPECIFIED) {
		if (!strcmp(options.default_backend, "merge"))
			imply_merge(&options, "--merge");
		else if (!strcmp(options.default_backend, "apply"))
			options.type = REBASE_APPLY;
		else
			die(_("Unknown rebase backend: %s"),
			    options.default_backend);
	}

	if (options.type == REBASE_MERGE &&
	    !options.strategy &&
	    getenv("GIT_TEST_MERGE_ALGORITHM"))
		options.strategy = xstrdup(getenv("GIT_TEST_MERGE_ALGORITHM"));

	switch (options.type) {
	case REBASE_MERGE:
		options.state_dir = merge_dir();
		break;
	case REBASE_APPLY:
		options.state_dir = apply_dir();
		break;
	default:
		BUG("options.type was just set above; should be unreachable.");
	}

	if (options.empty == EMPTY_UNSPECIFIED) {
		if (options.flags & REBASE_INTERACTIVE_EXPLICIT)
			options.empty = EMPTY_ASK;
		else if (exec.nr > 0)
			options.empty = EMPTY_KEEP;
		else
			options.empty = EMPTY_DROP;
	}
	if (reschedule_failed_exec > 0 && !is_merge(&options))
		die(_("--reschedule-failed-exec requires "
		      "--exec or --interactive"));
	if (reschedule_failed_exec >= 0)
		options.reschedule_failed_exec = reschedule_failed_exec;

	if (options.signoff) {
		strvec_push(&options.git_am_opts, "--signoff");
		options.flags |= REBASE_FORCE;
	}

	if (!options.root) {
		if (argc < 1) {
			struct branch *branch;

			branch = branch_get(NULL);
			options.upstream_name = branch_get_upstream(branch,
								    NULL);
			if (!options.upstream_name)
				error_on_missing_default_upstream();
			if (options.fork_point < 0)
				options.fork_point = 1;
		} else {
			options.upstream_name = argv[0];
			argc--;
			argv++;
			if (!strcmp(options.upstream_name, "-"))
				options.upstream_name = "@{-1}";
		}
		options.upstream =
			lookup_commit_reference_by_name(options.upstream_name);
		if (!options.upstream)
			die(_("invalid upstream '%s'"), options.upstream_name);
		options.upstream_arg = options.upstream_name;
	} else {
		if (!options.onto_name) {
			if (commit_tree("", 0, the_hash_algo->empty_tree, NULL,
					&squash_onto, NULL, NULL) < 0)
				die(_("Could not create new root commit"));
			options.squash_onto = &squash_onto;
			options.onto_name = squash_onto_name =
				xstrdup(oid_to_hex(&squash_onto));
		} else
			options.root_with_onto = 1;

		options.upstream_name = NULL;
		options.upstream = NULL;
		if (argc > 1)
			usage_with_options(builtin_rebase_usage,
					   builtin_rebase_options);
		options.upstream_arg = "--root";
	}

	/*
	 * If the branch to rebase is given, that is the branch we will rebase
	 * branch_name -- branch/commit being rebased, or
	 * 		  HEAD (already detached)
	 * orig_head -- commit object name of tip of the branch before rebasing
	 * head_name -- refs/heads/<that-branch> or NULL (detached HEAD)
	 */
	if (argc == 1) {
		/* Is it "rebase other branchname" or "rebase other commit"? */
		branch_name = argv[0];
		options.switch_to = argv[0];

		/* Is it a local branch? */
		strbuf_reset(&buf);
		strbuf_addf(&buf, "refs/heads/%s", branch_name);
		if (!read_ref(buf.buf, &options.orig_head)) {
			die_if_checked_out(buf.buf, 1);
			options.head_name = xstrdup(buf.buf);
		/* If not is it a valid ref (branch or commit)? */
		} else {
			struct commit *commit =
				lookup_commit_reference_by_name(branch_name);
			if (!commit)
				die(_("no such branch/commit '%s'"),
				    branch_name);
			oidcpy(&options.orig_head, &commit->object.oid);
			options.head_name = NULL;
		}
	} else if (argc == 0) {
		/* Do not need to switch branches, we are already on it. */
		options.head_name =
			xstrdup_or_null(resolve_ref_unsafe("HEAD", 0, NULL,
					 &flags));
		if (!options.head_name)
			die(_("No such ref: %s"), "HEAD");
		if (flags & REF_ISSYMREF) {
			if (!skip_prefix(options.head_name,
					 "refs/heads/", &branch_name))
				branch_name = options.head_name;

		} else {
			FREE_AND_NULL(options.head_name);
			branch_name = "HEAD";
		}
		if (get_oid("HEAD", &options.orig_head))
			die(_("Could not resolve HEAD to a revision"));
	} else
		BUG("unexpected number of arguments left to parse");

	/* Make sure the branch to rebase onto is valid. */
	if (keep_base) {
		strbuf_reset(&buf);
		strbuf_addstr(&buf, options.upstream_name);
		strbuf_addstr(&buf, "...");
		strbuf_addstr(&buf, branch_name);
		options.onto_name = xstrdup(buf.buf);
	} else if (!options.onto_name)
		options.onto_name = options.upstream_name;
	if (strstr(options.onto_name, "...")) {
		if (get_oid_mb(options.onto_name, &merge_base) < 0) {
			if (keep_base)
				die(_("'%s': need exactly one merge base with branch"),
				    options.upstream_name);
			else
				die(_("'%s': need exactly one merge base"),
				    options.onto_name);
		}
		options.onto = lookup_commit_or_die(&merge_base,
						    options.onto_name);
	} else {
		options.onto =
			lookup_commit_reference_by_name(options.onto_name);
		if (!options.onto)
			die(_("Does not point to a valid commit '%s'"),
				options.onto_name);
	}

	if (options.fork_point > 0) {
		struct commit *head =
			lookup_commit_reference(the_repository,
						&options.orig_head);
		options.restrict_revision =
			get_fork_point(options.upstream_name, head);
	}

	if (repo_read_index(the_repository) < 0)
		die(_("could not read index"));

	if (options.autostash)
		create_autostash(the_repository,
				 state_dir_path("autostash", &options));


	if (require_clean_work_tree(the_repository, "rebase",
				    _("Please commit or stash them."), 1, 1)) {
		ret = -1;
		goto cleanup;
	}

	/*
	 * Now we are rebasing commits upstream..orig_head (or with --root,
	 * everything leading up to orig_head) on top of onto.
	 */

	/*
	 * Check if we are already based on onto with linear history,
	 * in which case we could fast-forward without replacing the commits
	 * with new commits recreated by replaying their changes.
	 *
	 * Note that can_fast_forward() initializes merge_base, so we have to
	 * call it before checking allow_preemptive_ff.
	 */
	if (can_fast_forward(options.onto, options.upstream, options.restrict_revision,
		    &options.orig_head, &merge_base) &&
	    allow_preemptive_ff) {
		int flag;

		if (!(options.flags & REBASE_FORCE)) {
			/* Lazily switch to the target branch if needed... */
			if (options.switch_to) {
				ret = checkout_up_to_date(&options);
				if (ret)
					goto cleanup;
			}

			if (!(options.flags & REBASE_NO_QUIET))
				; /* be quiet */
			else if (!strcmp(branch_name, "HEAD") &&
				 resolve_ref_unsafe("HEAD", 0, NULL, &flag))
				puts(_("HEAD is up to date."));
			else
				printf(_("Current branch %s is up to date.\n"),
				       branch_name);
			ret = finish_rebase(&options);
			goto cleanup;
		} else if (!(options.flags & REBASE_NO_QUIET))
			; /* be quiet */
		else if (!strcmp(branch_name, "HEAD") &&
			 resolve_ref_unsafe("HEAD", 0, NULL, &flag))
			puts(_("HEAD is up to date, rebase forced."));
		else
			printf(_("Current branch %s is up to date, rebase "
				 "forced.\n"), branch_name);
	}

	/* If a hook exists, give it a chance to interrupt*/
	if (!ok_to_skip_pre_rebase &&
	    run_hooks_l("pre-rebase", options.upstream_arg,
			argc ? argv[0] : NULL, NULL))
		die(_("The pre-rebase hook refused to rebase."));

	if (options.flags & REBASE_DIFFSTAT) {
		struct diff_options opts;

		if (options.flags & REBASE_VERBOSE) {
			if (is_null_oid(&merge_base))
				printf(_("Changes to %s:\n"),
				       oid_to_hex(&options.onto->object.oid));
			else
				printf(_("Changes from %s to %s:\n"),
				       oid_to_hex(&merge_base),
				       oid_to_hex(&options.onto->object.oid));
		}

		/* We want color (if set), but no pager */
		diff_setup(&opts);
		opts.stat_width = -1; /* use full terminal width */
		opts.stat_graph_width = -1; /* respect statGraphWidth config */
		opts.output_format |=
			DIFF_FORMAT_SUMMARY | DIFF_FORMAT_DIFFSTAT;
		opts.detect_rename = DIFF_DETECT_RENAME;
		diff_setup_done(&opts);
		diff_tree_oid(is_null_oid(&merge_base) ?
			      the_hash_algo->empty_tree : &merge_base,
			      &options.onto->object.oid, "", &opts);
		diffcore_std(&opts);
		diff_flush(&opts);
	}

	if (is_merge(&options))
		goto run_rebase;

	/* Detach HEAD and reset the tree */
	if (options.flags & REBASE_NO_QUIET)
		printf(_("First, rewinding head to replay your work on top of "
			 "it...\n"));

	strbuf_addf(&msg, "%s: checkout %s",
		    getenv(GIT_REFLOG_ACTION_ENVIRONMENT), options.onto_name);
	ropts.oid = &options.onto->object.oid;
	ropts.orig_head = &options.orig_head,
	ropts.flags = RESET_HEAD_DETACH | RESET_ORIG_HEAD |
			RESET_HEAD_RUN_POST_CHECKOUT_HOOK;
	ropts.head_msg = msg.buf;
	ropts.default_reflog_action = DEFAULT_REFLOG_ACTION;
	if (reset_head(the_repository, &ropts))
		die(_("Could not detach HEAD"));
	strbuf_release(&msg);

	/*
	 * If the onto is a proper descendant of the tip of the branch, then
	 * we just fast-forwarded.
	 */
	strbuf_reset(&msg);
	if (oideq(&merge_base, &options.orig_head)) {
		printf(_("Fast-forwarded %s to %s.\n"),
			branch_name, options.onto_name);
		strbuf_addf(&msg, "rebase finished: %s onto %s",
			options.head_name ? options.head_name : "detached HEAD",
			oid_to_hex(&options.onto->object.oid));
		memset(&ropts, 0, sizeof(ropts));
		ropts.branch = options.head_name;
		ropts.flags = RESET_HEAD_REFS_ONLY;
		ropts.head_msg = msg.buf;
		reset_head(the_repository, &ropts);
		strbuf_release(&msg);
		ret = finish_rebase(&options);
		goto cleanup;
	}

	strbuf_addf(&revisions, "%s..%s",
		    options.root ? oid_to_hex(&options.onto->object.oid) :
		    (options.restrict_revision ?
		     oid_to_hex(&options.restrict_revision->object.oid) :
		     oid_to_hex(&options.upstream->object.oid)),
		    oid_to_hex(&options.orig_head));

	options.revisions = revisions.buf;

run_rebase:
	ret = run_specific_rebase(&options, action);

cleanup:
	strbuf_release(&buf);
	strbuf_release(&revisions);
	free(options.head_name);
	free(options.gpg_sign_opt);
	free(options.cmd);
	free(options.strategy);
	strbuf_release(&options.git_format_patch_opt);
	free(squash_onto_name);
	return !!ret;
}