diff options
Diffstat (limited to 'builtin/commit.c')
-rw-r--r-- | builtin/commit.c | 204 |
1 files changed, 96 insertions, 108 deletions
diff --git a/builtin/commit.c b/builtin/commit.c index 1f6dbcd0d0..8d1cac0629 100644 --- a/builtin/commit.c +++ b/builtin/commit.c @@ -142,14 +142,24 @@ static int show_ignored_in_status, have_option_m; static const char *only_include_assumed; static struct strbuf message = STRBUF_INIT; -static enum status_format { - STATUS_FORMAT_NONE = 0, - STATUS_FORMAT_LONG, - STATUS_FORMAT_SHORT, - STATUS_FORMAT_PORCELAIN, +static enum wt_status_format status_format = STATUS_FORMAT_UNSPECIFIED; - STATUS_FORMAT_UNSPECIFIED -} status_format = STATUS_FORMAT_UNSPECIFIED; +static int opt_parse_porcelain(const struct option *opt, const char *arg, int unset) +{ + enum wt_status_format *value = (enum wt_status_format *)opt->value; + if (unset) + *value = STATUS_FORMAT_NONE; + else if (!arg) + *value = STATUS_FORMAT_PORCELAIN; + else if (!strcmp(arg, "v1") || !strcmp(arg, "1")) + *value = STATUS_FORMAT_PORCELAIN; + else if (!strcmp(arg, "v2") || !strcmp(arg, "2")) + *value = STATUS_FORMAT_PORCELAIN_V2; + else + die("unsupported porcelain version '%s'", arg); + + return 0; +} static int opt_parse_m(const struct option *opt, const char *arg, int unset) { @@ -173,7 +183,7 @@ static void determine_whence(struct wt_status *s) whence = FROM_MERGE; else if (file_exists(git_path_cherry_pick_head())) { whence = FROM_CHERRY_PICK; - if (file_exists(git_path(SEQ_DIR))) + if (file_exists(git_path_seq_dir())) sequencer_in_use = 1; } else @@ -341,7 +351,7 @@ static const char *prepare_index(int argc, const char **argv, const char *prefix if (interactive) { char *old_index_env = NULL; - hold_locked_index(&index_lock, 1); + hold_locked_index(&index_lock, LOCK_DIE_ON_ERROR); refresh_cache_or_die(refresh_flags); @@ -386,8 +396,8 @@ static const char *prepare_index(int argc, const char **argv, const char *prefix * (B) on failure, rollback the real index. */ if (all || (also && pathspec.nr)) { - hold_locked_index(&index_lock, 1); - add_files_to_cache(also ? prefix : NULL, &pathspec, 0, 0); + hold_locked_index(&index_lock, LOCK_DIE_ON_ERROR); + add_files_to_cache(also ? prefix : NULL, &pathspec, 0); refresh_cache_or_die(refresh_flags); update_main_cache_tree(WRITE_TREE_SILENT); if (write_locked_index(&the_index, &index_lock, CLOSE_LOCK)) @@ -406,7 +416,7 @@ static const char *prepare_index(int argc, const char **argv, const char *prefix * We still need to refresh the index here. */ if (!only && !pathspec.nr) { - hold_locked_index(&index_lock, 1); + hold_locked_index(&index_lock, LOCK_DIE_ON_ERROR); refresh_cache_or_die(refresh_flags); if (active_cache_changed || !cache_tree_fully_valid(active_cache_tree)) @@ -458,7 +468,7 @@ static const char *prepare_index(int argc, const char **argv, const char *prefix if (read_cache() < 0) die(_("cannot read the index")); - hold_locked_index(&index_lock, 1); + hold_locked_index(&index_lock, LOCK_DIE_ON_ERROR); add_remove_files(&partial); refresh_cache(REFRESH_QUIET); update_main_cache_tree(WRITE_TREE_SILENT); @@ -486,7 +496,7 @@ static const char *prepare_index(int argc, const char **argv, const char *prefix static int run_status(FILE *fp, const char *index_file, const char *prefix, int nowarn, struct wt_status *s) { - unsigned char sha1[20]; + struct object_id oid; if (s->relative_paths) s->prefix = prefix; @@ -499,25 +509,14 @@ static int run_status(FILE *fp, const char *index_file, const char *prefix, int s->index_file = index_file; s->fp = fp; s->nowarn = nowarn; - s->is_initial = get_sha1(s->reference, sha1) ? 1 : 0; + s->is_initial = get_sha1(s->reference, oid.hash) ? 1 : 0; + if (!s->is_initial) + hashcpy(s->sha1_commit, oid.hash); + s->status_format = status_format; + s->ignore_submodule_arg = ignore_submodule_arg; wt_status_collect(s); - - switch (status_format) { - case STATUS_FORMAT_SHORT: - wt_shortstatus_print(s); - break; - case STATUS_FORMAT_PORCELAIN: - wt_porcelain_print(s); - break; - case STATUS_FORMAT_UNSPECIFIED: - die("BUG: finalize_deferred_config() should have been called"); - break; - case STATUS_FORMAT_NONE: - case STATUS_FORMAT_LONG: - wt_status_print(s); - break; - } + wt_status_print(s); return s->commitable; } @@ -791,7 +790,7 @@ static int prepare_to_commit(const char *index_file, const char *prefix, strbuf_stripspace(&sb, 0); if (signoff) - append_signoff(&sb, ignore_non_trailer(&sb), 0); + append_signoff(&sb, ignore_non_trailer(sb.buf, sb.len), 0); if (fwrite(sb.buf, 1, sb.len, s->fp) < sb.len) die_errno(_("could not write commit template")); @@ -822,9 +821,9 @@ static int prepare_to_commit(const char *index_file, const char *prefix, "If this is not correct, please remove the file\n" " %s\n" "and try again.\n"), - git_path(whence == FROM_MERGE - ? "MERGE_HEAD" - : "CHERRY_PICK_HEAD")); + whence == FROM_MERGE ? + git_path_merge_head() : + git_path_cherry_pick_head()); } fprintf(s->fp, "\n"); @@ -886,7 +885,7 @@ static int prepare_to_commit(const char *index_file, const char *prefix, commitable = run_status(s->fp, index_file, prefix, 1, s); s->use_color = saved_color_setting; } else { - unsigned char sha1[20]; + struct object_id oid; const char *parent = "HEAD"; if (!active_nr && read_cache() < 0) @@ -895,9 +894,14 @@ static int prepare_to_commit(const char *index_file, const char *prefix, if (amend) parent = "HEAD^1"; - if (get_sha1(parent, sha1)) - commitable = !!active_nr; - else { + if (get_sha1(parent, oid.hash)) { + int i, ita_nr = 0; + + for (i = 0; i < active_nr; i++) + if (ce_intent_to_add(active_cache[i])) + ita_nr++; + commitable = active_nr - ita_nr > 0; + } else { /* * Unless the user did explicitly request a submodule * ignore mode by passing a command line option we do @@ -911,7 +915,7 @@ static int prepare_to_commit(const char *index_file, const char *prefix, if (ignore_submodule_arg && !strcmp(ignore_submodule_arg, "all")) diff_flags |= DIFF_OPT_IGNORE_SUBMODULES; - commitable = index_differs_from(parent, diff_flags); + commitable = index_differs_from(parent, diff_flags, 1); } } strbuf_release(&committer_ident); @@ -956,15 +960,15 @@ static int prepare_to_commit(const char *index_file, const char *prefix, return 0; if (use_editor) { - char index[PATH_MAX]; - const char *env[2] = { NULL }; - env[0] = index; - snprintf(index, sizeof(index), "GIT_INDEX_FILE=%s", index_file); - if (launch_editor(git_path_commit_editmsg(), NULL, env)) { + struct argv_array env = ARGV_ARRAY_INIT; + + argv_array_pushf(&env, "GIT_INDEX_FILE=%s", index_file); + if (launch_editor(git_path_commit_editmsg(), NULL, env.argv)) { fprintf(stderr, _("Please supply the message using either -m or -F option.\n")); exit(1); } + argv_array_clear(&env); } if (!no_verify && @@ -1099,7 +1103,7 @@ static const char *read_commit_message(const char *name) * is not in effect here. */ static struct status_deferred_config { - enum status_format status_format; + enum wt_status_format status_format; int show_branch; } status_deferred_config = { STATUS_FORMAT_UNSPECIFIED, @@ -1109,6 +1113,7 @@ static struct status_deferred_config { static void finalize_deferred_config(struct wt_status *s) { int use_deferred_config = (status_format != STATUS_FORMAT_PORCELAIN && + status_format != STATUS_FORMAT_PORCELAIN_V2 && !s->null_termination); if (s->null_termination) { @@ -1201,10 +1206,8 @@ static int parse_and_validate_options(int argc, const char *argv[], if (also + only + all + interactive > 1) die(_("Only one of --include/--only/--all/--interactive/--patch can be used.")); - if (argc == 0 && (also || (only && !amend))) + if (argc == 0 && (also || (only && !amend && !allow_empty))) die(_("No paths with --include/--only does not make sense.")); - if (argc == 0 && only && amend) - only_include_assumed = _("Clever... amending the last one with dirty index."); if (argc > 0 && !also && !only) only_include_assumed = _("Explicit paths specified without -i or -o; assuming --only paths..."); if (!cleanup_arg || !strcmp(cleanup_arg, "default")) @@ -1329,16 +1332,16 @@ int cmd_status(int argc, const char **argv, const char *prefix) { static struct wt_status s; int fd; - unsigned char sha1[20]; + struct object_id oid; static struct option builtin_status_options[] = { OPT__VERBOSE(&verbose, N_("be verbose")), OPT_SET_INT('s', "short", &status_format, N_("show status concisely"), STATUS_FORMAT_SHORT), OPT_BOOL('b', "branch", &s.show_branch, N_("show branch information")), - OPT_SET_INT(0, "porcelain", &status_format, - N_("machine-readable output"), - STATUS_FORMAT_PORCELAIN), + { OPTION_CALLBACK, 0, "porcelain", &status_format, + N_("version"), N_("machine-readable output"), + PARSE_OPT_OPTARG, opt_parse_porcelain }, OPT_SET_INT(0, "long", &status_format, N_("show status in long format (default)"), STATUS_FORMAT_LONG), @@ -1379,8 +1382,14 @@ int cmd_status(int argc, const char **argv, const char *prefix) fd = hold_locked_index(&index_lock, 0); - s.is_initial = get_sha1(s.reference, sha1) ? 1 : 0; + s.is_initial = get_sha1(s.reference, oid.hash) ? 1 : 0; + if (!s.is_initial) + hashcpy(s.sha1_commit, oid.hash); + s.ignore_submodule_arg = ignore_submodule_arg; + s.status_format = status_format; + s.verbose = verbose; + wt_status_collect(&s); if (0 <= fd) @@ -1389,29 +1398,13 @@ int cmd_status(int argc, const char **argv, const char *prefix) if (s.relative_paths) s.prefix = prefix; - switch (status_format) { - case STATUS_FORMAT_SHORT: - wt_shortstatus_print(&s); - break; - case STATUS_FORMAT_PORCELAIN: - wt_porcelain_print(&s); - break; - case STATUS_FORMAT_UNSPECIFIED: - die("BUG: finalize_deferred_config() should have been called"); - break; - case STATUS_FORMAT_NONE: - case STATUS_FORMAT_LONG: - s.verbose = verbose; - s.ignore_submodule_arg = ignore_submodule_arg; - wt_status_print(&s); - break; - } + wt_status_print(&s); return 0; } static const char *implicit_ident_advice(void) { - char *user_config = expand_user_path("~/.gitconfig"); + char *user_config = expand_user_path("~/.gitconfig", 0); char *xdg_config = xdg_config_home("config"); int config_exists = file_exists(user_config) || file_exists(xdg_config); @@ -1425,19 +1418,19 @@ static const char *implicit_ident_advice(void) } -static void print_summary(const char *prefix, const unsigned char *sha1, +static void print_summary(const char *prefix, const struct object_id *oid, int initial_commit) { struct rev_info rev; struct commit *commit; struct strbuf format = STRBUF_INIT; - unsigned char junk_sha1[20]; + struct object_id junk_oid; const char *head; struct pretty_print_context pctx = {0}; struct strbuf author_ident = STRBUF_INIT; struct strbuf committer_ident = STRBUF_INIT; - commit = lookup_commit(sha1); + commit = lookup_commit(oid->hash); if (!commit) die(_("couldn't look up newly created commit")); if (parse_commit(commit)) @@ -1484,7 +1477,7 @@ static void print_summary(const char *prefix, const unsigned char *sha1, rev.diffopt.break_opt = 0; diff_setup_done(&rev.diffopt); - head = resolve_ref_unsafe("HEAD", 0, junk_sha1, NULL); + head = resolve_ref_unsafe("HEAD", 0, junk_oid.hash, NULL); if (!strcmp(head, "HEAD")) head = _("detached HEAD"); else @@ -1529,15 +1522,13 @@ static int git_commit_config(const char *k, const char *v, void *cb) return git_status_config(k, v, s); } -static int run_rewrite_hook(const unsigned char *oldsha1, - const unsigned char *newsha1) +static int run_rewrite_hook(const struct object_id *oldoid, + const struct object_id *newoid) { - /* oldsha1 SP newsha1 LF NUL */ - static char buf[2*40 + 3]; struct child_process proc = CHILD_PROCESS_INIT; const char *argv[3]; int code; - size_t n; + struct strbuf sb = STRBUF_INIT; argv[0] = find_hook("post-rewrite"); if (!argv[0]) @@ -1553,34 +1544,33 @@ static int run_rewrite_hook(const unsigned char *oldsha1, code = start_command(&proc); if (code) return code; - n = snprintf(buf, sizeof(buf), "%s %s\n", - sha1_to_hex(oldsha1), sha1_to_hex(newsha1)); + strbuf_addf(&sb, "%s %s\n", oid_to_hex(oldoid), oid_to_hex(newoid)); sigchain_push(SIGPIPE, SIG_IGN); - write_in_full(proc.in, buf, n); + write_in_full(proc.in, sb.buf, sb.len); close(proc.in); + strbuf_release(&sb); sigchain_pop(SIGPIPE); return finish_command(&proc); } int run_commit_hook(int editor_is_used, const char *index_file, const char *name, ...) { - const char *hook_env[3] = { NULL }; - char index[PATH_MAX]; + struct argv_array hook_env = ARGV_ARRAY_INIT; va_list args; int ret; - snprintf(index, sizeof(index), "GIT_INDEX_FILE=%s", index_file); - hook_env[0] = index; + argv_array_pushf(&hook_env, "GIT_INDEX_FILE=%s", index_file); /* * Let the hook know that no editor will be launched. */ if (!editor_is_used) - hook_env[1] = "GIT_EDITOR=:"; + argv_array_push(&hook_env, "GIT_EDITOR=:"); va_start(args, name); - ret = run_hook_ve(hook_env, name, args); + ret = run_hook_ve(hook_env.argv,name, args); va_end(args); + argv_array_clear(&hook_env); return ret; } @@ -1617,7 +1607,7 @@ int cmd_commit(int argc, const char **argv, const char *prefix) OPT_BOOL(0, "interactive", &interactive, N_("interactively add files")), OPT_BOOL('p', "patch", &patch_interactive, N_("interactively add changes")), OPT_BOOL('o', "only", &only, N_("commit only specified files")), - OPT_BOOL('n', "no-verify", &no_verify, N_("bypass pre-commit hook")), + OPT_BOOL('n', "no-verify", &no_verify, N_("bypass pre-commit and commit-msg hooks")), OPT_BOOL(0, "dry-run", &dry_run, N_("show what would be committed")), OPT_SET_INT(0, "short", &status_format, N_("show status concisely"), STATUS_FORMAT_SHORT), @@ -1646,8 +1636,8 @@ int cmd_commit(int argc, const char **argv, const char *prefix) struct strbuf author_ident = STRBUF_INIT; const char *index_file, *reflog_msg; char *nl; - unsigned char sha1[20]; - struct commit_list *parents = NULL, **pptr = &parents; + struct object_id oid; + struct commit_list *parents = NULL; struct stat statbuf; struct commit *current_head = NULL; struct commit_extra_header *extra = NULL; @@ -1661,10 +1651,10 @@ int cmd_commit(int argc, const char **argv, const char *prefix) status_format = STATUS_FORMAT_NONE; /* Ignore status.short */ s.colopts = 0; - if (get_sha1("HEAD", sha1)) + if (get_sha1("HEAD", oid.hash)) current_head = NULL; else { - current_head = lookup_commit_or_die(sha1, "HEAD"); + current_head = lookup_commit_or_die(oid.hash, "HEAD"); if (parse_commit(current_head)) die(_("could not parse HEAD commit")); } @@ -1693,20 +1683,18 @@ int cmd_commit(int argc, const char **argv, const char *prefix) if (!reflog_msg) reflog_msg = "commit (initial)"; } else if (amend) { - struct commit_list *c; - if (!reflog_msg) reflog_msg = "commit (amend)"; - for (c = current_head->parents; c; c = c->next) - pptr = &commit_list_insert(c->item, pptr)->next; + parents = copy_commit_list(current_head->parents); } else if (whence == FROM_MERGE) { struct strbuf m = STRBUF_INIT; FILE *fp; int allow_fast_forward = 1; + struct commit_list **pptr = &parents; if (!reflog_msg) reflog_msg = "commit (merge)"; - pptr = &commit_list_insert(current_head, pptr)->next; + pptr = commit_list_append(current_head, pptr); fp = fopen(git_path_merge_head(), "r"); if (fp == NULL) die_errno(_("could not open '%s' for reading"), @@ -1717,7 +1705,7 @@ int cmd_commit(int argc, const char **argv, const char *prefix) parent = get_merge_parent(m.buf); if (!parent) die(_("Corrupt MERGE_HEAD file (%s)"), m.buf); - pptr = &commit_list_insert(parent, pptr)->next; + pptr = commit_list_append(parent, pptr); } fclose(fp); strbuf_release(&m); @@ -1734,7 +1722,7 @@ int cmd_commit(int argc, const char **argv, const char *prefix) reflog_msg = (whence == FROM_CHERRY_PICK) ? "commit (cherry-pick)" : "commit"; - pptr = &commit_list_insert(current_head, pptr)->next; + commit_list_insert(current_head, &parents); } /* Finally, get the commit message */ @@ -1747,7 +1735,7 @@ int cmd_commit(int argc, const char **argv, const char *prefix) if (verbose || /* Truncate the message just before the diff, if any. */ cleanup_mode == CLEANUP_SCISSORS) - wt_status_truncate_message_at_cut_line(&sb); + strbuf_setlen(&sb, wt_status_locate_end(sb.buf, sb.len)); if (cleanup_mode != CLEANUP_NONE) strbuf_stripspace(&sb, cleanup_mode == CLEANUP_ALL); @@ -1771,7 +1759,7 @@ int cmd_commit(int argc, const char **argv, const char *prefix) } if (commit_tree_extended(sb.buf, sb.len, active_cache_tree->sha1, - parents, sha1, author_ident.buf, sign_commit, extra)) { + parents, oid.hash, author_ident.buf, sign_commit, extra)) { rollback_index_files(); die(_("failed to write commit object")); } @@ -1788,7 +1776,7 @@ int cmd_commit(int argc, const char **argv, const char *prefix) transaction = ref_transaction_begin(&err); if (!transaction || - ref_transaction_update(transaction, "HEAD", sha1, + ref_transaction_update(transaction, "HEAD", oid.hash, current_head ? current_head->object.oid.hash : null_sha1, 0, sb.buf, &err) || @@ -1817,13 +1805,13 @@ int cmd_commit(int argc, const char **argv, const char *prefix) cfg = init_copy_notes_for_rewrite("amend"); if (cfg) { /* we are amending, so current_head is not NULL */ - copy_note_for_rewrite(cfg, current_head->object.oid.hash, sha1); + copy_note_for_rewrite(cfg, current_head->object.oid.hash, oid.hash); finish_copy_notes_for_rewrite(cfg, "Notes added by 'git commit --amend'"); } - run_rewrite_hook(current_head->object.oid.hash, sha1); + run_rewrite_hook(¤t_head->object.oid, &oid); } if (!quiet) - print_summary(prefix, sha1, !current_head); + print_summary(prefix, &oid, !current_head); strbuf_release(&err); return 0; |