diff options
Diffstat (limited to 'sequencer.c')
-rw-r--r-- | sequencer.c | 644 |
1 files changed, 364 insertions, 280 deletions
diff --git a/sequencer.c b/sequencer.c index 1c96a75b1e..3cc88d8a80 100644 --- a/sequencer.c +++ b/sequencer.c @@ -1,23 +1,33 @@ -#include "cache.h" +#include "git-compat-util.h" +#include "abspath.h" +#include "advice.h" #include "config.h" +#include "copy.h" +#include "environment.h" +#include "gettext.h" +#include "hex.h" #include "lockfile.h" #include "dir.h" -#include "object-store.h" +#include "object-file.h" +#include "object-name.h" +#include "object-store-ll.h" #include "object.h" +#include "pager.h" #include "commit.h" #include "sequencer.h" -#include "tag.h" #include "run-command.h" #include "hook.h" -#include "exec-cmd.h" #include "utf8.h" #include "cache-tree.h" #include "diff.h" +#include "path.h" #include "revision.h" #include "rerere.h" +#include "merge.h" #include "merge-ort.h" #include "merge-ort-wrappers.h" #include "refs.h" +#include "sparse-index.h" #include "strvec.h" #include "quote.h" #include "trailer.h" @@ -27,7 +37,6 @@ #include "notes-utils.h" #include "sigchain.h" #include "unpack-trees.h" -#include "worktree.h" #include "oidmap.h" #include "oidset.h" #include "commit-slab.h" @@ -39,6 +48,15 @@ #define GIT_REFLOG_ACTION "GIT_REFLOG_ACTION" +/* + * To accommodate common filesystem limitations, where the loose refs' file + * names must not exceed `NAME_MAX`, the labels generated by `git rebase + * --rebase-merges` need to be truncated if the corresponding commit subjects + * are too long. + * Add some margin to stay clear from reaching `NAME_MAX`. + */ +#define GIT_MAX_LABEL_LENGTH ((NAME_MAX) - (LOCK_SUFFIX_LEN) - 16) + static const char sign_off_header[] = "Signed-off-by: "; static const char cherry_picked_prefix[] = "(cherry picked from commit "; @@ -127,6 +145,11 @@ static GIT_PATH_FUNC(rebase_path_amend, "rebase-merge/amend") */ static GIT_PATH_FUNC(rebase_path_stopped_sha, "rebase-merge/stopped-sha") /* + * When we stop for the user to resolve conflicts this file contains + * the patch of the commit that is being picked. + */ +static GIT_PATH_FUNC(rebase_path_patch, "rebase-merge/patch") +/* * For the post-rewrite hook, we make a list of rewritten commits and * their new sha1s. The rewritten-pending list keeps the sha1s of * commits that have been processed, but not committed yet, @@ -208,37 +231,33 @@ static struct update_ref_record *init_update_ref_record(const char *ref) return rec; } -static int git_sequencer_config(const char *k, const char *v, void *cb) +static int git_sequencer_config(const char *k, const char *v, + const struct config_context *ctx, void *cb) { struct replay_opts *opts = cb; - int status; if (!strcmp(k, "commit.cleanup")) { - const char *s; - - status = git_config_string(&s, k, v); - if (status) - return status; + if (!v) + return config_error_nonbool(k); - if (!strcmp(s, "verbatim")) { + if (!strcmp(v, "verbatim")) { opts->default_msg_cleanup = COMMIT_MSG_CLEANUP_NONE; opts->explicit_cleanup = 1; - } else if (!strcmp(s, "whitespace")) { + } else if (!strcmp(v, "whitespace")) { opts->default_msg_cleanup = COMMIT_MSG_CLEANUP_SPACE; opts->explicit_cleanup = 1; - } else if (!strcmp(s, "strip")) { + } else if (!strcmp(v, "strip")) { opts->default_msg_cleanup = COMMIT_MSG_CLEANUP_ALL; opts->explicit_cleanup = 1; - } else if (!strcmp(s, "scissors")) { + } else if (!strcmp(v, "scissors")) { opts->default_msg_cleanup = COMMIT_MSG_CLEANUP_SCISSORS; opts->explicit_cleanup = 1; } else { warning(_("invalid commit message cleanup mode '%s'"), - s); + v); } - free((char *)s); - return status; + return 0; } if (!strcmp(k, "commit.gpgsign")) { @@ -263,11 +282,7 @@ static int git_sequencer_config(const char *k, const char *v, void *cb) if (opts->action == REPLAY_REVERT && !strcmp(k, "revert.reference")) opts->commit_use_reference = git_config_bool(k, v); - status = git_gpg_config(k, v, NULL); - if (status) - return status; - - return git_diff_basic_config(k, v, NULL); + return git_diff_basic_config(k, v, ctx, NULL); } void sequencer_init_config(struct replay_opts *opts) @@ -322,7 +337,7 @@ static int has_conforming_footer(struct strbuf *sb, struct strbuf *sob, if (ignore_footer) sb->buf[sb->len - ignore_footer] = saved_char; - if (info.trailer_start == info.trailer_end) + if (info.trailer_block_start == info.trailer_block_end) return 0; for (i = 0; i < info.trailer_nr; i++) @@ -357,9 +372,7 @@ void replay_opts_release(struct replay_opts *opts) free(opts->reflog_action); free(opts->default_strategy); free(opts->strategy); - for (size_t i = 0; i < opts->xopts_nr; i++) - free(opts->xopts[i]); - free(opts->xopts); + strvec_clear (&opts->xopts); strbuf_release(&opts->current_fixups); if (opts->revs) release_revisions(opts->revs); @@ -417,9 +430,9 @@ struct commit_message { const char *message; }; -static const char *short_commit_name(struct commit *commit) +static const char *short_commit_name(struct repository *r, struct commit *commit) { - return find_unique_abbrev(&commit->object.oid, DEFAULT_ABBREV); + return repo_find_unique_abbrev(r, &commit->object.oid, DEFAULT_ABBREV); } static int get_message(struct commit *commit, struct commit_message *out) @@ -427,8 +440,9 @@ static int get_message(struct commit *commit, struct commit_message *out) const char *abbrev, *subject; int subject_len; - out->message = logmsg_reencode(commit, NULL, get_commit_output_encoding()); - abbrev = short_commit_name(commit); + out->message = repo_logmsg_reencode(the_repository, commit, NULL, + get_commit_output_encoding()); + abbrev = short_commit_name(the_repository, commit); subject_len = find_commit_subject(out->message, &subject); @@ -444,7 +458,7 @@ static void free_message(struct commit *commit, struct commit_message *msg) free(msg->parent_label); free(msg->label); free(msg->subject); - unuse_commit_buffer(commit, msg->message); + repo_unuse_commit_buffer(the_repository, commit, msg->message); } static void print_advice(struct repository *r, int show_hint, @@ -561,7 +575,7 @@ static void update_abort_safety_file(void) if (!file_exists(git_path_seq_dir())) return; - if (!get_oid("HEAD", &head)) + if (!repo_get_oid(the_repository, "HEAD", &head)) write_file(git_path_abort_safety_file(), "%s", oid_to_hex(&head)); else write_file(git_path_abort_safety_file(), "%s", ""); @@ -653,11 +667,12 @@ void append_conflicts_hint(struct index_state *istate, } strbuf_addch(msgbuf, '\n'); - strbuf_commented_addf(msgbuf, "Conflicts:\n"); + strbuf_commented_addf(msgbuf, comment_line_char, "Conflicts:\n"); for (i = 0; i < istate->cache_nr;) { const struct cache_entry *ce = istate->cache[i++]; if (ce_stage(ce)) { - strbuf_commented_addf(msgbuf, "\t%s\n", ce->name); + strbuf_commented_addf(msgbuf, comment_line_char, + "\t%s\n", ce->name); while (i < istate->cache_nr && !strcmp(ce->name, istate->cache[i]->name)) i++; @@ -692,11 +707,11 @@ static int do_recursive_merge(struct repository *r, o.show_rename_progress = 1; head_tree = parse_tree_indirect(head); - next_tree = next ? get_commit_tree(next) : empty_tree(r); - base_tree = base ? get_commit_tree(base) : empty_tree(r); + next_tree = next ? repo_get_commit_tree(r, next) : empty_tree(r); + base_tree = base ? repo_get_commit_tree(r, base) : empty_tree(r); - for (i = 0; i < opts->xopts_nr; i++) - parse_merge_opt(&o, opts->xopts[i]); + for (i = 0; i < opts->xopts.nr; i++) + parse_merge_opt(&o, opts->xopts.v[i]); if (!opts->strategy || !strcmp(opts->strategy, "ort")) { memset(&result, 0, sizeof(result)); @@ -766,12 +781,12 @@ static int is_index_unchanged(struct repository *r) /* * If head_commit is NULL, check_commit, called from * lookup_commit, would have indicated that head_commit is not - * a commit object already. parse_commit() will return failure + * a commit object already. repo_parse_commit() will return failure * without further complaints in such a case. Otherwise, if - * the commit is invalid, parse_commit() will complain. So + * the commit is invalid, repo_parse_commit() will complain. So * there is nothing for us to say here. Just return failure. */ - if (parse_commit(head_commit)) + if (repo_parse_commit(r, head_commit)) return -1; if (!(cache_tree_oid = get_cache_tree_oid(istate))) @@ -1136,7 +1151,8 @@ void cleanup_message(struct strbuf *msgbuf, cleanup_mode == COMMIT_MSG_CLEANUP_SCISSORS) strbuf_setlen(msgbuf, wt_status_locate_end(msgbuf->buf, msgbuf->len)); if (cleanup_mode != COMMIT_MSG_CLEANUP_NONE) - strbuf_stripspace(msgbuf, cleanup_mode == COMMIT_MSG_CLEANUP_ALL); + strbuf_stripspace(msgbuf, + cleanup_mode == COMMIT_MSG_CLEANUP_ALL ? comment_line_char : '\0'); } /* @@ -1167,7 +1183,8 @@ int template_untouched(const struct strbuf *sb, const char *template_file, if (!template_file || strbuf_read_file(&tmpl, template_file, 0) <= 0) return 0; - strbuf_stripspace(&tmpl, cleanup_mode == COMMIT_MSG_CLEANUP_ALL); + strbuf_stripspace(&tmpl, + cleanup_mode == COMMIT_MSG_CLEANUP_ALL ? comment_line_char : '\0'); if (!skip_prefix(sb->buf, tmpl.buf, &start)) start = sb->buf; strbuf_release(&tmpl); @@ -1336,13 +1353,15 @@ void print_commit_summary(struct repository *r, commit = lookup_commit(r, oid); if (!commit) die(_("couldn't look up newly created commit")); - if (parse_commit(commit)) + if (repo_parse_commit(r, commit)) die(_("could not parse newly created commit")); strbuf_addstr(&format, "format:%h] %s"); - format_commit_message(commit, "%an <%ae>", &author_ident, &pctx); - format_commit_message(commit, "%cn <%ce>", &committer_ident, &pctx); + repo_format_commit_message(r, commit, "%an <%ae>", &author_ident, + &pctx); + repo_format_commit_message(r, commit, "%cn <%ce>", &committer_ident, + &pctx); if (strbuf_cmp(&author_ident, &committer_ident)) { strbuf_addstr(&format, "\n Author: "); strbuf_addbuf_percentquote(&format, &author_ident); @@ -1350,7 +1369,7 @@ void print_commit_summary(struct repository *r, if (flags & SUMMARY_SHOW_AUTHOR_DATE) { struct strbuf date = STRBUF_INIT; - format_commit_message(commit, "%ad", &date, &pctx); + repo_format_commit_message(r, commit, "%ad", &date, &pctx); strbuf_addstr(&format, "\n Date: "); strbuf_addbuf_percentquote(&format, &date); strbuf_release(&date); @@ -1380,7 +1399,7 @@ void print_commit_summary(struct repository *r, rev.diffopt.detect_rename = DIFF_DETECT_RENAME; diff_setup_done(&rev.diffopt); - refs = get_main_ref_store(the_repository); + refs = get_main_ref_store(r); head = refs_resolve_ref_unsafe(refs, "HEAD", 0, NULL, NULL); if (!head) die(_("unable to resolve HEAD after creating commit")); @@ -1406,7 +1425,7 @@ static int parse_head(struct repository *r, struct commit **head) struct commit *current_head; struct object_id oid; - if (get_oid("HEAD", &oid)) { + if (repo_get_oid(r, "HEAD", &oid)) { current_head = NULL; } else { current_head = lookup_commit_reference(r, &oid); @@ -1416,7 +1435,7 @@ static int parse_head(struct repository *r, struct commit **head) warning(_("HEAD %s is not a commit!"), oid_to_hex(&oid)); } - if (parse_commit(current_head)) + if (repo_parse_commit(r, current_head)) return error(_("could not parse HEAD commit")); } *head = current_head; @@ -1459,8 +1478,8 @@ static int try_to_commit(struct repository *r, if (flags & AMEND_MSG) { const char *exclude_gpgsig[] = { "gpgsig", "gpgsig-sha256", NULL }; const char *out_enc = get_commit_output_encoding(); - const char *message = logmsg_reencode(current_head, NULL, - out_enc); + const char *message = repo_logmsg_reencode(r, current_head, + NULL, out_enc); if (!msg) { const char *orig_message = NULL; @@ -1471,7 +1490,8 @@ static int try_to_commit(struct repository *r, hook_commit = "HEAD"; } author = amend_author = get_author(message); - unuse_commit_buffer(current_head, message); + repo_unuse_commit_buffer(r, current_head, + message); if (!author) { res = error(_("unable to parse commit author")); goto out; @@ -1536,7 +1556,8 @@ static int try_to_commit(struct repository *r, cleanup = opts->default_msg_cleanup; if (cleanup != COMMIT_MSG_CLEANUP_NONE) - strbuf_stripspace(msg, cleanup == COMMIT_MSG_CLEANUP_ALL); + strbuf_stripspace(msg, + cleanup == COMMIT_MSG_CLEANUP_ALL ? comment_line_char : '\0'); if ((flags & EDIT_MSG) && message_is_empty(msg, cleanup)) { res = 1; /* run 'git commit' to display error message */ goto out; @@ -1668,12 +1689,12 @@ static int is_original_commit_empty(struct commit *commit) { const struct object_id *ptree_oid; - if (parse_commit(commit)) + if (repo_parse_commit(the_repository, commit)) 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)) + if (repo_parse_commit(the_repository, parent)) return error(_("could not parse parent commit %s"), oid_to_hex(&parent->object.oid)); ptree_oid = get_commit_tree_oid(parent); @@ -1830,7 +1851,7 @@ static void add_commented_lines(struct strbuf *buf, const void *str, size_t len) s += count; len -= count; } - strbuf_add_commented_lines(buf, s, len); + strbuf_add_commented_lines(buf, s, len, comment_line_char); } /* Does the current fixup chain contain a squash command? */ @@ -1929,7 +1950,7 @@ static int append_squash_message(struct strbuf *buf, const char *body, strbuf_addf(buf, _(nth_commit_msg_fmt), ++opts->current_fixup_count + 1); strbuf_addstr(buf, "\n\n"); - strbuf_add_commented_lines(buf, body, commented_len); + strbuf_add_commented_lines(buf, body, commented_len, comment_line_char); /* buf->buf may be reallocated so store an offset into the buffer */ fixup_off = buf->len; strbuf_addstr(buf, body + commented_len); @@ -1997,17 +2018,18 @@ static int update_squash_messages(struct repository *r, struct commit *head_commit; const char *head_message, *body; - if (get_oid("HEAD", &head)) + if (repo_get_oid(r, "HEAD", &head)) return error(_("need a HEAD to fixup")); if (!(head_commit = lookup_commit_reference(r, &head))) return error(_("could not read HEAD")); - if (!(head_message = logmsg_reencode(head_commit, NULL, encoding))) + if (!(head_message = repo_logmsg_reencode(r, head_commit, NULL, + encoding))) return error(_("could not read HEAD's commit message")); find_commit_subject(head_message, &body); if (command == TODO_FIXUP && !flag && write_message(body, strlen(body), rebase_path_fixup_msg(), 0) < 0) { - unuse_commit_buffer(head_commit, head_message); + repo_unuse_commit_buffer(r, head_commit, head_message); return error(_("cannot write '%s'"), rebase_path_fixup_msg()); } strbuf_addf(&buf, "%c ", comment_line_char); @@ -2018,14 +2040,15 @@ static int update_squash_messages(struct repository *r, _(first_commit_msg_str)); strbuf_addstr(&buf, "\n\n"); if (is_fixup_flag(command, flag)) - strbuf_add_commented_lines(&buf, body, strlen(body)); + strbuf_add_commented_lines(&buf, body, strlen(body), + comment_line_char); else strbuf_addstr(&buf, body); - unuse_commit_buffer(head_commit, head_message); + repo_unuse_commit_buffer(r, head_commit, head_message); } - if (!(message = logmsg_reencode(commit, NULL, encoding))) + if (!(message = repo_logmsg_reencode(r, commit, NULL, encoding))) return error(_("could not read commit message of %s"), oid_to_hex(&commit->object.oid)); find_commit_subject(message, &body); @@ -2037,10 +2060,11 @@ static int update_squash_messages(struct repository *r, strbuf_addf(&buf, _(skip_nth_commit_msg_fmt), ++opts->current_fixup_count + 1); strbuf_addstr(&buf, "\n\n"); - strbuf_add_commented_lines(&buf, body, strlen(body)); + strbuf_add_commented_lines(&buf, body, strlen(body), + comment_line_char); } else return error(_("unknown command: %d"), command); - unuse_commit_buffer(commit, message); + repo_unuse_commit_buffer(r, commit, message); if (!res) res = write_message(buf.buf, buf.len, rebase_path_squash_msg(), @@ -2067,7 +2091,7 @@ static void flush_rewritten_pending(void) FILE *out; if (strbuf_read_file(&buf, rebase_path_rewritten_pending(), (GIT_MAX_HEXSZ + 1) * 2) > 0 && - !get_oid("HEAD", &newoid) && + !repo_get_oid(the_repository, "HEAD", &newoid) && (out = fopen_or_warn(rebase_path_rewritten_list(), "a"))) { char *bol = buf.buf, *eol; @@ -2119,7 +2143,8 @@ static void refer_to_commit(struct replay_opts *opts, .abbrev = DEFAULT_ABBREV, .date_mode.type = DATE_SHORT, }; - format_commit_message(commit, "%h (%s, %ad)", msgbuf, &ctx); + repo_format_commit_message(the_repository, commit, + "%h (%s, %ad)", msgbuf, &ctx); } else { strbuf_addstr(msgbuf, oid_to_hex(&commit->object.oid)); } @@ -2152,7 +2177,7 @@ static int do_pick_commit(struct repository *r, if (write_index_as_tree(&head, r->index, r->index_file, 0, NULL)) return error(_("your index file is unmerged.")); } else { - unborn = get_oid("HEAD", &head); + unborn = repo_get_oid(r, "HEAD", &head); /* Do we want to generate a root commit? */ if (is_pick_or_similar(command) && opts->have_squash_onto && oideq(&head, &opts->squash_onto)) { @@ -2214,7 +2239,7 @@ static int do_pick_commit(struct repository *r, msg_file = NULL; goto fast_forward_edit; } - if (parent && parse_commit(parent) < 0) + if (parent && repo_parse_commit(r, parent) < 0) /* TRANSLATORS: The first %s will be a "todo" command like "revert" or "pick", the second %s a SHA1. */ return error(_("%s: cannot parse parent commit %s"), @@ -2229,6 +2254,8 @@ static int do_pick_commit(struct repository *r, */ if (command == TODO_REVERT) { + const char *orig_subject; + base = commit; base_label = msg.label; next = parent; @@ -2236,6 +2263,15 @@ static int do_pick_commit(struct repository *r, if (opts->commit_use_reference) { strbuf_addstr(&msgbuf, "# *** SAY WHY WE ARE REVERTING ON THE TITLE LINE ***"); + } else if (skip_prefix(msg.subject, "Revert \"", &orig_subject) && + /* + * We don't touch pre-existing repeated reverts, because + * theoretically these can be nested arbitrarily deeply, + * thus requiring excessive complexity to deal with. + */ + !starts_with(orig_subject, "Revert \"")) { + strbuf_addstr(&msgbuf, "Reapply \""); + strbuf_addstr(&msgbuf, orig_subject); } else { strbuf_addstr(&msgbuf, "Revert \""); strbuf_addstr(&msgbuf, msg.subject); @@ -2291,7 +2327,7 @@ static int do_pick_commit(struct repository *r, const char *dest = git_path_squash_msg(r); unlink(dest); if (copy_file(dest, rebase_path_squash_msg(), 0666)) { - res = error(_("could not rename '%s' to '%s'"), + res = error(_("could not copy '%s' to '%s'"), rebase_path_squash_msg(), dest); goto leave; } @@ -2327,7 +2363,7 @@ static int do_pick_commit(struct repository *r, commit_list_insert(base, &common); commit_list_insert(next, &remotes); res |= try_merge_command(r, opts->strategy, - opts->xopts_nr, (const char **)opts->xopts, + opts->xopts.nr, opts->xopts.v, common, oid_to_hex(&head), remotes); free_commit_list(common); free_commit_list(remotes); @@ -2354,7 +2390,7 @@ static int do_pick_commit(struct repository *r, error(command == TODO_REVERT ? _("could not revert %s... %s") : _("could not apply %s... %s"), - short_commit_name(commit), msg.subject); + short_commit_name(r, commit), msg.subject); print_advice(r, res == 1, opts); repo_rerere(r, opts->allow_rerere_auto); goto leave; @@ -2465,7 +2501,6 @@ void todo_list_release(struct todo_list *todo_list) static struct todo_item *append_new_todo(struct todo_list *todo_list) { ALLOC_GROW(todo_list->items, todo_list->nr + 1, todo_list->alloc); - todo_list->total_nr++; return todo_list->items + todo_list->nr++; } @@ -2606,7 +2641,7 @@ static int parse_insn_line(struct repository *r, struct todo_item *item, end_of_object_name = (char *) bol + strcspn(bol, " \t\n"); saved = *end_of_object_name; *end_of_object_name = '\0'; - status = get_oid(bol, &commit_oid); + status = repo_get_oid(r, bol, &commit_oid); if (status < 0) error(_("could not parse '%s'"), bol); /* return later */ *end_of_object_name = saved; @@ -2622,7 +2657,7 @@ static int parse_insn_line(struct repository *r, struct todo_item *item, return item->commit ? 0 : -1; } -int sequencer_get_last_command(struct repository *r, enum replay_action *action) +int sequencer_get_last_command(struct repository *r UNUSED, enum replay_action *action) { const char *todo_file, *bol; struct strbuf buf = STRBUF_INIT; @@ -2656,7 +2691,7 @@ int todo_list_parse_insn_buffer(struct repository *r, char *buf, char *p = buf, *next_p; int i, res = 0, fixup_okay = file_exists(rebase_path_done()); - todo_list->current = todo_list->nr = 0; + todo_list->current = todo_list->nr = todo_list->total_nr = 0; for (i = 1; *p; i++, p = next_p) { char *eol = strchrnul(p, '\n'); @@ -2677,10 +2712,13 @@ int todo_list_parse_insn_buffer(struct repository *r, char *buf, item->commit = NULL; } + if (item->command != TODO_COMMENT) + todo_list->total_nr++; + if (fixup_okay) ; /* do nothing */ else if (is_fixup(item->command)) - return error(_("cannot '%s' without a previous commit"), + res = error(_("cannot '%s' without a previous commit"), command_to_string(item->command)); else if (!is_noop(item->command)) fixup_okay = 1; @@ -2867,7 +2905,9 @@ static int git_config_string_dup(char **dest, return 0; } -static int populate_opts_cb(const char *key, const char *value, void *data) +static int populate_opts_cb(const char *key, const char *value, + const struct config_context *ctx, + void *data) { struct replay_opts *opts = data; int error_flag = 1; @@ -2875,36 +2915,35 @@ static int populate_opts_cb(const char *key, const char *value, void *data) if (!value) error_flag = 0; else if (!strcmp(key, "options.no-commit")) - opts->no_commit = git_config_bool_or_int(key, value, &error_flag); + opts->no_commit = git_config_bool_or_int(key, value, ctx->kvi, &error_flag); else if (!strcmp(key, "options.edit")) - opts->edit = git_config_bool_or_int(key, value, &error_flag); + opts->edit = git_config_bool_or_int(key, value, ctx->kvi, &error_flag); else if (!strcmp(key, "options.allow-empty")) opts->allow_empty = - git_config_bool_or_int(key, value, &error_flag); + git_config_bool_or_int(key, value, ctx->kvi, &error_flag); else if (!strcmp(key, "options.allow-empty-message")) opts->allow_empty_message = - git_config_bool_or_int(key, value, &error_flag); + git_config_bool_or_int(key, value, ctx->kvi, &error_flag); else if (!strcmp(key, "options.keep-redundant-commits")) opts->keep_redundant_commits = - git_config_bool_or_int(key, value, &error_flag); + git_config_bool_or_int(key, value, ctx->kvi, &error_flag); else if (!strcmp(key, "options.signoff")) - opts->signoff = git_config_bool_or_int(key, value, &error_flag); + opts->signoff = git_config_bool_or_int(key, value, ctx->kvi, &error_flag); else if (!strcmp(key, "options.record-origin")) - opts->record_origin = git_config_bool_or_int(key, value, &error_flag); + opts->record_origin = git_config_bool_or_int(key, value, ctx->kvi, &error_flag); else if (!strcmp(key, "options.allow-ff")) - opts->allow_ff = git_config_bool_or_int(key, value, &error_flag); + opts->allow_ff = git_config_bool_or_int(key, value, ctx->kvi, &error_flag); else if (!strcmp(key, "options.mainline")) - opts->mainline = git_config_int(key, value); + opts->mainline = git_config_int(key, value, ctx->kvi); else if (!strcmp(key, "options.strategy")) git_config_string_dup(&opts->strategy, key, value); else if (!strcmp(key, "options.gpg-sign")) git_config_string_dup(&opts->gpg_sign, key, value); else if (!strcmp(key, "options.strategy-option")) { - ALLOC_GROW(opts->xopts, opts->xopts_nr + 1, opts->xopts_alloc); - opts->xopts[opts->xopts_nr++] = xstrdup(value); + strvec_push(&opts->xopts, value); } else if (!strcmp(key, "options.allow-rerere-auto")) opts->allow_rerere_auto = - git_config_bool_or_int(key, value, &error_flag) ? + git_config_bool_or_int(key, value, ctx->kvi, &error_flag) ? RERERE_AUTOUPDATE : RERERE_NOAUTOUPDATE; else if (!strcmp(key, "options.default-msg-cleanup")) { opts->explicit_cleanup = 1; @@ -2918,22 +2957,27 @@ static int populate_opts_cb(const char *key, const char *value, void *data) return 0; } -void parse_strategy_opts(struct replay_opts *opts, char *raw_opts) +static void parse_strategy_opts(struct replay_opts *opts, char *raw_opts) { int i; + int count; + const char **argv; char *strategy_opts_string = raw_opts; if (*strategy_opts_string == ' ') strategy_opts_string++; - opts->xopts_nr = split_cmdline(strategy_opts_string, - (const char ***)&opts->xopts); - for (i = 0; i < opts->xopts_nr; i++) { - const char *arg = opts->xopts[i]; + count = split_cmdline(strategy_opts_string, &argv); + if (count < 0) + BUG("could not split '%s': %s", strategy_opts_string, + split_cmdline_strerror(count)); + for (i = 0; i < count; i++) { + const char *arg = argv[i]; skip_prefix(arg, "--", &arg); - opts->xopts[i] = xstrdup(arg); + strvec_push(&opts->xopts, arg); } + free(argv); } static void read_strategy_opts(struct replay_opts *opts, struct strbuf *buf) @@ -2941,7 +2985,6 @@ static void read_strategy_opts(struct replay_opts *opts, struct strbuf *buf) strbuf_reset(buf); if (!read_oneliner(buf, rebase_path_strategy(), 0)) return; - free(opts->strategy); opts->strategy = strbuf_detach(buf, NULL); if (!read_oneliner(buf, rebase_path_strategy_opts(), 0)) return; @@ -3022,7 +3065,7 @@ static int read_populate_opts(struct replay_opts *opts) } if (read_oneliner(&buf, rebase_path_squash_onto(), 0)) { - if (get_oid_committish(buf.buf, &opts->squash_onto) < 0) { + if (repo_get_oid_committish(the_repository, buf.buf, &opts->squash_onto) < 0) { ret = error(_("unusable squash-onto")); goto done_rebase_i; } @@ -3050,12 +3093,13 @@ done_rebase_i: static void write_strategy_opts(struct replay_opts *opts) { - int i; struct strbuf buf = STRBUF_INIT; - for (i = 0; i < opts->xopts_nr; ++i) - strbuf_addf(&buf, " --%s", opts->xopts[i]); - + /* + * Quote strategy options so that they can be read correctly + * by split_cmdline(). + */ + quote_cmdline(&buf, opts->xopts.v); write_file(rebase_path_strategy_opts(), "%s\n", buf.buf); strbuf_release(&buf); } @@ -3078,7 +3122,7 @@ int write_basic_state(struct replay_opts *opts, const char *head_name, write_file(rebase_path_verbose(), "%s", ""); if (opts->strategy) write_file(rebase_path_strategy(), "%s\n", opts->strategy); - if (opts->xopts_nr > 0) + if (opts->xopts.nr > 0) write_strategy_opts(opts); if (opts->allow_rerere_auto == RERERE_AUTOUPDATE) @@ -3122,7 +3166,9 @@ static int walk_revs_populate_todo(struct todo_list *todo_list, while ((commit = get_revision(opts->revs))) { struct todo_item *item = append_new_todo(todo_list); - const char *commit_buffer = logmsg_reencode(commit, NULL, encoding); + const char *commit_buffer = repo_logmsg_reencode(the_repository, + commit, NULL, + encoding); const char *subject; int subject_len; @@ -3133,8 +3179,10 @@ static int walk_revs_populate_todo(struct todo_list *todo_list, item->offset_in_buf = todo_list->buf.len; subject_len = find_commit_subject(commit_buffer, &subject); strbuf_addf(&todo_list->buf, "%s %s %.*s\n", command_string, - short_commit_name(commit), subject_len, subject); - unuse_commit_buffer(commit, commit_buffer); + short_commit_name(the_repository, commit), + subject_len, subject); + repo_unuse_commit_buffer(the_repository, commit, + commit_buffer); } if (!todo_list->nr) @@ -3184,25 +3232,7 @@ static int create_seq_dir(struct repository *r) static int save_head(const char *head) { - 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) - 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); - if (written < 0) { - error_errno(_("could not write to '%s'"), git_path_head_file()); - rollback_lock_file(&head_lock); - return -1; - } - if (commit_lock_file(&head_lock) < 0) - return error(_("failed to finalize '%s'"), git_path_head_file()); - return 0; + return write_message(head, strlen(head), git_path_head_file(), 1); } static int rollback_is_safe(void) @@ -3223,7 +3253,7 @@ static int rollback_is_safe(void) else die_errno(_("could not read '%s'"), git_path_abort_safety_file()); - if (get_oid("HEAD", &actual_head)) + if (repo_get_oid(the_repository, "HEAD", &actual_head)) oidclr(&actual_head); return oideq(&actual_head, &expected_head); @@ -3379,7 +3409,8 @@ give_advice: return -1; } -static int save_todo(struct todo_list *todo_list, struct replay_opts *opts) +static int save_todo(struct todo_list *todo_list, struct replay_opts *opts, + int reschedule) { struct lock_file todo_lock = LOCK_INIT; const char *todo_path = get_todo_path(opts); @@ -3389,7 +3420,7 @@ static int save_todo(struct todo_list *todo_list, struct replay_opts *opts) * rebase -i writes "git-rebase-todo" without the currently executing * command, appending it to "done" instead. */ - if (is_rebase_i(opts)) + if (is_rebase_i(opts) && !reschedule) next++; fd = hold_lock_file_for_update(&todo_lock, todo_path, 0); @@ -3402,7 +3433,7 @@ static int save_todo(struct todo_list *todo_list, struct replay_opts *opts) if (commit_lock_file(&todo_lock) < 0) return error(_("failed to finalize '%s'"), todo_path); - if (is_rebase_i(opts) && next > 0) { + if (is_rebase_i(opts) && !reschedule && next > 0) { const char *done = rebase_path_done(); int fd = open(done, O_CREAT | O_WRONLY | O_APPEND, 0666); int ret = 0; @@ -3462,13 +3493,10 @@ static int save_opts(struct replay_opts *opts) if (opts->gpg_sign) res |= git_config_set_in_file_gently(opts_file, "options.gpg-sign", opts->gpg_sign); - if (opts->xopts) { - int i; - for (i = 0; i < opts->xopts_nr; i++) - res |= git_config_set_multivar_in_file_gently(opts_file, - "options.strategy-option", - opts->xopts[i], "^$", 0); - } + for (size_t i = 0; i < opts->xopts.nr; i++) + res |= git_config_set_multivar_in_file_gently(opts_file, + "options.strategy-option", + opts->xopts.v[i], "^$", 0); if (opts->allow_rerere_auto) res |= git_config_set_in_file_gently(opts_file, "options.allow-rerere-auto", @@ -3486,18 +3514,19 @@ static int make_patch(struct repository *r, struct commit *commit, struct replay_opts *opts) { - struct strbuf buf = STRBUF_INIT; struct rev_info log_tree_opt; const char *subject; char hex[GIT_MAX_HEXSZ + 1]; int res = 0; + if (!is_rebase_i(opts)) + BUG("make_patch should only be called when rebasing"); + oid_to_hex_r(hex, &commit->object.oid); if (write_message(hex, strlen(hex), rebase_path_stopped_sha(), 1) < 0) return -1; res |= write_rebase_head(&commit->object.oid); - strbuf_addf(&buf, "%s/patch", get_dir(opts)); memset(&log_tree_opt, 0, sizeof(log_tree_opt)); repo_init_revisions(r, &log_tree_opt, NULL); log_tree_opt.abbrev = 0; @@ -3505,25 +3534,26 @@ static int make_patch(struct repository *r, log_tree_opt.diffopt.output_format = DIFF_FORMAT_PATCH; log_tree_opt.disable_stdin = 1; log_tree_opt.no_commit_id = 1; - log_tree_opt.diffopt.file = fopen(buf.buf, "w"); + log_tree_opt.diffopt.file = fopen(rebase_path_patch(), "w"); log_tree_opt.diffopt.use_color = GIT_COLOR_NEVER; if (!log_tree_opt.diffopt.file) - res |= error_errno(_("could not open '%s'"), buf.buf); + res |= error_errno(_("could not open '%s'"), + rebase_path_patch()); else { res |= log_tree_commit(&log_tree_opt, commit); fclose(log_tree_opt.diffopt.file); } - strbuf_reset(&buf); - strbuf_addf(&buf, "%s/message", get_dir(opts)); - if (!file_exists(buf.buf)) { + if (!file_exists(rebase_path_message())) { const char *encoding = get_commit_output_encoding(); - const char *commit_buffer = logmsg_reencode(commit, NULL, encoding); + const char *commit_buffer = repo_logmsg_reencode(r, + commit, NULL, + encoding); find_commit_subject(commit_buffer, &subject); - res |= write_message(subject, strlen(subject), buf.buf, 1); - unuse_commit_buffer(commit, commit_buffer); + res |= write_message(subject, strlen(subject), rebase_path_message(), 1); + repo_unuse_commit_buffer(r, commit, + commit_buffer); } - strbuf_release(&buf); release_revisions(&log_tree_opt); return res; @@ -3534,7 +3564,7 @@ static int intend_to_amend(void) struct object_id head; char *p; - if (get_oid("HEAD", &head)) + if (repo_get_oid(the_repository, "HEAD", &head)) return error(_("cannot read HEAD")); p = oid_to_hex(&head); @@ -3571,7 +3601,7 @@ static int error_with_patch(struct repository *r, } else if (exit_code) { if (commit) fprintf_ln(stderr, _("Could not apply %s... %.*s"), - short_commit_name(commit), subject_len, subject); + short_commit_name(r, commit), subject_len, subject); else /* * We don't have the hash of the parent so @@ -3625,14 +3655,14 @@ static int do_exec(struct repository *r, const char *command_line) " git rebase --continue\n" "\n"), command_line, - dirty ? N_("and made changes to the index and/or the " - "working tree\n") : ""); + dirty ? _("and made changes to the index and/or the " + "working tree.\n") : ""); if (status == 127) /* command not found */ status = 1; } else if (dirty) { warning(_("execution succeeded: %s\nbut " - "left changes to the index and/or the working tree\n" + "left changes to the index and/or the working tree.\n" "Commit or stash your changes, and then run\n" "\n" " git rebase --continue\n" @@ -3673,7 +3703,6 @@ static int safe_append(const char *filename, const char *fmt, ...) } if (commit_lock_file(&lock) < 0) { strbuf_release(&buf); - rollback_lock_file(&lock); return error(_("failed to finalize '%s'"), filename); } @@ -3700,7 +3729,7 @@ static int do_label(struct repository *r, const char *name, int len) if (!transaction) { error("%s", err.buf); ret = -1; - } else if (get_oid("HEAD", &head_oid)) { + } else if (repo_get_oid(r, "HEAD", &head_oid)) { error(_("could not read HEAD")); ret = -1; } else if (ref_transaction_update(transaction, ref_name.buf, &head_oid, @@ -3878,7 +3907,7 @@ static int do_merge(struct repository *r, struct commit *head_commit, *merge_commit, *i; struct commit_list *bases, *j; struct commit_list *to_merge = NULL, **tail = &to_merge; - const char *strategy = !opts->xopts_nr && + const char *strategy = !opts->xopts.nr && (!opts->strategy || !strcmp(opts->strategy, "recursive") || !strcmp(opts->strategy, "ort")) ? @@ -3988,7 +4017,8 @@ static int do_merge(struct repository *r, if (commit) { const char *encoding = get_commit_output_encoding(); - const char *message = logmsg_reencode(commit, NULL, encoding); + const char *message = repo_logmsg_reencode(r, commit, NULL, + encoding); const char *body; int len; @@ -4001,7 +4031,7 @@ static int do_merge(struct repository *r, find_commit_subject(message, &body); len = strlen(body); ret = write_message(body, len, git_path_merge_msg(r), 0); - unuse_commit_buffer(commit, message); + repo_unuse_commit_buffer(r, commit, message); if (ret) { error_errno(_("could not write '%s'"), git_path_merge_msg(r)); @@ -4061,9 +4091,9 @@ static int do_merge(struct repository *r, strvec_push(&cmd.args, "octopus"); else { strvec_push(&cmd.args, strategy); - for (k = 0; k < opts->xopts_nr; k++) + for (k = 0; k < opts->xopts.nr; k++) strvec_pushf(&cmd.args, - "-X%s", opts->xopts[k]); + "-X%s", opts->xopts.v[k]); } if (!(flags & TODO_EDIT_MERGE_MSG)) strvec_push(&cmd.args, "--no-edit"); @@ -4101,7 +4131,7 @@ static int do_merge(struct repository *r, } merge_commit = to_merge->item; - bases = get_merge_bases(head_commit, merge_commit); + bases = repo_get_merge_bases(r, head_commit, merge_commit); if (bases && oideq(&merge_commit->object.oid, &bases->item->object.oid)) { ret = 0; @@ -4141,6 +4171,7 @@ static int do_merge(struct repository *r, if (ret < 0) { error(_("could not even attempt to merge '%.*s'"), merge_arg_len, arg); + unlink(git_path_merge_msg(r)); goto leave_merge; } /* @@ -4269,7 +4300,7 @@ void todo_list_filter_update_refs(struct repository *r, if (!is_null_oid(&rec->after)) continue; - for (j = 0; !found && j < todo_list->total_nr; j++) { + for (j = 0; !found && j < todo_list->nr; j++) { struct todo_item *item = &todo_list->items[j]; const char *arg = todo_list->buf.buf + item->arg_offset; @@ -4299,7 +4330,7 @@ void todo_list_filter_update_refs(struct repository *r, * For each todo_item, check if its ref is in the update_refs list. * If not, then add it as an un-updated ref. */ - for (i = 0; i < todo_list->total_nr; i++) { + for (i = 0; i < todo_list->nr; i++) { struct todo_item *item = &todo_list->items[i]; const char *arg = todo_list->buf.buf + item->arg_offset; int j, found = 0; @@ -4456,7 +4487,7 @@ void create_autostash(struct repository *r, const char *path) if (capture_command(&stash, &buf, GIT_MAX_HEXSZ)) die(_("Cannot autostash")); strbuf_trim_trailing_newline(&buf); - if (get_oid(buf.buf, &oid)) + if (repo_get_oid(r, buf.buf, &oid)) die(_("Unexpected stash response: '%s'"), buf.buf); strbuf_reset(&buf); @@ -4581,9 +4612,9 @@ static int stopped_at_head(struct repository *r) struct commit *commit; struct commit_message message; - if (get_oid("HEAD", &head) || + if (repo_get_oid(r, "HEAD", &head) || !(commit = lookup_commit(r, &head)) || - parse_commit(commit) || get_message(commit, &message)) + repo_parse_commit(r, commit) || get_message(commit, &message)) fprintf(stderr, _("Stopped at HEAD\n")); else { fprintf(stderr, _("Stopped at %s\n"), message.label); @@ -4628,6 +4659,68 @@ N_("Could not execute the todo command\n" " git rebase --edit-todo\n" " git rebase --continue\n"); +static int pick_one_commit(struct repository *r, + struct todo_list *todo_list, + struct replay_opts *opts, + int *check_todo, int* reschedule) +{ + int res; + struct todo_item *item = todo_list->items + todo_list->current; + const char *arg = todo_item_get_arg(todo_list, item); + if (is_rebase_i(opts)) + opts->reflog_message = reflog_message( + opts, command_to_string(item->command), NULL); + + res = do_pick_commit(r, item, opts, is_final_fixup(todo_list), + check_todo); + if (is_rebase_i(opts) && res < 0) { + /* Reschedule */ + *reschedule = 1; + return -1; + } + if (item->command == TODO_EDIT) { + struct commit *commit = item->commit; + if (!res) { + if (!opts->verbose) + term_clear_line(); + fprintf(stderr, _("Stopped at %s... %.*s\n"), + short_commit_name(r, commit), item->arg_len, arg); + } + return error_with_patch(r, commit, + arg, item->arg_len, opts, res, !res); + } + if (is_rebase_i(opts) && !res) + record_in_rewritten(&item->commit->object.oid, + peek_command(todo_list, 1)); + if (res && is_fixup(item->command)) { + if (res == 1) + intend_to_amend(); + return error_failed_squash(r, item->commit, opts, + item->arg_len, arg); + } else if (res && is_rebase_i(opts) && item->commit) { + int to_amend = 0; + struct object_id oid; + + /* + * If we are rewording and have either + * fast-forwarded already, or are about to + * create a new root commit, we want to amend, + * otherwise we do not. + */ + if (item->command == TODO_REWORD && + !repo_get_oid(r, "HEAD", &oid) && + (oideq(&item->commit->object.oid, &oid) || + (opts->have_squash_onto && + oideq(&opts->squash_onto, &oid)))) + to_amend = 1; + + return res | error_with_patch(r, item->commit, + arg, item->arg_len, opts, + res, to_amend); + } + return res; +} + static int pick_commits(struct repository *r, struct todo_list *todo_list, struct replay_opts *opts) @@ -4643,12 +4736,17 @@ static int pick_commits(struct repository *r, if (read_and_refresh_cache(r, opts)) return -1; + unlink(rebase_path_message()); + unlink(rebase_path_stopped_sha()); + unlink(rebase_path_amend()); + unlink(rebase_path_patch()); + while (todo_list->current < todo_list->nr) { struct todo_item *item = todo_list->items + todo_list->current; const char *arg = todo_item_get_arg(todo_list, item); int check_todo = 0; - if (save_todo(todo_list, opts)) + if (save_todo(todo_list, opts, reschedule)) return -1; if (is_rebase_i(opts)) { if (item->command != TODO_COMMENT) { @@ -4666,10 +4764,7 @@ static int pick_commits(struct repository *r, todo_list->total_nr, opts->verbose ? "\n" : "\r"); } - unlink(rebase_path_message()); unlink(rebase_path_author_script()); - unlink(rebase_path_stopped_sha()); - unlink(rebase_path_amend()); unlink(git_path_merge_head(r)); unlink(git_path_auto_merge(r)); delete_ref(NULL, "REBASE_HEAD", NULL, REF_NO_DEREF); @@ -4681,66 +4776,10 @@ static int pick_commits(struct repository *r, } } if (item->command <= TODO_SQUASH) { - if (is_rebase_i(opts)) - opts->reflog_message = reflog_message(opts, - command_to_string(item->command), NULL); - - res = do_pick_commit(r, item, opts, - is_final_fixup(todo_list), - &check_todo); - if (is_rebase_i(opts) && res < 0) { - /* Reschedule */ - advise(_(rescheduled_advice), - get_item_line_length(todo_list, - todo_list->current), - get_item_line(todo_list, - todo_list->current)); - todo_list->current--; - if (save_todo(todo_list, opts)) - return -1; - } - if (item->command == TODO_EDIT) { - struct commit *commit = item->commit; - if (!res) { - if (!opts->verbose) - term_clear_line(); - fprintf(stderr, - _("Stopped at %s... %.*s\n"), - short_commit_name(commit), - item->arg_len, arg); - } - return error_with_patch(r, commit, - arg, item->arg_len, opts, res, !res); - } - if (is_rebase_i(opts) && !res) - record_in_rewritten(&item->commit->object.oid, - peek_command(todo_list, 1)); - if (res && is_fixup(item->command)) { - if (res == 1) - intend_to_amend(); - return error_failed_squash(r, item->commit, opts, - item->arg_len, arg); - } else if (res && is_rebase_i(opts) && item->commit) { - int to_amend = 0; - struct object_id oid; - - /* - * If we are rewording and have either - * fast-forwarded already, or are about to - * create a new root commit, we want to amend, - * otherwise we do not. - */ - if (item->command == TODO_REWORD && - !get_oid("HEAD", &oid) && - (oideq(&item->commit->object.oid, &oid) || - (opts->have_squash_onto && - oideq(&opts->squash_onto, &oid)))) - to_amend = 1; - - return res | error_with_patch(r, item->commit, - arg, item->arg_len, opts, - res, to_amend); - } + res = pick_one_commit(r, todo_list, opts, &check_todo, + &reschedule); + if (!res && item->command == TODO_EDIT) + return 0; } else if (item->command == TODO_EXEC) { char *end_of_arg = (char *)(arg + item->arg_len); int saved = *end_of_arg; @@ -4788,39 +4827,32 @@ static int pick_commits(struct repository *r, get_item_line_length(todo_list, todo_list->current), get_item_line(todo_list, todo_list->current)); - todo_list->current--; - if (save_todo(todo_list, opts)) + if (save_todo(todo_list, opts, reschedule)) return -1; if (item->commit) - return error_with_patch(r, - item->commit, - arg, item->arg_len, - opts, res, 0); + write_rebase_head(&item->commit->object.oid); } else if (is_rebase_i(opts) && check_todo && !res && reread_todo_if_changed(r, todo_list, opts)) { return -1; } - todo_list->current++; if (res) return res; + + todo_list->current++; } if (is_rebase_i(opts)) { struct strbuf head_ref = STRBUF_INIT, buf = STRBUF_INIT; struct stat st; - /* Stopped in the middle, as planned? */ - if (todo_list->current < todo_list->nr) - return 0; - if (read_oneliner(&head_ref, rebase_path_head_name(), 0) && starts_with(head_ref.buf, "refs/")) { const char *msg; struct object_id head, orig; int res; - if (get_oid("HEAD", &head)) { + if (repo_get_oid(r, "HEAD", &head)) { res = error(_("cannot read HEAD")); cleanup_head_ref: strbuf_release(&head_ref); @@ -4867,8 +4899,8 @@ cleanup_head_ref: log_tree_opt.disable_stdin = 1; if (read_oneliner(&buf, rebase_path_orig_head(), 0) && - !get_oid(buf.buf, &orig) && - !get_oid("HEAD", &head)) { + !repo_get_oid(r, buf.buf, &orig) && + !repo_get_oid(r, "HEAD", &head)) { diff_tree_oid(&orig, &head, "", &log_tree_opt.diffopt); log_tree_diff_flush(&log_tree_opt); @@ -4956,11 +4988,16 @@ static int commit_staged_changes(struct repository *r, is_clean = !has_uncommitted_changes(r, 0); + if (!is_clean && !file_exists(rebase_path_message())) { + const char *gpg_opt = gpg_sign_opt_quoted(opts); + + return error(_(staged_changes_advice), gpg_opt, gpg_opt); + } if (file_exists(rebase_path_amend())) { struct strbuf rev = STRBUF_INIT; struct object_id head, to_amend; - if (get_oid("HEAD", &head)) + if (repo_get_oid(r, "HEAD", &head)) return error(_("cannot amend non-existing commit")); if (!read_oneliner(&rev, rebase_path_amend(), 0)) return error(_("invalid file: '%s'"), rebase_path_amend()); @@ -5035,18 +5072,31 @@ static int commit_staged_changes(struct repository *r, * We need to update the squash message to skip * the latest commit message. */ + int res = 0; struct commit *commit; + const char *msg; const char *path = rebase_path_squash_msg(); const char *encoding = get_commit_output_encoding(); - if (parse_head(r, &commit) || - !(p = logmsg_reencode(commit, NULL, encoding)) || - write_message(p, strlen(p), path, 0)) { - unuse_commit_buffer(commit, p); - return error(_("could not write file: " + if (parse_head(r, &commit)) + return error(_("could not parse HEAD")); + + p = repo_logmsg_reencode(r, commit, NULL, encoding); + if (!p) { + res = error(_("could not parse commit %s"), + oid_to_hex(&commit->object.oid)); + goto unuse_commit_buffer; + } + find_commit_subject(p, &msg); + if (write_message(msg, strlen(msg), path, 0)) { + res = error(_("could not write file: " "'%s'"), path); + goto unuse_commit_buffer; } - unuse_commit_buffer(commit, p); + unuse_commit_buffer: + repo_unuse_commit_buffer(r, commit, p); + if (res) + return res; } } @@ -5185,7 +5235,7 @@ int sequencer_pick_revisions(struct repository *r, if (!strlen(name)) continue; - if (!get_oid(name, &oid)) { + if (!repo_get_oid(r, name, &oid)) { if (!lookup_commit_reference_gently(r, &oid, 1)) { enum object_type type = oid_object_info(r, &oid, @@ -5228,7 +5278,7 @@ int sequencer_pick_revisions(struct repository *r, if (walk_revs_populate_todo(&todo_list, opts) || create_seq_dir(r) < 0) return -1; - if (get_oid("HEAD", &oid) && (opts->action == REPLAY_REVERT)) + if (repo_get_oid(r, "HEAD", &oid) && (opts->action == REPLAY_REVERT)) return error(_("can't revert as initial commit")); if (save_head(oid_to_hex(&oid))) return -1; @@ -5326,6 +5376,7 @@ struct label_state { struct oidmap commit2label; struct hashmap labels; struct strbuf buf; + int max_label_length; }; static const char *label_oid(struct object_id *oid, const char *label, @@ -5344,7 +5395,7 @@ static const char *label_oid(struct object_id *oid, const char *label, * For "uninteresting" commits, i.e. commits that are not to be * rebased, and which can therefore not be labeled, we use a unique * abbreviation of the commit name. This is slightly more complicated - * than calling find_unique_abbrev() because we also need to make + * than calling repo_find_unique_abbrev() because we also need to make * sure that the abbreviation does not conflict with any other * label. * @@ -5360,7 +5411,8 @@ static const char *label_oid(struct object_id *oid, const char *label, strbuf_grow(&state->buf, GIT_MAX_HEXSZ); label = p = state->buf.buf; - find_unique_abbrev_r(p, oid, default_abbrev); + repo_find_unique_abbrev_r(the_repository, p, oid, + default_abbrev); /* * We may need to extend the abbreviated hash so that there is @@ -5381,6 +5433,8 @@ static const char *label_oid(struct object_id *oid, const char *label, } } else { struct strbuf *buf = &state->buf; + int label_is_utf8 = 1; /* start with this assumption */ + size_t max_len = buf->len + state->max_label_length; /* * Sanitize labels by replacing non-alpha-numeric characters @@ -5389,14 +5443,34 @@ static const char *label_oid(struct object_id *oid, const char *label, * * Note that we retain non-ASCII UTF-8 characters (identified * via the most significant bit). They should be all acceptable - * in file names. We do not validate the UTF-8 here, that's not - * the job of this function. + * in file names. + * + * As we will use the labels as names of (loose) refs, it is + * vital that the name not be longer than the maximum component + * size of the file system (`NAME_MAX`). We are careful to + * truncate the label accordingly, allowing for the `.lock` + * suffix and for the label to be UTF-8 encoded (i.e. we avoid + * truncating in the middle of a character). */ - for (; *label; label++) - if ((*label & 0x80) || isalnum(*label)) + for (; *label && buf->len + 1 < max_len; label++) + if (isalnum(*label) || + (!label_is_utf8 && (*label & 0x80))) strbuf_addch(buf, *label); + else if (*label & 0x80) { + const char *p = label; + + utf8_width(&p, NULL); + if (p) { + if (buf->len + (p - label) > max_len) + break; + strbuf_add(buf, label, p - label); + label = p - 1; + } else { + label_is_utf8 = 0; + strbuf_addch(buf, *label); + } /* avoid leading dash and double-dashes */ - else if (buf->len && buf->buf[buf->len - 1] != '-') + } else if (buf->len && buf->buf[buf->len - 1] != '-') strbuf_addch(buf, '-'); if (!buf->len) { strbuf_addstr(buf, "rev-"); @@ -5458,7 +5532,8 @@ static int make_script_with_merges(struct pretty_print_context *pp, struct string_entry *entry; struct oidset interesting = OIDSET_INIT, child_seen = OIDSET_INIT, shown = OIDSET_INIT; - struct label_state state = { OIDMAP_INIT, { NULL }, STRBUF_INIT }; + struct label_state state = + { OIDMAP_INIT, { NULL }, STRBUF_INIT, GIT_MAX_LABEL_LENGTH }; int abbr = flags & TODO_LIST_ABBREVIATE_CMDS; const char *cmd_pick = abbr ? "p" : "pick", @@ -5466,6 +5541,8 @@ static int make_script_with_merges(struct pretty_print_context *pp, *cmd_reset = abbr ? "t" : "reset", *cmd_merge = abbr ? "m" : "merge"; + git_config_get_int("rebase.maxlabellength", &state.max_label_length); + oidmap_init(&commit2todo, 0); oidmap_init(&state.commit2label, 0); hashmap_init(&state.labels, labels_cmp, NULL, 0); @@ -5502,7 +5579,7 @@ static int make_script_with_merges(struct pretty_print_context *pp, if (!is_empty && (commit->object.flags & PATCHSAME)) { if (flags & TODO_LIST_WARN_SKIPPED_CHERRY_PICKS) warning(_("skipped previously applied commit %s"), - short_commit_name(commit)); + short_commit_name(the_repository, commit)); skipped_commit = 1; continue; } @@ -5738,7 +5815,7 @@ int sequencer_make_script(struct repository *r, struct strbuf *out, int argc, if (!is_empty && (commit->object.flags & PATCHSAME)) { if (flags & TODO_LIST_WARN_SKIPPED_CHERRY_PICKS) warning(_("skipped previously applied commit %s"), - short_commit_name(commit)); + short_commit_name(r, commit)); skipped_commit = 1; continue; } @@ -5830,7 +5907,8 @@ static void todo_list_add_exec_commands(struct todo_list *todo_list, todo_list->alloc = alloc; } -static void todo_list_to_strbuf(struct repository *r, struct todo_list *todo_list, +static void todo_list_to_strbuf(struct repository *r, + struct todo_list *todo_list, struct strbuf *buf, int num, unsigned flags) { struct todo_item *item; @@ -5859,7 +5937,7 @@ static void todo_list_to_strbuf(struct repository *r, struct todo_list *todo_lis /* add commit id */ if (item->commit) { const char *oid = flags & TODO_LIST_SHORTEN_IDS ? - short_commit_name(item->commit) : + short_commit_name(r, item->commit) : oid_to_hex(&item->commit->object.oid); if (item->command == TODO_FIXUP) { @@ -5922,7 +6000,7 @@ static int skip_unnecessary_picks(struct repository *r, continue; if (item->command != TODO_PICK) break; - if (parse_commit(item->commit)) { + if (repo_parse_commit(r, item->commit)) { return error(_("could not parse commit '%s'"), oid_to_hex(&item->commit->object.oid)); } @@ -6093,7 +6171,8 @@ int complete_action(struct repository *r, struct replay_opts *opts, unsigned fla struct object_id oid = onto->object.oid; int res; - find_unique_abbrev_r(shortonto, &oid, DEFAULT_ABBREV); + repo_find_unique_abbrev_r(r, shortonto, &oid, + DEFAULT_ABBREV); if (buf->len == 0) { struct todo_item *item = append_new_todo(todo_list); @@ -6144,7 +6223,8 @@ int complete_action(struct repository *r, struct replay_opts *opts, unsigned fla todo_list_to_strbuf(r, &new_todo, &buf2, -1, 0); strbuf_swap(&new_todo.buf, &buf2); strbuf_release(&buf2); - new_todo.total_nr -= new_todo.nr; + /* Nothing is done yet, and we're reparsing, so let's reset the count */ + new_todo.total_nr = 0; if (todo_list_parse_insn_buffer(r, new_todo.buf.buf, &new_todo) < 0) BUG("invalid todo list after expanding IDs:\n%s", new_todo.buf.buf); @@ -6165,7 +6245,7 @@ int complete_action(struct repository *r, struct replay_opts *opts, unsigned fla if (checkout_onto(r, opts, onto_name, &oid, orig_head)) goto cleanup; - if (require_clean_work_tree(r, "rebase", "", 1, 1)) + if (require_clean_work_tree(r, "rebase", NULL, 1, 1)) goto cleanup; todo_list_write_total_nr(&new_todo); @@ -6216,7 +6296,7 @@ static int skip_fixupish(const char *subject, const char **p) { int todo_list_rearrange_squash(struct todo_list *todo_list) { struct hashmap subject2item; - int rearranged = 0, *next, *tail, i, nr = 0, alloc = 0; + int rearranged = 0, *next, *tail, i, nr = 0; char **subjects; struct commit_todo_item commit_todo; struct todo_item *items = NULL; @@ -6254,12 +6334,15 @@ int todo_list_rearrange_squash(struct todo_list *todo_list) return error(_("the script was already rearranged.")); } - parse_commit(item->commit); - commit_buffer = logmsg_reencode(item->commit, NULL, "UTF-8"); + repo_parse_commit(the_repository, item->commit); + commit_buffer = repo_logmsg_reencode(the_repository, + item->commit, NULL, + "UTF-8"); find_commit_subject(commit_buffer, &subject); format_subject(&buf, subject, " "); subject = subjects[i] = strbuf_detach(&buf, &subject_len); - unuse_commit_buffer(item->commit, commit_buffer); + repo_unuse_commit_buffer(the_repository, item->commit, + commit_buffer); if (skip_fixupish(subject, &p)) { struct commit *commit2; @@ -6325,6 +6408,8 @@ int todo_list_rearrange_squash(struct todo_list *todo_list) } if (rearranged) { + ALLOC_ARRAY(items, todo_list->nr); + for (i = 0; i < todo_list->nr; i++) { enum todo_command command = todo_list->items[i].command; int cur = i; @@ -6337,16 +6422,15 @@ int todo_list_rearrange_squash(struct todo_list *todo_list) continue; while (cur >= 0) { - ALLOC_GROW(items, nr + 1, alloc); items[nr++] = todo_list->items[cur]; cur = next[cur]; } } + assert(nr == todo_list->nr); + todo_list->alloc = nr; FREE_AND_NULL(todo_list->items); todo_list->items = items; - todo_list->nr = nr; - todo_list->alloc = alloc; } free(next); @@ -6369,8 +6453,8 @@ int sequencer_determine_whence(struct repository *r, enum commit_whence *whence) if (file_exists(git_path_seq_dir())) *whence = FROM_CHERRY_PICK_MULTI; if (file_exists(rebase_path()) && - !get_oid("REBASE_HEAD", &rebase_head) && - !get_oid("CHERRY_PICK_HEAD", &cherry_pick_head) && + !repo_get_oid(r, "REBASE_HEAD", &rebase_head) && + !repo_get_oid(r, "CHERRY_PICK_HEAD", &cherry_pick_head) && oideq(&rebase_head, &cherry_pick_head)) *whence = FROM_REBASE_PICK; else |