configure: fix a regression in PCRE v1 detection
[gitweb.git] / sequencer.c
index 86840b4bc1d3e49bb0070aefc6feac39cad1fbf9..091bd6bda5c55c27e2d0688a5e12988e22e6e431 100644 (file)
@@ -339,7 +339,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 +352,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;
 }
@@ -396,7 +394,7 @@ static int read_oneliner(struct strbuf *buf,
 
 static struct tree *empty_tree(void)
 {
-       return lookup_tree(&empty_tree_oid);
+       return lookup_tree(the_hash_algo->empty_tree);
 }
 
 static int error_dirty_index(struct replay_opts *opts)
@@ -485,9 +483,10 @@ 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;
 
-       hold_locked_index(&index_lock, LOCK_DIE_ON_ERROR);
+       if (hold_locked_index(&index_lock, LOCK_REPORT_ON_ERROR) < 0)
+               return -1;
 
        read_cache();
 
@@ -497,6 +496,7 @@ static int do_recursive_merge(struct commit *base, struct commit *next,
        o.branch2 = next ? next_label : "(empty tree)";
        if (is_rebase_i(opts))
                o.buffer_output = 2;
+       o.show_rename_progress = 1;
 
        head_tree = parse_tree_indirect(head);
        next_tree = next ? next->tree : empty_tree();
@@ -511,8 +511,11 @@ static int do_recursive_merge(struct commit *base, struct commit *next,
        if (is_rebase_i(opts) && clean <= 0)
                fputs(o.obuf.buf, stdout);
        strbuf_release(&o.obuf);
-       if (clean < 0)
+       diff_warn_rename_limit("merge.renamelimit", o.needed_rename_limit, 0);
+       if (clean < 0) {
+               rollback_lock_file(&index_lock);
                return clean;
+       }
 
        if (active_cache_changed &&
            write_locked_index(&the_index, &index_lock, COMMIT_LOCK))
@@ -536,7 +539,7 @@ static int is_index_unchanged(void)
        struct commit *head_commit;
 
        if (!resolve_ref_unsafe("HEAD", RESOLVE_REF_READING, &head_oid, NULL))
-               return error(_("could not resolve HEAD commit\n"));
+               return error(_("could not resolve HEAD commit"));
 
        head_commit = lookup_commit(&head_oid);
 
@@ -556,7 +559,7 @@ static int is_index_unchanged(void)
 
        if (!cache_tree_fully_valid(active_cache_tree))
                if (cache_tree_update(&the_index, 0))
-                       return error(_("unable to update cache tree\n"));
+                       return error(_("unable to update cache tree"));
 
        return !oidcmp(&active_cache_tree->oid,
                       &head_commit->tree->object.oid);
@@ -1011,7 +1014,7 @@ void print_commit_summary(const char *prefix, const struct object_id *oid,
        rev.show_root_diff = 1;
        get_commit_format(format.buf, &rev);
        rev.always_show_header = 0;
-       rev.diffopt.detect_rename = 1;
+       rev.diffopt.detect_rename = DIFF_DETECT_RENAME;
        rev.diffopt.break_opt = 0;
        diff_setup_done(&rev.diffopt);
 
@@ -1146,8 +1149,8 @@ static int try_to_commit(struct strbuf *msg, const char *author,
                goto out;
        }
 
-       if (commit_tree_extended(msg->buf, msg->len, tree.hash, parents,
-                                oid->hash, author, opts->gpg_sign, extra)) {
+       if (commit_tree_extended(msg->buf, msg->len, &tree, parents,
+                                oid, author, opts->gpg_sign, extra)) {
                res = error(_("failed to write commit object"));
                goto out;
        }
@@ -1207,16 +1210,16 @@ static int is_original_commit_empty(struct commit *commit)
        const struct object_id *ptree_oid;
 
        if (parse_commit(commit))
-               return error(_("could not parse commit %s\n"),
+               return error(_("could not parse commit %s"),
                             oid_to_hex(&commit->object.oid));
        if (commit->parents) {
                struct commit *parent = commit->parents->item;
                if (parse_commit(parent))
-                       return error(_("could not parse parent commit %s\n"),
+                       return error(_("could not parse parent commit %s"),
                                oid_to_hex(&parent->object.oid));
                ptree_oid = &parent->tree->object.oid;
        } else {
-               ptree_oid = &empty_tree_oid; /* commit is root */
+               ptree_oid = the_hash_algo->empty_tree; /* commit is root */
        }
 
        return !oidcmp(ptree_oid, &commit->tree->object.oid);
@@ -1305,6 +1308,13 @@ static const char *command_to_string(const enum todo_command command)
        die("Unknown command: %d", command);
 }
 
+static char command_to_char(const enum todo_command command)
+{
+       if (command < TODO_COMMENT && todo_command_info[command].c)
+               return todo_command_info[command].c;
+       return comment_line_char;
+}
+
 static int is_noop(const enum todo_command command)
 {
        return TODO_NOOP <= command;
@@ -1470,7 +1480,7 @@ static int do_pick_commit(enum todo_command command, struct commit *commit,
        } else {
                unborn = get_oid("HEAD", &head);
                if (unborn)
-                       oidcpy(&head, &empty_tree_oid);
+                       oidcpy(&head, the_hash_algo->empty_tree);
                if (index_differs_from(unborn ? EMPTY_TREE_SHA1_HEX : "HEAD",
                                       NULL, 0))
                        return error_dirty_index(opts);
@@ -1515,9 +1525,7 @@ static int do_pick_commit(enum todo_command command, struct commit *commit,
                        opts);
                if (res || command != TODO_REWORD)
                        goto leave;
-               flags |= EDIT_MSG | AMEND_MSG;
-               if (command == TODO_REWORD)
-                       flags |= VERIFY_MSG;
+               flags |= EDIT_MSG | AMEND_MSG | VERIFY_MSG;
                msg_file = NULL;
                goto fast_forward_edit;
        }
@@ -1634,11 +1642,11 @@ static int do_pick_commit(enum todo_command command, struct commit *commit,
         */
        if (command == TODO_PICK && !opts->no_commit && (res == 0 || res == 1) &&
            update_ref(NULL, "CHERRY_PICK_HEAD", &commit->object.oid, NULL,
-                      REF_NODEREF, UPDATE_REFS_MSG_ON_ERR))
+                      REF_NO_DEREF, UPDATE_REFS_MSG_ON_ERR))
                res = -1;
        if (command == TODO_REVERT && ((opts->no_commit && res == 0) || res == 1) &&
            update_ref(NULL, "REVERT_HEAD", &commit->object.oid, NULL,
-                      REF_NODEREF, UPDATE_REFS_MSG_ON_ERR))
+                      REF_NO_DEREF, UPDATE_REFS_MSG_ON_ERR))
                res = -1;
 
        if (res) {
@@ -1697,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);
@@ -1775,20 +1783,26 @@ static int parse_insn_line(struct todo_item *item, const char *bol, char *eol)
        if (i >= TODO_COMMENT)
                return -1;
 
+       /* Eat up extra spaces/ tabs before object name */
+       padding = strspn(bol, " \t");
+       bol += padding;
+
        if (item->command == TODO_NOOP) {
+               if (bol != eol)
+                       return error(_("%s does not accept arguments: '%s'"),
+                                    command_to_string(item->command), bol);
                item->commit = NULL;
                item->arg = bol;
                item->arg_len = eol - bol;
                return 0;
        }
 
-       /* Eat up extra spaces/ tabs before object name */
-       padding = strspn(bol, " \t");
        if (!padding)
-               return -1;
-       bol += padding;
+               return error(_("missing arguments for %s"),
+                            command_to_string(item->command));
 
        if (item->command == TODO_EXEC) {
+               item->commit = NULL;
                item->arg = bol;
                item->arg_len = (int)(eol - bol);
                return 0;
@@ -1855,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)
@@ -2085,16 +2108,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);
@@ -2103,10 +2124,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;
 }
 
@@ -2210,7 +2229,7 @@ int sequencer_rollback(struct replay_opts *opts)
 
 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;
 
@@ -2230,7 +2249,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();
@@ -2300,6 +2319,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));
@@ -2551,6 +2573,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))
@@ -2652,7 +2675,7 @@ static int pick_commits(struct todo_list *todo_list, struct replay_opts *opts)
                        msg = reflog_message(opts, "finish", "%s onto %s",
                                head_ref.buf, buf.buf);
                        if (update_ref(msg, head_ref.buf, &head, &orig,
-                                      REF_NODEREF, UPDATE_REFS_MSG_ON_ERR)) {
+                                      REF_NO_DEREF, UPDATE_REFS_MSG_ON_ERR)) {
                                res = error(_("could not update %s"),
                                        head_ref.buf);
                                goto cleanup_head_ref;
@@ -2856,7 +2879,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);
@@ -2964,14 +2987,16 @@ void append_signoff(struct strbuf *msgbuf, int ignore_footer, unsigned flag)
        strbuf_release(&sob);
 }
 
-int sequencer_make_script(int keep_empty, FILE *out,
-               int argc, const char **argv)
+int sequencer_make_script(FILE *out, int argc, const char **argv,
+                         unsigned flags)
 {
        char *format = NULL;
        struct pretty_print_context pp = {0};
        struct strbuf buf = STRBUF_INIT;
        struct rev_info revs;
        struct commit *commit;
+       int keep_empty = flags & TODO_LIST_KEEP_EMPTY;
+       const char *insn = flags & TODO_LIST_ABBREVIATE_CMDS ? "p" : "pick";
 
        init_revisions(&revs, NULL);
        revs.verbose_header = 1;
@@ -3004,7 +3029,8 @@ int sequencer_make_script(int keep_empty, FILE *out,
                strbuf_reset(&buf);
                if (!keep_empty && is_original_commit_empty(commit))
                        strbuf_addf(&buf, "%c ", comment_line_char);
-               strbuf_addf(&buf, "pick %s ", oid_to_hex(&commit->object.oid));
+               strbuf_addf(&buf, "%s %s ", insn,
+                           oid_to_hex(&commit->object.oid));
                pretty_print_commit(&pp, commit, &buf);
                strbuf_addch(&buf, '\n');
                fputs(buf.buf, out);
@@ -3013,61 +3039,93 @@ int sequencer_make_script(int keep_empty, FILE *out,
        return 0;
 }
 
-
-int transform_todo_ids(int shorten_ids)
+/*
+ * Add commands after pick and (series of) squash/fixup commands
+ * in the todo list.
+ */
+int sequencer_add_exec_commands(const char *commands)
 {
        const char *todo_file = rebase_path_todo();
        struct todo_list todo_list = TODO_LIST_INIT;
-       int fd, res, i;
-       FILE *out;
+       struct todo_item *item;
+       struct strbuf *buf = &todo_list.buf;
+       size_t offset = 0, commands_len = strlen(commands);
+       int i, first;
 
-       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);
+       if (strbuf_read_file(&todo_list.buf, todo_file, 0) < 0)
                return error(_("could not read '%s'."), todo_file);
-       }
-       close(fd);
 
-       res = parse_insn_buffer(todo_list.buf.buf, &todo_list);
-       if (res) {
+       if (parse_insn_buffer(todo_list.buf.buf, &todo_list)) {
                todo_list_release(&todo_list);
                return error(_("unusable todo list: '%s'"), todo_file);
        }
 
-       out = fopen(todo_file, "w");
-       if (!out) {
+       first = 1;
+       /* insert <commands> before every pick except the first one */
+       for (item = todo_list.items, i = 0; i < todo_list.nr; i++, item++) {
+               if (item->command == TODO_PICK && !first) {
+                       strbuf_insert(buf, item->offset_in_buf + offset,
+                                     commands, commands_len);
+                       offset += commands_len;
+               }
+               first = 0;
+       }
+
+       /* append final <commands> */
+       strbuf_add(buf, commands, commands_len);
+
+       i = write_message(buf->buf, buf->len, todo_file, 0);
+       todo_list_release(&todo_list);
+       return i;
+}
+
+int transform_todos(unsigned flags)
+{
+       const char *todo_file = rebase_path_todo();
+       struct todo_list todo_list = TODO_LIST_INIT;
+       struct strbuf buf = STRBUF_INIT;
+       struct todo_item *item;
+       int i;
+
+       if (strbuf_read_file(&todo_list.buf, todo_file, 0) < 0)
+               return error(_("could not read '%s'."), todo_file);
+
+       if (parse_insn_buffer(todo_list.buf.buf, &todo_list)) {
                todo_list_release(&todo_list);
-               return error(_("unable to open '%s' for writing"), todo_file);
+               return error(_("unusable todo list: '%s'"), todo_file);
        }
-       for (i = 0; i < todo_list.nr; i++) {
-               struct todo_item *item = todo_list.items + i;
-               int bol = item->offset_in_buf;
-               const char *p = todo_list.buf.buf + bol;
-               int eol = i + 1 < todo_list.nr ?
-                       todo_list.items[i + 1].offset_in_buf :
-                       todo_list.buf.len;
-
-               if (item->command >= TODO_EXEC && item->command != TODO_DROP)
-                       fwrite(p, eol - bol, 1, out);
-               else {
-                       const char *id = shorten_ids ?
-                               short_commit_name(item->commit) :
-                               oid_to_hex(&item->commit->object.oid);
-                       int len;
-
-                       p += strspn(p, " \t"); /* left-trim command */
-                       len = strcspn(p, " \t"); /* length of command */
-
-                       fprintf(out, "%.*s %s %.*s\n",
-                               len, p, id, item->arg_len, item->arg);
+
+       for (item = todo_list.items, i = 0; i < todo_list.nr; i++, item++) {
+               /* if the item is not a command write it and continue */
+               if (item->command >= TODO_COMMENT) {
+                       strbuf_addf(&buf, "%.*s\n", item->arg_len, item->arg);
+                       continue;
+               }
+
+               /* add command to the buffer */
+               if (flags & TODO_LIST_ABBREVIATE_CMDS)
+                       strbuf_addch(&buf, command_to_char(item->command));
+               else
+                       strbuf_addstr(&buf, command_to_string(item->command));
+
+               /* add commit id */
+               if (item->commit) {
+                       const char *oid = flags & TODO_LIST_SHORTEN_IDS ?
+                                         short_commit_name(item->commit) :
+                                         oid_to_hex(&item->commit->object.oid);
+
+                       strbuf_addf(&buf, " %s", oid);
                }
+               /* add all the rest */
+               if (!item->arg_len)
+                       strbuf_addch(&buf, '\n');
+               else
+                       strbuf_addf(&buf, " %.*s\n", item->arg_len, item->arg);
        }
-       fclose(out);
+
+       i = write_message(buf.buf, buf.len, todo_file, 0);
        todo_list_release(&todo_list);
-       return 0;
+       return i;
 }
 
 enum check_level {
@@ -3102,20 +3160,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);
 
@@ -3131,17 +3182,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);
 
@@ -3192,6 +3236,19 @@ int check_todo_list(void)
        return res;
 }
 
+static int rewrite_file(const char *path, const char *buf, size_t len)
+{
+       int rc = 0;
+       int fd = open(path, O_WRONLY | O_TRUNC);
+       if (fd < 0)
+               return error_errno(_("could not open '%s' for writing"), path);
+       if (write_in_full(fd, buf, len) < 0)
+               rc = error_errno(_("could not write to '%s'"), path);
+       if (close(fd) && !rc)
+               rc = error_errno(_("could not close '%s'"), path);
+       return rc;
+}
+
 /* skip picking commits whose parents are unchanged */
 int skip_unnecessary_picks(void)
 {
@@ -3209,15 +3266,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;
@@ -3264,29 +3314,11 @@ int skip_unnecessary_picks(void)
                }
                close(fd);
 
-               fd = open(rebase_path_todo(), O_WRONLY, 0666);
-               if (fd < 0) {
-                       error_errno(_("could not open '%s' for writing"),
-                                   rebase_path_todo());
-                       todo_list_release(&todo_list);
-                       return -1;
-               }
-               if (write_in_full(fd, todo_list.buf.buf + offset,
-                               todo_list.buf.len - offset) < 0) {
-                       error_errno(_("could not write to '%s'"),
-                                   rebase_path_todo());
-                       close(fd);
+               if (rewrite_file(rebase_path_todo(), todo_list.buf.buf + offset,
+                                todo_list.buf.len - offset) < 0) {
                        todo_list_release(&todo_list);
                        return -1;
                }
-               if (ftruncate(fd, todo_list.buf.len - offset) < 0) {
-                       error_errno(_("could not truncate '%s'"),
-                                   rebase_path_todo());
-                       todo_list_release(&todo_list);
-                       close(fd);
-                       return -1;
-               }
-               close(fd);
 
                todo_list.current = i;
                if (is_fixup(peek_command(&todo_list, 0)))
@@ -3326,17 +3358,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;
@@ -3471,15 +3497,7 @@ int rearrange_squash(void)
                        }
                }
 
-               fd = open(todo_file, O_WRONLY);
-               if (fd < 0)
-                       res = error_errno(_("could not open '%s'"), todo_file);
-               else if (write(fd, buf.buf, buf.len) < 0)
-                       res = error_errno(_("could not write to '%s'"), todo_file);
-               else if (ftruncate(fd, buf.len) < 0)
-                       res = error_errno(_("could not truncate '%s'"),
-                                          todo_file);
-               close(fd);
+               res = rewrite_file(todo_file, buf.buf, buf.len);
                strbuf_release(&buf);
        }