dfa6dd530f3dd246de866ca5db5174a549cf31f5
   1#include "cache.h"
   2#include "commit.h"
   3#include "sequencer.h"
   4#include "rebase-interactive.h"
   5#include "strbuf.h"
   6#include "commit-slab.h"
   7#include "config.h"
   8
   9enum missing_commit_check_level {
  10        MISSING_COMMIT_CHECK_IGNORE = 0,
  11        MISSING_COMMIT_CHECK_WARN,
  12        MISSING_COMMIT_CHECK_ERROR
  13};
  14
  15static enum missing_commit_check_level get_missing_commit_check_level(void)
  16{
  17        const char *value;
  18
  19        if (git_config_get_value("rebase.missingcommitscheck", &value) ||
  20                        !strcasecmp("ignore", value))
  21                return MISSING_COMMIT_CHECK_IGNORE;
  22        if (!strcasecmp("warn", value))
  23                return MISSING_COMMIT_CHECK_WARN;
  24        if (!strcasecmp("error", value))
  25                return MISSING_COMMIT_CHECK_ERROR;
  26        warning(_("unrecognized setting %s for option "
  27                  "rebase.missingCommitsCheck. Ignoring."), value);
  28        return MISSING_COMMIT_CHECK_IGNORE;
  29}
  30
  31void append_todo_help(unsigned edit_todo, unsigned keep_empty,
  32                      struct strbuf *buf)
  33{
  34        const char *msg = _("\nCommands:\n"
  35"p, pick <commit> = use commit\n"
  36"r, reword <commit> = use commit, but edit the commit message\n"
  37"e, edit <commit> = use commit, but stop for amending\n"
  38"s, squash <commit> = use commit, but meld into previous commit\n"
  39"f, fixup <commit> = like \"squash\", but discard this commit's log message\n"
  40"x, exec <command> = run command (the rest of the line) using shell\n"
  41"b, break = stop here (continue rebase later with 'git rebase --continue')\n"
  42"d, drop <commit> = remove commit\n"
  43"l, label <label> = label current HEAD with a name\n"
  44"t, reset <label> = reset HEAD to a label\n"
  45"m, merge [-C <commit> | -c <commit>] <label> [# <oneline>]\n"
  46".       create a merge commit using the original merge commit's\n"
  47".       message (or the oneline, if no original merge commit was\n"
  48".       specified). Use -c <commit> to reword the commit message.\n"
  49"\n"
  50"These lines can be re-ordered; they are executed from top to bottom.\n");
  51
  52        strbuf_add_commented_lines(buf, msg, strlen(msg));
  53
  54        if (get_missing_commit_check_level() == MISSING_COMMIT_CHECK_ERROR)
  55                msg = _("\nDo not remove any line. Use 'drop' "
  56                         "explicitly to remove a commit.\n");
  57        else
  58                msg = _("\nIf you remove a line here "
  59                         "THAT COMMIT WILL BE LOST.\n");
  60
  61        strbuf_add_commented_lines(buf, msg, strlen(msg));
  62
  63        if (edit_todo)
  64                msg = _("\nYou are editing the todo file "
  65                        "of an ongoing interactive rebase.\n"
  66                        "To continue rebase after editing, run:\n"
  67                        "    git rebase --continue\n\n");
  68        else
  69                msg = _("\nHowever, if you remove everything, "
  70                        "the rebase will be aborted.\n\n");
  71
  72        strbuf_add_commented_lines(buf, msg, strlen(msg));
  73
  74        if (!keep_empty) {
  75                msg = _("Note that empty commits are commented out");
  76                strbuf_add_commented_lines(buf, msg, strlen(msg));
  77        }
  78}
  79
  80int edit_todo_list(struct repository *r, unsigned flags)
  81{
  82        struct strbuf buf = STRBUF_INIT;
  83        const char *todo_file = rebase_path_todo();
  84
  85        if (strbuf_read_file(&buf, todo_file, 0) < 0)
  86                return error_errno(_("could not read '%s'."), todo_file);
  87
  88        strbuf_stripspace(&buf, 1);
  89        if (write_message(buf.buf, buf.len, todo_file, 0)) {
  90                strbuf_release(&buf);
  91                return -1;
  92        }
  93
  94        strbuf_release(&buf);
  95
  96        transform_todo_file(r, flags | TODO_LIST_SHORTEN_IDS);
  97
  98        if (strbuf_read_file(&buf, todo_file, 0) < 0)
  99                return error_errno(_("could not read '%s'."), todo_file);
 100
 101        append_todo_help(1, 0, &buf);
 102        if (write_message(buf.buf, buf.len, todo_file, 0)) {
 103                strbuf_release(&buf);
 104                return -1;
 105        }
 106
 107        strbuf_release(&buf);
 108
 109        if (launch_sequence_editor(todo_file, NULL, NULL))
 110                return -1;
 111
 112        transform_todo_file(r, flags & ~(TODO_LIST_SHORTEN_IDS));
 113
 114        return 0;
 115}
 116
 117define_commit_slab(commit_seen, unsigned char);
 118/*
 119 * Check if the user dropped some commits by mistake
 120 * Behaviour determined by rebase.missingCommitsCheck.
 121 * Check if there is an unrecognized command or a
 122 * bad SHA-1 in a command.
 123 */
 124int todo_list_check(struct todo_list *old_todo, struct todo_list *new_todo)
 125{
 126        enum missing_commit_check_level check_level = get_missing_commit_check_level();
 127        struct strbuf missing = STRBUF_INIT;
 128        int res = 0, i;
 129        struct commit_seen commit_seen;
 130
 131        init_commit_seen(&commit_seen);
 132
 133        if (check_level == MISSING_COMMIT_CHECK_IGNORE)
 134                goto leave_check;
 135
 136        /* Mark the commits in git-rebase-todo as seen */
 137        for (i = 0; i < new_todo->nr; i++) {
 138                struct commit *commit = new_todo->items[i].commit;
 139                if (commit)
 140                        *commit_seen_at(&commit_seen, commit) = 1;
 141        }
 142
 143        /* Find commits in git-rebase-todo.backup yet unseen */
 144        for (i = old_todo->nr - 1; i >= 0; i--) {
 145                struct todo_item *item = old_todo->items + i;
 146                struct commit *commit = item->commit;
 147                if (commit && !*commit_seen_at(&commit_seen, commit)) {
 148                        strbuf_addf(&missing, " - %s %.*s\n",
 149                                    find_unique_abbrev(&commit->object.oid, DEFAULT_ABBREV),
 150                                    item->arg_len,
 151                                    todo_item_get_arg(old_todo, item));
 152                        *commit_seen_at(&commit_seen, commit) = 1;
 153                }
 154        }
 155
 156        /* Warn about missing commits */
 157        if (!missing.len)
 158                goto leave_check;
 159
 160        if (check_level == MISSING_COMMIT_CHECK_ERROR)
 161                res = 1;
 162
 163        fprintf(stderr,
 164                _("Warning: some commits may have been dropped accidentally.\n"
 165                "Dropped commits (newer to older):\n"));
 166
 167        /* Make the list user-friendly and display */
 168        fputs(missing.buf, stderr);
 169        strbuf_release(&missing);
 170
 171        fprintf(stderr, _("To avoid this message, use \"drop\" to "
 172                "explicitly remove a commit.\n\n"
 173                "Use 'git config rebase.missingCommitsCheck' to change "
 174                "the level of warnings.\n"
 175                "The possible behaviours are: ignore, warn, error.\n\n"));
 176
 177leave_check:
 178        clear_commit_seen(&commit_seen);
 179        return res;
 180}