|
| 1 | +#include "builtin.h" |
| 2 | +#include "cache.h" |
| 3 | +#include "config.h" |
| 4 | +#include "parse-options.h" |
| 5 | +#include "sequencer.h" |
| 6 | +#include "rebase-interactive.h" |
| 7 | +#include "argv-array.h" |
| 8 | +#include "refs.h" |
| 9 | +#include "rerere.h" |
| 10 | +#include "run-command.h" |
| 11 | + |
| 12 | +static GIT_PATH_FUNC(path_state_dir, "rebase-merge/") |
| 13 | +static GIT_PATH_FUNC(path_squash_onto, "rebase-merge/squash-onto") |
| 14 | +static GIT_PATH_FUNC(path_interactive, "rebase-merge/interactive") |
| 15 | + |
| 16 | +static int get_revision_ranges(const char *upstream, const char *onto, |
| 17 | + const char **head_hash, |
| 18 | + char **revisions, char **shortrevisions) |
| 19 | +{ |
| 20 | + const char *base_rev = upstream ? upstream : onto, *shorthead; |
| 21 | + struct object_id orig_head; |
| 22 | + |
| 23 | + if (get_oid("HEAD", &orig_head)) |
| 24 | + return error(_("no HEAD?")); |
| 25 | + |
| 26 | + *head_hash = find_unique_abbrev(&orig_head, GIT_MAX_HEXSZ); |
| 27 | + *revisions = xstrfmt("%s...%s", base_rev, *head_hash); |
| 28 | + |
| 29 | + shorthead = find_unique_abbrev(&orig_head, DEFAULT_ABBREV); |
| 30 | + |
| 31 | + if (upstream) { |
| 32 | + const char *shortrev; |
| 33 | + struct object_id rev_oid; |
| 34 | + |
| 35 | + get_oid(base_rev, &rev_oid); |
| 36 | + shortrev = find_unique_abbrev(&rev_oid, DEFAULT_ABBREV); |
| 37 | + |
| 38 | + *shortrevisions = xstrfmt("%s..%s", shortrev, shorthead); |
| 39 | + } else |
| 40 | + *shortrevisions = xstrdup(shorthead); |
| 41 | + |
| 42 | + return 0; |
| 43 | +} |
| 44 | + |
| 45 | +static int init_basic_state(struct replay_opts *opts, const char *head_name, |
| 46 | + const char *onto, const char *orig_head) |
| 47 | +{ |
| 48 | + FILE *interactive; |
| 49 | + |
| 50 | + if (!is_directory(path_state_dir()) && mkdir_in_gitdir(path_state_dir())) |
| 51 | + return error_errno(_("could not create temporary %s"), path_state_dir()); |
| 52 | + |
| 53 | + delete_reflog("REBASE_HEAD"); |
| 54 | + |
| 55 | + interactive = fopen(path_interactive(), "w"); |
| 56 | + if (!interactive) |
| 57 | + return error_errno(_("could not mark as interactive")); |
| 58 | + fclose(interactive); |
| 59 | + |
| 60 | + return write_basic_state(opts, head_name, onto, orig_head); |
| 61 | +} |
| 62 | + |
| 63 | +static int do_interactive_rebase(struct replay_opts *opts, unsigned flags, |
| 64 | + const char *switch_to, const char *upstream, |
| 65 | + const char *onto, const char *onto_name, |
| 66 | + const char *squash_onto, const char *head_name, |
| 67 | + const char *restrict_revision, char *raw_strategies, |
| 68 | + const char *cmd, unsigned autosquash) |
| 69 | +{ |
| 70 | + int ret; |
| 71 | + const char *head_hash = NULL; |
| 72 | + char *revisions = NULL, *shortrevisions = NULL; |
| 73 | + struct argv_array make_script_args = ARGV_ARRAY_INIT; |
| 74 | + FILE *todo_list; |
| 75 | + |
| 76 | + if (prepare_branch_to_be_rebased(opts, switch_to)) |
| 77 | + return -1; |
| 78 | + |
| 79 | + if (get_revision_ranges(upstream, onto, &head_hash, |
| 80 | + &revisions, &shortrevisions)) |
| 81 | + return -1; |
| 82 | + |
| 83 | + if (raw_strategies) |
| 84 | + parse_strategy_opts(opts, raw_strategies); |
| 85 | + |
| 86 | + if (init_basic_state(opts, head_name, onto, head_hash)) { |
| 87 | + free(revisions); |
| 88 | + free(shortrevisions); |
| 89 | + |
| 90 | + return -1; |
| 91 | + } |
| 92 | + |
| 93 | + if (!upstream && squash_onto) |
| 94 | + write_file(path_squash_onto(), "%s\n", squash_onto); |
| 95 | + |
| 96 | + todo_list = fopen(rebase_path_todo(), "w"); |
| 97 | + if (!todo_list) { |
| 98 | + free(revisions); |
| 99 | + free(shortrevisions); |
| 100 | + |
| 101 | + return error_errno(_("could not open %s"), rebase_path_todo()); |
| 102 | + } |
| 103 | + |
| 104 | + argv_array_pushl(&make_script_args, "", revisions, NULL); |
| 105 | + if (restrict_revision) |
| 106 | + argv_array_push(&make_script_args, restrict_revision); |
| 107 | + |
| 108 | + ret = sequencer_make_script(todo_list, |
| 109 | + make_script_args.argc, make_script_args.argv, |
| 110 | + flags); |
| 111 | + fclose(todo_list); |
| 112 | + |
| 113 | + if (ret) |
| 114 | + error(_("could not generate todo list")); |
| 115 | + else { |
| 116 | + discard_cache(); |
| 117 | + ret = complete_action(opts, flags, shortrevisions, onto_name, onto, |
| 118 | + head_hash, cmd, autosquash); |
| 119 | + } |
| 120 | + |
| 121 | + free(revisions); |
| 122 | + free(shortrevisions); |
| 123 | + argv_array_clear(&make_script_args); |
| 124 | + |
| 125 | + return ret; |
| 126 | +} |
| 127 | + |
| 128 | +static const char * const builtin_rebase_interactive_usage[] = { |
| 129 | + N_("git rebase--interactive [<options>]"), |
| 130 | + NULL |
| 131 | +}; |
| 132 | + |
| 133 | +int cmd_rebase__interactive(int argc, const char **argv, const char *prefix) |
| 134 | +{ |
| 135 | + struct replay_opts opts = REPLAY_OPTS_INIT; |
| 136 | + unsigned flags = 0, keep_empty = 0, rebase_merges = 0, autosquash = 0; |
| 137 | + int abbreviate_commands = 0, rebase_cousins = -1; |
| 138 | + const char *onto = NULL, *onto_name = NULL, *restrict_revision = NULL, |
| 139 | + *squash_onto = NULL, *upstream = NULL, *head_name = NULL, |
| 140 | + *switch_to = NULL, *cmd = NULL; |
| 141 | + char *raw_strategies = NULL; |
| 142 | + struct option options[] = { |
| 143 | + OPT_BOOL(0, "ff", &opts.allow_ff, N_("allow fast-forward")), |
| 144 | + OPT_BOOL(0, "keep-empty", &keep_empty, N_("keep empty commits")), |
| 145 | + OPT_BOOL(0, "allow-empty-message", &opts.allow_empty_message, |
| 146 | + N_("allow commits with empty messages")), |
| 147 | + OPT_BOOL(0, "rebase-merges", &rebase_merges, N_("rebase merge commits")), |
| 148 | + OPT_BOOL(0, "rebase-cousins", &rebase_cousins, |
| 149 | + N_("keep original branch points of cousins")), |
| 150 | + OPT_BOOL(0, "autosquash", &autosquash, |
| 151 | + N_("move commits that begin with squash!/fixup!")), |
| 152 | + OPT_BOOL(0, "signoff", &opts.signoff, N_("sign commits")), |
| 153 | + OPT__VERBOSE(&opts.verbose, N_("be verbose")), |
| 154 | + OPT_STRING(0, "onto", &onto, N_("onto"), N_("onto")), |
| 155 | + OPT_STRING(0, "restrict-revision", &restrict_revision, |
| 156 | + N_("restrict-revision"), N_("restrict revision")), |
| 157 | + OPT_STRING(0, "squash-onto", &squash_onto, N_("squash-onto"), |
| 158 | + N_("squash onto")), |
| 159 | + OPT_STRING(0, "upstream", &upstream, N_("upstream"), |
| 160 | + N_("the upstream commit")), |
| 161 | + OPT_STRING(0, "head-name", &head_name, N_("head-name"), N_("head name")), |
| 162 | + OPT_STRING('S', "gpg-sign", &opts.gpg_sign, N_("gpg-sign"), |
| 163 | + N_("GPG-sign commits")), |
| 164 | + OPT_STRING(0, "strategy", &opts.strategy, N_("strategy"), |
| 165 | + N_("rebase strategy")), |
| 166 | + OPT_STRING(0, "strategy-opts", &raw_strategies, N_("strategy-opts"), |
| 167 | + N_("strategy options")), |
| 168 | + OPT_STRING(0, "switch-to", &switch_to, N_("switch-to"), |
| 169 | + N_("the branch or commit to checkout")), |
| 170 | + OPT_STRING(0, "onto-name", &onto_name, N_("onto-name"), N_("onto name")), |
| 171 | + OPT_STRING(0, "cmd", &cmd, N_("cmd"), N_("the command to run")), |
| 172 | + OPT_RERERE_AUTOUPDATE(&opts.allow_rerere_auto), |
| 173 | + OPT_END() |
| 174 | + }; |
| 175 | + |
| 176 | + sequencer_init_config(&opts); |
| 177 | + git_config_get_bool("rebase.abbreviatecommands", &abbreviate_commands); |
| 178 | + |
| 179 | + opts.action = REPLAY_INTERACTIVE_REBASE; |
| 180 | + opts.allow_ff = 1; |
| 181 | + opts.allow_empty = 1; |
| 182 | + |
| 183 | + if (argc == 1) |
| 184 | + usage_with_options(builtin_rebase_interactive_usage, options); |
| 185 | + |
| 186 | + argc = parse_options(argc, argv, NULL, options, |
| 187 | + builtin_rebase_interactive_usage, PARSE_OPT_KEEP_ARGV0); |
| 188 | + |
| 189 | + opts.gpg_sign = xstrdup_or_null(opts.gpg_sign); |
| 190 | + |
| 191 | + flags |= keep_empty ? TODO_LIST_KEEP_EMPTY : 0; |
| 192 | + flags |= abbreviate_commands ? TODO_LIST_ABBREVIATE_CMDS : 0; |
| 193 | + flags |= rebase_merges ? TODO_LIST_REBASE_MERGES : 0; |
| 194 | + flags |= rebase_cousins > 0 ? TODO_LIST_REBASE_COUSINS : 0; |
| 195 | + |
| 196 | + if (rebase_cousins >= 0 && !rebase_merges) |
| 197 | + warning(_("--[no-]rebase-cousins has no effect without " |
| 198 | + "--rebase-merges")); |
| 199 | + |
| 200 | + if (!onto && !upstream) |
| 201 | + die(_("a base commit must be provided with --upstream or --onto")); |
| 202 | + |
| 203 | + return !!do_interactive_rebase(&opts, flags, switch_to, upstream, onto, |
| 204 | + onto_name, squash_onto, head_name, restrict_revision, |
| 205 | + raw_strategies, cmd, autosquash); |
| 206 | +} |
0 commit comments