rebase-interactive.c 7.8 KB

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