]> git.ipfire.org Git - thirdparty/git.git/blobdiff - sequencer.c
rebase -i: Handle "combination of <n> commits" with GETTEXT_POISON
[thirdparty/git.git] / sequencer.c
index e9baaf59bd954279970367631c930fe051712cda..3ce45fce80dd78e27d6d2be46dd1c4a6c6d65c81 100644 (file)
@@ -74,13 +74,6 @@ static GIT_PATH_FUNC(rebase_path_message, "rebase-merge/message")
  * previous commit and from the first squash/fixup commit are written
  * to it. The commit message for each subsequent squash/fixup commit
  * is appended to the file as it is processed.
- *
- * The first line of the file is of the form
- *     # This is a combination of $count commits.
- * where $count is the number of commits whose messages have been
- * written to the file so far (including the initial "pick" commit).
- * Each time that a commit message is processed, this line is read and
- * updated. It is deleted just before the combined commit is made.
  */
 static GIT_PATH_FUNC(rebase_path_squash_msg, "rebase-merge/message-squash")
 /*
@@ -91,6 +84,11 @@ static GIT_PATH_FUNC(rebase_path_squash_msg, "rebase-merge/message-squash")
  * commit without opening the editor.)
  */
 static GIT_PATH_FUNC(rebase_path_fixup_msg, "rebase-merge/message-fixup")
+/*
+ * This file contains the list fixup/squash commands that have been
+ * accumulated into message-fixup or message-squash so far.
+ */
+static GIT_PATH_FUNC(rebase_path_current_fixups, "rebase-merge/current-fixups")
 /*
  * A script to set the GIT_AUTHOR_NAME, GIT_AUTHOR_EMAIL, and
  * GIT_AUTHOR_DATE that will be used for the commit that is currently
@@ -252,6 +250,7 @@ int sequencer_remove_state(struct replay_opts *opts)
        for (i = 0; i < opts->xopts_nr; i++)
                free(opts->xopts[i]);
        free(opts->xopts);
+       strbuf_release(&opts->current_fixups);
 
        strbuf_addstr(&dir, get_dir(opts));
        remove_dir_recursively(&dir, 0);
@@ -339,7 +338,7 @@ static void print_advice(int show_hint, struct replay_opts *opts)
 static int write_message(const void *buf, size_t len, const char *filename,
                         int append_eol)
 {
-       static struct lock_file msg_file;
+       struct lock_file msg_file = LOCK_INIT;
 
        int msg_fd = hold_lock_file_for_update(&msg_file, filename, 0);
        if (msg_fd < 0)
@@ -352,10 +351,8 @@ static int write_message(const void *buf, size_t len, const char *filename,
                rollback_lock_file(&msg_file);
                return error_errno(_("could not write eol to '%s'"), filename);
        }
-       if (commit_lock_file(&msg_file) < 0) {
-               rollback_lock_file(&msg_file);
-               return error(_("failed to finalize '%s'."), filename);
-       }
+       if (commit_lock_file(&msg_file) < 0)
+               return error(_("failed to finalize '%s'"), filename);
 
        return 0;
 }
@@ -485,7 +482,7 @@ static int do_recursive_merge(struct commit *base, struct commit *next,
        struct tree *result, *next_tree, *base_tree, *head_tree;
        int clean;
        char **xopt;
-       static struct lock_file index_lock;
+       struct lock_file index_lock = LOCK_INIT;
 
        if (hold_locked_index(&index_lock, LOCK_REPORT_ON_ERROR) < 0)
                return -1;
@@ -514,18 +511,19 @@ static int do_recursive_merge(struct commit *base, struct commit *next,
                fputs(o.obuf.buf, stdout);
        strbuf_release(&o.obuf);
        diff_warn_rename_limit("merge.renamelimit", o.needed_rename_limit, 0);
-       if (clean < 0)
+       if (clean < 0) {
+               rollback_lock_file(&index_lock);
                return clean;
+       }
 
-       if (active_cache_changed &&
-           write_locked_index(&the_index, &index_lock, COMMIT_LOCK))
+       if (write_locked_index(&the_index, &index_lock,
+                              COMMIT_LOCK | SKIP_IF_UNCHANGED))
                /*
                 * TRANSLATORS: %s will be "revert", "cherry-pick" or
                 * "rebase -i".
                 */
                return error(_("%s: Unable to write new index file"),
                        _(action_name(opts)));
-       rollback_lock_file(&index_lock);
 
        if (!clean)
                append_conflicts_hint(msgbuf);
@@ -1329,34 +1327,23 @@ static int update_squash_messages(enum todo_command command,
                struct commit *commit, struct replay_opts *opts)
 {
        struct strbuf buf = STRBUF_INIT;
-       int count, res;
+       int res;
        const char *message, *body;
 
-       if (file_exists(rebase_path_squash_msg())) {
+       if (opts->current_fixup_count > 0) {
                struct strbuf header = STRBUF_INIT;
-               char *eol, *p;
+               char *eol;
 
-               if (strbuf_read_file(&buf, rebase_path_squash_msg(), 2048) <= 0)
+               if (strbuf_read_file(&buf, rebase_path_squash_msg(), 9) <= 0)
                        return error(_("could not read '%s'"),
                                rebase_path_squash_msg());
 
-               p = buf.buf + 1;
-               eol = strchrnul(buf.buf, '\n');
-               if (buf.buf[0] != comment_line_char ||
-                   (p += strcspn(p, "0123456789\n")) == eol)
-                       return error(_("unexpected 1st line of squash message:"
-                                      "\n\n\t%.*s"),
-                                    (int)(eol - buf.buf), buf.buf);
-               count = strtol(p, NULL, 10);
-
-               if (count < 1)
-                       return error(_("invalid 1st line of squash message:\n"
-                                      "\n\t%.*s"),
-                                    (int)(eol - buf.buf), buf.buf);
+               eol = buf.buf[0] != comment_line_char ?
+                       buf.buf : strchrnul(buf.buf, '\n');
 
                strbuf_addf(&header, "%c ", comment_line_char);
-               strbuf_addf(&header,
-                           _("This is a combination of %d commits."), ++count);
+               strbuf_addf(&header, _("This is a combination of %d commits."),
+                           opts->current_fixup_count + 2);
                strbuf_splice(&buf, 0, eol - buf.buf, header.buf, header.len);
                strbuf_release(&header);
        } else {
@@ -1379,10 +1366,8 @@ static int update_squash_messages(enum todo_command command,
                                     rebase_path_fixup_msg());
                }
 
-               count = 2;
                strbuf_addf(&buf, "%c ", comment_line_char);
-               strbuf_addf(&buf, _("This is a combination of %d commits."),
-                           count);
+               strbuf_addf(&buf, _("This is a combination of %d commits."), 2);
                strbuf_addf(&buf, "\n%c ", comment_line_char);
                strbuf_addstr(&buf, _("This is the 1st commit message:"));
                strbuf_addstr(&buf, "\n\n");
@@ -1399,13 +1384,14 @@ static int update_squash_messages(enum todo_command command,
        if (command == TODO_SQUASH) {
                unlink(rebase_path_fixup_msg());
                strbuf_addf(&buf, "\n%c ", comment_line_char);
-               strbuf_addf(&buf, _("This is the commit message #%d:"), count);
+               strbuf_addf(&buf, _("This is the commit message #%d:"),
+                           ++opts->current_fixup_count);
                strbuf_addstr(&buf, "\n\n");
                strbuf_addstr(&buf, body);
        } else if (command == TODO_FIXUP) {
                strbuf_addf(&buf, "\n%c ", comment_line_char);
                strbuf_addf(&buf, _("The commit message #%d will be skipped:"),
-                           count);
+                           ++opts->current_fixup_count);
                strbuf_addstr(&buf, "\n\n");
                strbuf_add_commented_lines(&buf, body, strlen(body));
        } else
@@ -1414,6 +1400,17 @@ static int update_squash_messages(enum todo_command command,
 
        res = write_message(buf.buf, buf.len, rebase_path_squash_msg(), 0);
        strbuf_release(&buf);
+
+       if (!res) {
+               strbuf_addf(&opts->current_fixups, "%s%s %s",
+                           opts->current_fixups.len ? "\n" : "",
+                           command_to_string(command),
+                           oid_to_hex(&commit->object.oid));
+               res = write_message(opts->current_fixups.buf,
+                                   opts->current_fixups.len,
+                                   rebase_path_current_fixups(), 0);
+       }
+
        return res;
 }
 
@@ -1676,6 +1673,9 @@ fast_forward_edit:
        if (!res && final_fixup) {
                unlink(rebase_path_fixup_msg());
                unlink(rebase_path_squash_msg());
+               unlink(rebase_path_current_fixups());
+               strbuf_reset(&opts->current_fixups);
+               opts->current_fixup_count = 0;
        }
 
 leave:
@@ -1705,7 +1705,7 @@ static int prepare_revs(struct replay_opts *opts)
 
 static int read_and_refresh_cache(struct replay_opts *opts)
 {
-       static struct lock_file index_lock;
+       struct lock_file index_lock = LOCK_INIT;
        int index_fd = hold_locked_index(&index_lock, 0);
        if (read_index_preload(&the_index, NULL) < 0) {
                rollback_lock_file(&index_lock);
@@ -1713,13 +1713,13 @@ static int read_and_refresh_cache(struct replay_opts *opts)
                        _(action_name(opts)));
        }
        refresh_index(&the_index, REFRESH_QUIET|REFRESH_UNMERGED, NULL, NULL, NULL);
-       if (the_index.cache_changed && index_fd >= 0) {
-               if (write_locked_index(&the_index, &index_lock, COMMIT_LOCK)) {
+       if (index_fd >= 0) {
+               if (write_locked_index(&the_index, &index_lock,
+                                      COMMIT_LOCK | SKIP_IF_UNCHANGED)) {
                        return error(_("git %s: failed to refresh the index"),
                                _(action_name(opts)));
                }
        }
-       rollback_lock_file(&index_lock);
        return 0;
 }
 
@@ -1869,22 +1869,31 @@ static int count_commands(struct todo_list *todo_list)
        return count;
 }
 
+static ssize_t strbuf_read_file_or_whine(struct strbuf *sb, const char *path)
+{
+       int fd;
+       ssize_t len;
+
+       fd = open(path, O_RDONLY);
+       if (fd < 0)
+               return error_errno(_("could not open '%s'"), path);
+       len = strbuf_read(sb, fd, 0);
+       close(fd);
+       if (len < 0)
+               return error(_("could not read '%s'."), path);
+       return len;
+}
+
 static int read_populate_todo(struct todo_list *todo_list,
                        struct replay_opts *opts)
 {
        struct stat st;
        const char *todo_file = get_todo_path(opts);
-       int fd, res;
+       int res;
 
        strbuf_reset(&todo_list->buf);
-       fd = open(todo_file, O_RDONLY);
-       if (fd < 0)
-               return error_errno(_("could not open '%s'"), todo_file);
-       if (strbuf_read(&todo_list->buf, fd, 0) < 0) {
-               close(fd);
-               return error(_("could not read '%s'."), todo_file);
-       }
-       close(fd);
+       if (strbuf_read_file_or_whine(&todo_list->buf, todo_file) < 0)
+               return -1;
 
        res = stat(todo_file, &st);
        if (res)
@@ -2038,6 +2047,16 @@ static int read_populate_opts(struct replay_opts *opts)
                read_strategy_opts(opts, &buf);
                strbuf_release(&buf);
 
+               if (read_oneliner(&opts->current_fixups,
+                                 rebase_path_current_fixups(), 1)) {
+                       const char *p = opts->current_fixups.buf;
+                       opts->current_fixup_count = 1;
+                       while ((p = strchr(p, '\n'))) {
+                               opts->current_fixup_count++;
+                               p++;
+                       }
+               }
+
                return 0;
        }
 
@@ -2099,16 +2118,14 @@ static int create_seq_dir(void)
 
 static int save_head(const char *head)
 {
-       static struct lock_file head_lock;
+       struct lock_file head_lock = LOCK_INIT;
        struct strbuf buf = STRBUF_INIT;
        int fd;
        ssize_t written;
 
        fd = hold_lock_file_for_update(&head_lock, git_path_head_file(), 0);
-       if (fd < 0) {
-               rollback_lock_file(&head_lock);
+       if (fd < 0)
                return error_errno(_("could not lock HEAD"));
-       }
        strbuf_addf(&buf, "%s\n", head);
        written = write_in_full(fd, buf.buf, buf.len);
        strbuf_release(&buf);
@@ -2117,10 +2134,8 @@ static int save_head(const char *head)
                return error_errno(_("could not write to '%s'"),
                                   git_path_head_file());
        }
-       if (commit_lock_file(&head_lock) < 0) {
-               rollback_lock_file(&head_lock);
-               return error(_("failed to finalize '%s'."), git_path_head_file());
-       }
+       if (commit_lock_file(&head_lock) < 0)
+               return error(_("failed to finalize '%s'"), git_path_head_file());
        return 0;
 }
 
@@ -2224,7 +2239,7 @@ fail:
 
 static int save_todo(struct todo_list *todo_list, struct replay_opts *opts)
 {
-       static struct lock_file todo_lock;
+       struct lock_file todo_lock = LOCK_INIT;
        const char *todo_path = get_todo_path(opts);
        int next = todo_list->current, offset, fd;
 
@@ -2244,7 +2259,7 @@ static int save_todo(struct todo_list *todo_list, struct replay_opts *opts)
                        todo_list->buf.len - offset) < 0)
                return error_errno(_("could not write to '%s'"), todo_path);
        if (commit_lock_file(&todo_lock) < 0)
-               return error(_("failed to finalize '%s'."), todo_path);
+               return error(_("failed to finalize '%s'"), todo_path);
 
        if (is_rebase_i(opts)) {
                const char *done_path = rebase_path_done();
@@ -2314,6 +2329,9 @@ static int make_patch(struct commit *commit, struct replay_opts *opts)
        p = short_commit_name(commit);
        if (write_message(p, strlen(p), rebase_path_stopped_sha(), 1) < 0)
                return -1;
+       if (update_ref("rebase", "REBASE_HEAD", &commit->object.oid,
+                      NULL, REF_NO_DEREF, UPDATE_REFS_MSG_ON_ERR))
+               res |= error(_("could not update %s"), "REBASE_HEAD");
 
        strbuf_addf(&buf, "%s/patch", get_dir(opts));
        memset(&log_tree_opt, 0, sizeof(log_tree_opt));
@@ -2385,10 +2403,9 @@ static int error_with_patch(struct commit *commit,
 static int error_failed_squash(struct commit *commit,
        struct replay_opts *opts, int subject_len, const char *subject)
 {
-       if (rename(rebase_path_squash_msg(), rebase_path_message()))
-               return error(_("could not rename '%s' to '%s'"),
+       if (copy_file(rebase_path_message(), rebase_path_squash_msg(), 0666))
+               return error(_("could not copy '%s' to '%s'"),
                        rebase_path_squash_msg(), rebase_path_message());
-       unlink(rebase_path_fixup_msg());
        unlink(git_path_merge_msg());
        if (copy_file(git_path_merge_msg(), rebase_path_message(), 0666))
                return error(_("could not copy '%s' to '%s'"),
@@ -2565,6 +2582,7 @@ static int pick_commits(struct todo_list *todo_list, struct replay_opts *opts)
                        unlink(rebase_path_author_script());
                        unlink(rebase_path_stopped_sha());
                        unlink(rebase_path_amend());
+                       delete_ref(NULL, "REBASE_HEAD", NULL, REF_NO_DEREF);
                }
                if (item->command <= TODO_SQUASH) {
                        if (is_rebase_i(opts))
@@ -2870,7 +2888,7 @@ int sequencer_pick_revisions(struct replay_opts *opts)
                        if (!lookup_commit_reference_gently(&oid, 1)) {
                                enum object_type type = sha1_object_info(oid.hash, NULL);
                                return error(_("%s: can't cherry-pick a %s"),
-                                       name, typename(type));
+                                       name, type_name(type));
                        }
                } else
                        return error(_("%s: bad revision"), name);
@@ -3151,20 +3169,13 @@ int check_todo_list(void)
        struct strbuf todo_file = STRBUF_INIT;
        struct todo_list todo_list = TODO_LIST_INIT;
        struct strbuf missing = STRBUF_INIT;
-       int advise_to_edit_todo = 0, res = 0, fd, i;
+       int advise_to_edit_todo = 0, res = 0, i;
 
        strbuf_addstr(&todo_file, rebase_path_todo());
-       fd = open(todo_file.buf, O_RDONLY);
-       if (fd < 0) {
-               res = error_errno(_("could not open '%s'"), todo_file.buf);
-               goto leave_check;
-       }
-       if (strbuf_read(&todo_list.buf, fd, 0) < 0) {
-               close(fd);
-               res = error(_("could not read '%s'."), todo_file.buf);
+       if (strbuf_read_file_or_whine(&todo_list.buf, todo_file.buf) < 0) {
+               res = -1;
                goto leave_check;
        }
-       close(fd);
        advise_to_edit_todo = res =
                parse_insn_buffer(todo_list.buf.buf, &todo_list);
 
@@ -3180,17 +3191,10 @@ int check_todo_list(void)
 
        todo_list_release(&todo_list);
        strbuf_addstr(&todo_file, ".backup");
-       fd = open(todo_file.buf, O_RDONLY);
-       if (fd < 0) {
-               res = error_errno(_("could not open '%s'"), todo_file.buf);
-               goto leave_check;
-       }
-       if (strbuf_read(&todo_list.buf, fd, 0) < 0) {
-               close(fd);
-               res = error(_("could not read '%s'."), todo_file.buf);
+       if (strbuf_read_file_or_whine(&todo_list.buf, todo_file.buf) < 0) {
+               res = -1;
                goto leave_check;
        }
-       close(fd);
        strbuf_release(&todo_file);
        res = !!parse_insn_buffer(todo_list.buf.buf, &todo_list);
 
@@ -3271,15 +3275,8 @@ int skip_unnecessary_picks(void)
        }
        strbuf_release(&buf);
 
-       fd = open(todo_file, O_RDONLY);
-       if (fd < 0) {
-               return error_errno(_("could not open '%s'"), todo_file);
-       }
-       if (strbuf_read(&todo_list.buf, fd, 0) < 0) {
-               close(fd);
-               return error(_("could not read '%s'."), todo_file);
-       }
-       close(fd);
+       if (strbuf_read_file_or_whine(&todo_list.buf, todo_file) < 0)
+               return -1;
        if (parse_insn_buffer(todo_list.buf.buf, &todo_list) < 0) {
                todo_list_release(&todo_list);
                return -1;
@@ -3370,17 +3367,11 @@ int rearrange_squash(void)
        const char *todo_file = rebase_path_todo();
        struct todo_list todo_list = TODO_LIST_INIT;
        struct hashmap subject2item;
-       int res = 0, rearranged = 0, *next, *tail, fd, i;
+       int res = 0, rearranged = 0, *next, *tail, i;
        char **subjects;
 
-       fd = open(todo_file, O_RDONLY);
-       if (fd < 0)
-               return error_errno(_("could not open '%s'"), todo_file);
-       if (strbuf_read(&todo_list.buf, fd, 0) < 0) {
-               close(fd);
-               return error(_("could not read '%s'."), todo_file);
-       }
-       close(fd);
+       if (strbuf_read_file_or_whine(&todo_list.buf, todo_file) < 0)
+               return -1;
        if (parse_insn_buffer(todo_list.buf.buf, &todo_list) < 0) {
                todo_list_release(&todo_list);
                return -1;