static int suggest_conflicts(void) { FILE *fp; int pos; fp = fopen(git_path("MERGE_MSG"), "a"); if (!fp) die("Could open %s for writing", git_path("MERGE_MSG")); fprintf(fp, "\nConflicts:\n"); for (pos = 0; pos < active_nr; pos++) { struct cache_entry *ce = active_cache[pos]; if (ce_stage(ce)) { fprintf(fp, "\t%s\n", ce->name); while (pos + 1 < active_nr && !strcmp(ce->name, active_cache[pos + 1]->name)) pos++; } } fclose(fp); rerere(); printf("Automatic merge failed; " "fix conflicts and then commit the result.\n"); return 1; }
static int suggest_conflicts(int renormalizing) { const char *filename; FILE *fp; int pos; filename = git_path("MERGE_MSG"); fp = fopen(filename, "a"); if (!fp) die_errno(_("Could not open '%s' for writing"), filename); fprintf(fp, "\nConflicts:\n"); for (pos = 0; pos < active_nr; pos++) { struct cache_entry *ce = active_cache[pos]; if (ce_stage(ce)) { fprintf(fp, "\t%s\n", ce->name); while (pos + 1 < active_nr && !strcmp(ce->name, active_cache[pos + 1]->name)) pos++; } } fclose(fp); rerere(allow_rerere_auto); printf(_("Automatic merge failed; " "fix conflicts and then commit the result.\n")); return 1; }
static void do_recursive_merge(struct commit *base, struct commit *next, const char *base_label, const char *next_label, unsigned char *head, struct strbuf *msgbuf, char *defmsg) { struct merge_options o; struct tree *result, *next_tree, *base_tree, *head_tree; int clean, index_fd; static struct lock_file index_lock; index_fd = hold_locked_index(&index_lock, 1); read_cache(); init_merge_options(&o); o.ancestor = base ? base_label : "(empty tree)"; o.branch1 = "HEAD"; o.branch2 = next ? next_label : "(empty tree)"; head_tree = parse_tree_indirect(head); next_tree = next ? next->tree : empty_tree(); base_tree = base ? base->tree : empty_tree(); clean = merge_trees(&o, head_tree, next_tree, base_tree, &result); if (active_cache_changed && (write_cache(index_fd, active_cache, active_nr) || commit_locked_index(&index_lock))) die("%s: Unable to write new index file", me); rollback_lock_file(&index_lock); if (!clean) { int i; strbuf_addstr(msgbuf, "\nConflicts:\n\n"); for (i = 0; i < active_nr;) { struct cache_entry *ce = active_cache[i++]; if (ce_stage(ce)) { strbuf_addch(msgbuf, '\t'); strbuf_addstr(msgbuf, ce->name); strbuf_addch(msgbuf, '\n'); while (i < active_nr && !strcmp(ce->name, active_cache[i]->name)) i++; } } write_message(msgbuf, defmsg); fprintf(stderr, "Automatic %s failed.%s\n", me, help_msg()); rerere(allow_rerere_auto); exit(1); } write_message(msgbuf, defmsg); fprintf(stderr, "Finished one %s.\n", me); }
static int suggest_conflicts(void) { const char *filename; FILE *fp; struct strbuf msgbuf = STRBUF_INIT; filename = git_path_merge_msg(); fp = fopen(filename, "a"); if (!fp) die_errno(_("Could not open '%s' for writing"), filename); append_conflicts_hint(&msgbuf); fputs(msgbuf.buf, fp); strbuf_release(&msgbuf); fclose(fp); rerere(allow_rerere_auto); printf(_("Automatic merge failed; " "fix conflicts and then commit the result.\n")); return 1; }
int cmd_rerere(int argc, const char **argv, const char *prefix) { struct string_list merge_rr = { NULL, 0, 0, 1 }; int i, fd; if (argc < 2) return rerere(); fd = setup_rerere(&merge_rr); if (fd < 0) return 0; if (!strcmp(argv[1], "clear")) { for (i = 0; i < merge_rr.nr; i++) { const char *name = (const char *)merge_rr.items[i].util; if (!has_resolution(name)) unlink_rr_item(name); } unlink(git_path("rr-cache/MERGE_RR")); } else if (!strcmp(argv[1], "gc")) garbage_collect(&merge_rr); else if (!strcmp(argv[1], "status")) for (i = 0; i < merge_rr.nr; i++) printf("%s\n", merge_rr.items[i].string); else if (!strcmp(argv[1], "diff")) for (i = 0; i < merge_rr.nr; i++) { const char *path = merge_rr.items[i].string; const char *name = (const char *)merge_rr.items[i].util; diff_two(rr_path(name, "preimage"), path, path, path); } else usage(git_rerere_usage); string_list_clear(&merge_rr, 1); return 0; }
int cmd_commit(int argc, const char **argv, const char *prefix) { static struct wt_status s; static struct option builtin_commit_options[] = { OPT__QUIET(&quiet, N_("suppress summary after successful commit")), OPT__VERBOSE(&verbose, N_("show diff in commit message template")), OPT_GROUP(N_("Commit message options")), OPT_FILENAME('F', "file", &logfile, N_("read message from file")), OPT_STRING(0, "author", &force_author, N_("author"), N_("override author for commit")), OPT_STRING(0, "date", &force_date, N_("date"), N_("override date for commit")), OPT_CALLBACK('m', "message", &message, N_("message"), N_("commit message"), opt_parse_m), OPT_STRING('c', "reedit-message", &edit_message, N_("commit"), N_("reuse and edit message from specified commit")), OPT_STRING('C', "reuse-message", &use_message, N_("commit"), N_("reuse message from specified commit")), OPT_STRING(0, "fixup", &fixup_message, N_("commit"), N_("use autosquash formatted message to fixup specified commit")), OPT_STRING(0, "squash", &squash_message, N_("commit"), N_("use autosquash formatted message to squash specified commit")), OPT_BOOL(0, "reset-author", &renew_authorship, N_("the commit is authored by me now (used with -C/-c/--amend)")), OPT_BOOL('s', "signoff", &signoff, N_("add Signed-off-by:")), OPT_FILENAME('t', "template", &template_file, N_("use specified template file")), OPT_BOOL('e', "edit", &edit_flag, N_("force edit of commit")), OPT_STRING(0, "cleanup", &cleanup_arg, N_("default"), N_("how to strip spaces and #comments from message")), OPT_BOOL(0, "status", &include_status, N_("include status in commit message template")), { OPTION_STRING, 'S', "gpg-sign", &sign_commit, N_("key id"), N_("GPG sign commit"), PARSE_OPT_OPTARG, NULL, (intptr_t) "" }, /* end commit message options */ OPT_GROUP(N_("Commit contents options")), OPT_BOOL('a', "all", &all, N_("commit all changed files")), OPT_BOOL('i', "include", &also, N_("add specified files to index for commit")), 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(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), OPT_BOOL(0, "branch", &s.show_branch, N_("show branch information")), OPT_SET_INT(0, "porcelain", &status_format, N_("machine-readable output"), STATUS_FORMAT_PORCELAIN), OPT_SET_INT(0, "long", &status_format, N_("show status in long format (default)"), STATUS_FORMAT_LONG), OPT_BOOL('z', "null", &s.null_termination, N_("terminate entries with NUL")), OPT_BOOL(0, "amend", &amend, N_("amend previous commit")), OPT_BOOL(0, "no-post-rewrite", &no_post_rewrite, N_("bypass post-rewrite hook")), { OPTION_STRING, 'u', "untracked-files", &untracked_files_arg, N_("mode"), N_("show untracked files, optional modes: all, normal, no. (Default: all)"), PARSE_OPT_OPTARG, NULL, (intptr_t)"all" }, /* end commit contents options */ OPT_HIDDEN_BOOL(0, "allow-empty", &allow_empty, N_("ok to record an empty change")), OPT_HIDDEN_BOOL(0, "allow-empty-message", &allow_empty_message, N_("ok to record a change with an empty message")), OPT_END() }; struct strbuf sb = STRBUF_INIT; struct strbuf author_ident = STRBUF_INIT; const char *index_file, *reflog_msg; char *nl; unsigned char sha1[20]; struct ref_lock *ref_lock; struct commit_list *parents = NULL, **pptr = &parents; struct stat statbuf; struct commit *current_head = NULL; struct commit_extra_header *extra = NULL; if (argc == 2 && !strcmp(argv[1], "-h")) usage_with_options(builtin_commit_usage, builtin_commit_options); status_init_config(&s, git_commit_config); status_format = STATUS_FORMAT_NONE; /* Ignore status.short */ s.colopts = 0; if (get_sha1("HEAD", sha1)) current_head = NULL; else { current_head = lookup_commit_or_die(sha1, "HEAD"); if (parse_commit(current_head)) die(_("could not parse HEAD commit")); } argc = parse_and_validate_options(argc, argv, builtin_commit_options, builtin_commit_usage, prefix, current_head, &s); if (dry_run) return dry_run_commit(argc, argv, prefix, current_head, &s); index_file = prepare_index(argc, argv, prefix, current_head, 0); /* Set up everything for writing the commit object. This includes running hooks, writing the trees, and interacting with the user. */ if (!prepare_to_commit(index_file, prefix, current_head, &s, &author_ident)) { rollback_index_files(); return 1; } /* Determine parents */ reflog_msg = getenv("GIT_REFLOG_ACTION"); if (!current_head) { 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; } else if (whence == FROM_MERGE) { struct strbuf m = STRBUF_INIT; FILE *fp; int allow_fast_forward = 1; if (!reflog_msg) reflog_msg = "commit (merge)"; pptr = &commit_list_insert(current_head, pptr)->next; fp = fopen(git_path("MERGE_HEAD"), "r"); if (fp == NULL) die_errno(_("could not open '%s' for reading"), git_path("MERGE_HEAD")); while (strbuf_getline(&m, fp, '\n') != EOF) { struct commit *parent; parent = get_merge_parent(m.buf); if (!parent) die(_("Corrupt MERGE_HEAD file (%s)"), m.buf); pptr = &commit_list_insert(parent, pptr)->next; } fclose(fp); strbuf_release(&m); if (!stat(git_path("MERGE_MODE"), &statbuf)) { if (strbuf_read_file(&sb, git_path("MERGE_MODE"), 0) < 0) die_errno(_("could not read MERGE_MODE")); if (!strcmp(sb.buf, "no-ff")) allow_fast_forward = 0; } if (allow_fast_forward) parents = reduce_heads(parents); } else { if (!reflog_msg) reflog_msg = (whence == FROM_CHERRY_PICK) ? "commit (cherry-pick)" : "commit"; pptr = &commit_list_insert(current_head, pptr)->next; } /* Finally, get the commit message */ strbuf_reset(&sb); if (strbuf_read_file(&sb, git_path(commit_editmsg), 0) < 0) { int saved_errno = errno; rollback_index_files(); die(_("could not read commit message: %s"), strerror(saved_errno)); } /* Truncate the message just before the diff, if any. */ if (verbose) wt_status_truncate_message_at_cut_line(&sb); if (cleanup_mode != CLEANUP_NONE) stripspace(&sb, cleanup_mode == CLEANUP_ALL); if (template_untouched(&sb) && !allow_empty_message) { rollback_index_files(); fprintf(stderr, _("Aborting commit; you did not edit the message.\n")); exit(1); } if (message_is_empty(&sb) && !allow_empty_message) { rollback_index_files(); fprintf(stderr, _("Aborting commit due to empty commit message.\n")); exit(1); } if (amend) { const char *exclude_gpgsig[2] = { "gpgsig", NULL }; extra = read_commit_extra_headers(current_head, exclude_gpgsig); } else { struct commit_extra_header **tail = &extra; append_merge_tag_headers(parents, &tail); } if (commit_tree_extended(&sb, active_cache_tree->sha1, parents, sha1, author_ident.buf, sign_commit, extra)) { rollback_index_files(); die(_("failed to write commit object")); } strbuf_release(&author_ident); free_commit_extra_headers(extra); ref_lock = lock_any_ref_for_update("HEAD", !current_head ? NULL : current_head->object.sha1, 0, NULL); nl = strchr(sb.buf, '\n'); if (nl) strbuf_setlen(&sb, nl + 1 - sb.buf); else strbuf_addch(&sb, '\n'); strbuf_insert(&sb, 0, reflog_msg, strlen(reflog_msg)); strbuf_insert(&sb, strlen(reflog_msg), ": ", 2); if (!ref_lock) { rollback_index_files(); die(_("cannot lock HEAD ref")); } if (write_ref_sha1(ref_lock, sha1, sb.buf) < 0) { rollback_index_files(); die(_("cannot update HEAD ref")); } unlink(git_path("CHERRY_PICK_HEAD")); unlink(git_path("REVERT_HEAD")); unlink(git_path("MERGE_HEAD")); unlink(git_path("MERGE_MSG")); unlink(git_path("MERGE_MODE")); unlink(git_path("SQUASH_MSG")); if (commit_index_files()) die (_("Repository has been updated, but unable to write\n" "new_index file. Check that disk is not full or quota is\n" "not exceeded, and then \"git reset HEAD\" to recover.")); rerere(0); run_hook(get_index_file(), "post-commit", NULL); if (amend && !no_post_rewrite) { struct notes_rewrite_cfg *cfg; 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.sha1, sha1); finish_copy_notes_for_rewrite(cfg, "Notes added by 'git commit --amend'"); } run_rewrite_hook(current_head->object.sha1, sha1); } if (!quiet) print_summary(prefix, sha1, !current_head); return 0; }
static int do_pick_commit(struct commit *commit, struct replay_opts *opts) { unsigned char head[20]; struct commit *base, *next, *parent; const char *base_label, *next_label; struct commit_message msg = { NULL, NULL, NULL, NULL }; struct strbuf msgbuf = STRBUF_INIT; int res, unborn = 0, allow; if (opts->no_commit) { /* * We do not intend to commit immediately. We just want to * merge the differences in, so let's compute the tree * that represents the "current" state for merge-recursive * to work on. */ if (write_cache_as_tree(head, 0, NULL)) die (_("Your index file is unmerged.")); } else { unborn = get_sha1("HEAD", head); if (unborn) hashcpy(head, EMPTY_TREE_SHA1_BIN); if (index_differs_from(unborn ? EMPTY_TREE_SHA1_HEX : "HEAD", 0)) return error_dirty_index(opts); } discard_cache(); if (!commit->parents) { parent = NULL; } else if (commit->parents->next) { /* Reverting or cherry-picking a merge commit */ int cnt; struct commit_list *p; if (!opts->mainline) return error(_("Commit %s is a merge but no -m option was given."), oid_to_hex(&commit->object.oid)); for (cnt = 1, p = commit->parents; cnt != opts->mainline && p; cnt++) p = p->next; if (cnt != opts->mainline || !p) return error(_("Commit %s does not have parent %d"), oid_to_hex(&commit->object.oid), opts->mainline); parent = p->item; } else if (0 < opts->mainline) return error(_("Mainline was specified but commit %s is not a merge."), oid_to_hex(&commit->object.oid)); else parent = commit->parents->item; if (opts->allow_ff && ((parent && !hashcmp(parent->object.oid.hash, head)) || (!parent && unborn))) return fast_forward_to(commit->object.oid.hash, head, unborn, opts); if (parent && parse_commit(parent) < 0) /* TRANSLATORS: The first %s will be "revert" or "cherry-pick", the second %s a SHA1 */ return error(_("%s: cannot parse parent commit %s"), action_name(opts), oid_to_hex(&parent->object.oid)); if (get_message(commit, &msg) != 0) return error(_("Cannot get commit message for %s"), oid_to_hex(&commit->object.oid)); /* * "commit" is an existing commit. We would want to apply * the difference it introduces since its first parent "prev" * on top of the current HEAD if we are cherry-pick. Or the * reverse of it if we are revert. */ if (opts->action == REPLAY_REVERT) { base = commit; base_label = msg.label; next = parent; next_label = msg.parent_label; strbuf_addstr(&msgbuf, "Revert \""); strbuf_addstr(&msgbuf, msg.subject); strbuf_addstr(&msgbuf, "\"\n\nThis reverts commit "); strbuf_addstr(&msgbuf, oid_to_hex(&commit->object.oid)); if (commit->parents && commit->parents->next) { strbuf_addstr(&msgbuf, ", reversing\nchanges made to "); strbuf_addstr(&msgbuf, oid_to_hex(&parent->object.oid)); } strbuf_addstr(&msgbuf, ".\n"); } else { const char *p; base = parent; base_label = msg.parent_label; next = commit; next_label = msg.label; /* * Append the commit log message to msgbuf; it starts * after the tree, parent, author, committer * information followed by "\n\n". */ p = strstr(msg.message, "\n\n"); if (p) strbuf_addstr(&msgbuf, skip_blank_lines(p + 2)); if (opts->record_origin) { if (!has_conforming_footer(&msgbuf, NULL, 0)) strbuf_addch(&msgbuf, '\n'); strbuf_addstr(&msgbuf, cherry_picked_prefix); strbuf_addstr(&msgbuf, oid_to_hex(&commit->object.oid)); strbuf_addstr(&msgbuf, ")\n"); } } if (!opts->strategy || !strcmp(opts->strategy, "recursive") || opts->action == REPLAY_REVERT) { res = do_recursive_merge(base, next, base_label, next_label, head, &msgbuf, opts); write_message(&msgbuf, git_path_merge_msg()); } else { struct commit_list *common = NULL; struct commit_list *remotes = NULL; write_message(&msgbuf, git_path_merge_msg()); commit_list_insert(base, &common); commit_list_insert(next, &remotes); res = try_merge_command(opts->strategy, opts->xopts_nr, opts->xopts, common, sha1_to_hex(head), remotes); free_commit_list(common); free_commit_list(remotes); } /* * If the merge was clean or if it failed due to conflict, we write * CHERRY_PICK_HEAD for the subsequent invocation of commit to use. * However, if the merge did not even start, then we don't want to * write it at all. */ if (opts->action == REPLAY_PICK && !opts->no_commit && (res == 0 || res == 1)) update_ref(NULL, "CHERRY_PICK_HEAD", commit->object.oid.hash, NULL, REF_NODEREF, UPDATE_REFS_DIE_ON_ERR); if (opts->action == REPLAY_REVERT && ((opts->no_commit && res == 0) || res == 1)) update_ref(NULL, "REVERT_HEAD", commit->object.oid.hash, NULL, REF_NODEREF, UPDATE_REFS_DIE_ON_ERR); if (res) { error(opts->action == REPLAY_REVERT ? _("could not revert %s... %s") : _("could not apply %s... %s"), find_unique_abbrev(commit->object.oid.hash, DEFAULT_ABBREV), msg.subject); print_advice(res == 1, opts); rerere(opts->allow_rerere_auto); goto leave; } allow = allow_empty(opts, commit); if (allow < 0) { res = allow; goto leave; } if (!opts->no_commit) res = run_git_commit(git_path_merge_msg(), opts, allow); leave: free_message(commit, &msg); return res; }
static int do_apply_stash(const char *prefix, struct stash_info *info, int index, int quiet) { int ret; int has_index = index; struct merge_options o; struct object_id c_tree; struct object_id index_tree; struct commit *result; const struct object_id *bases[1]; read_cache_preload(NULL); if (refresh_cache(REFRESH_QUIET)) return -1; if (write_cache_as_tree(&c_tree, 0, NULL)) return error(_("cannot apply a stash in the middle of a merge")); if (index) { if (oideq(&info->b_tree, &info->i_tree) || oideq(&c_tree, &info->i_tree)) { has_index = 0; } else { struct strbuf out = STRBUF_INIT; if (diff_tree_binary(&out, &info->w_commit)) { strbuf_release(&out); return error(_("could not generate diff %s^!."), oid_to_hex(&info->w_commit)); } ret = apply_cached(&out); strbuf_release(&out); if (ret) return error(_("conflicts in index." "Try without --index.")); discard_cache(); read_cache(); if (write_cache_as_tree(&index_tree, 0, NULL)) return error(_("could not save index tree")); reset_head(); } } if (info->has_u && restore_untracked(&info->u_tree)) return error(_("could not restore untracked files from stash")); init_merge_options(&o, the_repository); o.branch1 = "Updated upstream"; o.branch2 = "Stashed changes"; if (oideq(&info->b_tree, &c_tree)) o.branch1 = "Version stash was based on"; if (quiet) o.verbosity = 0; if (o.verbosity >= 3) printf_ln(_("Merging %s with %s"), o.branch1, o.branch2); bases[0] = &info->b_tree; ret = merge_recursive_generic(&o, &c_tree, &info->w_tree, 1, bases, &result); if (ret) { rerere(0); if (index) fprintf_ln(stderr, _("Index was not unstashed.")); return ret; } if (has_index) { if (reset_tree(&index_tree, 0, 0)) return -1; } else { struct strbuf out = STRBUF_INIT; if (get_newly_staged(&out, &c_tree)) { strbuf_release(&out); return -1; } if (reset_tree(&c_tree, 0, 1)) { strbuf_release(&out); return -1; } ret = update_index(&out); strbuf_release(&out); if (ret) return -1; discard_cache(); } if (quiet) { if (refresh_cache(REFRESH_QUIET)) warning("could not refresh index"); } else { struct child_process cp = CHILD_PROCESS_INIT; /* * Status is quite simple and could be replaced with calls to * wt_status in the future, but it adds complexities which may * require more tests. */ cp.git_cmd = 1; cp.dir = prefix; argv_array_push(&cp.args, "status"); run_command(&cp); } return 0; }
int cmd_rerere(int argc, const char **argv, const char *prefix) { struct string_list merge_rr = STRING_LIST_INIT_DUP; int i, fd, autoupdate = -1, flags = 0; struct option options[] = { OPT_SET_INT(0, "rerere-autoupdate", &autoupdate, N_("register clean resolutions in index"), 1), OPT_END(), }; argc = parse_options(argc, argv, prefix, options, rerere_usage, 0); git_config(git_xmerge_config, NULL); if (autoupdate == 1) flags = RERERE_AUTOUPDATE; if (autoupdate == 0) flags = RERERE_NOAUTOUPDATE; if (argc < 1) return rerere(flags); if (!strcmp(argv[0], "forget")) { struct pathspec pathspec; if (argc < 2) warning("'git rerere forget' without paths is deprecated"); parse_pathspec(&pathspec, 0, PATHSPEC_PREFER_CWD, prefix, argv + 1); return rerere_forget(&pathspec); } fd = setup_rerere(&merge_rr, flags); if (fd < 0) return 0; if (!strcmp(argv[0], "clear")) { rerere_clear(&merge_rr); } else if (!strcmp(argv[0], "gc")) rerere_gc(&merge_rr); else if (!strcmp(argv[0], "status")) for (i = 0; i < merge_rr.nr; i++) printf("%s\n", merge_rr.items[i].string); else if (!strcmp(argv[0], "remaining")) { rerere_remaining(&merge_rr); for (i = 0; i < merge_rr.nr; i++) { if (merge_rr.items[i].util != RERERE_RESOLVED) printf("%s\n", merge_rr.items[i].string); else /* prepare for later call to * string_list_clear() */ merge_rr.items[i].util = NULL; } } else if (!strcmp(argv[0], "diff")) for (i = 0; i < merge_rr.nr; i++) { const char *path = merge_rr.items[i].string; const char *name = (const char *)merge_rr.items[i].util; diff_two(rerere_path(name, "preimage"), path, path, path); } else usage_with_options(rerere_usage, options); string_list_clear(&merge_rr, 1); return 0; }
static int do_pick_commit(void) { unsigned char head[20]; struct commit *base, *next, *parent; const char *base_label, *next_label; struct commit_message msg = { NULL, NULL, NULL, NULL, NULL }; char *defmsg = NULL; struct strbuf msgbuf = STRBUF_INIT; if (no_commit) { /* * We do not intend to commit immediately. We just want to * merge the differences in, so let's compute the tree * that represents the "current" state for merge-recursive * to work on. */ if (write_cache_as_tree(head, 0, NULL)) die ("Your index file is unmerged."); } else { if (get_sha1("HEAD", head)) die ("You do not have a valid HEAD"); if (index_differs_from("HEAD", 0)) die_dirty_index(me); } discard_cache(); if (!commit->parents) { if (action == REVERT) die ("Cannot revert a root commit"); parent = NULL; } else if (commit->parents->next) { /* Reverting or cherry-picking a merge commit */ int cnt; struct commit_list *p; if (!mainline) die("Commit %s is a merge but no -m option was given.", sha1_to_hex(commit->object.sha1)); for (cnt = 1, p = commit->parents; cnt != mainline && p; cnt++) p = p->next; if (cnt != mainline || !p) die("Commit %s does not have parent %d", sha1_to_hex(commit->object.sha1), mainline); parent = p->item; } else if (0 < mainline) die("Mainline was specified but commit %s is not a merge.", sha1_to_hex(commit->object.sha1)); else parent = commit->parents->item; if (allow_ff && !hashcmp(parent->object.sha1, head)) return fast_forward_to(commit->object.sha1, head); if (parent && parse_commit(parent) < 0) die("%s: cannot parse parent commit %s", me, sha1_to_hex(parent->object.sha1)); if (get_message(commit->buffer, &msg) != 0) die("Cannot get commit message for %s", sha1_to_hex(commit->object.sha1)); /* * "commit" is an existing commit. We would want to apply * the difference it introduces since its first parent "prev" * on top of the current HEAD if we are cherry-pick. Or the * reverse of it if we are revert. */ defmsg = git_pathdup("MERGE_MSG"); if (action == REVERT) { base = commit; base_label = msg.label; next = parent; next_label = msg.parent_label; strbuf_addstr(&msgbuf, "Revert \""); strbuf_addstr(&msgbuf, msg.subject); strbuf_addstr(&msgbuf, "\"\n\nThis reverts commit "); strbuf_addstr(&msgbuf, sha1_to_hex(commit->object.sha1)); if (commit->parents->next) { strbuf_addstr(&msgbuf, ", reversing\nchanges made to "); strbuf_addstr(&msgbuf, sha1_to_hex(parent->object.sha1)); } strbuf_addstr(&msgbuf, ".\n"); } else { base = parent; base_label = msg.parent_label; next = commit; next_label = msg.label; set_author_ident_env(msg.message); add_message_to_msg(&msgbuf, msg.message); if (no_replay) { strbuf_addstr(&msgbuf, "(cherry picked from commit "); strbuf_addstr(&msgbuf, sha1_to_hex(commit->object.sha1)); strbuf_addstr(&msgbuf, ")\n"); } } if (!strategy || !strcmp(strategy, "recursive") || action == REVERT) do_recursive_merge(base, next, base_label, next_label, head, &msgbuf, defmsg); else { int res; struct commit_list *common = NULL; struct commit_list *remotes = NULL; write_message(&msgbuf, defmsg); commit_list_insert(base, &common); commit_list_insert(next, &remotes); res = try_merge_command(strategy, common, sha1_to_hex(head), remotes); free_commit_list(common); free_commit_list(remotes); if (res) { fprintf(stderr, "Automatic %s with strategy %s failed.%s\n", me, strategy, help_msg()); rerere(allow_rerere_auto); exit(1); } } free_message(&msg); /* * * If we are cherry-pick, and if the merge did not result in * hand-editing, we will hit this commit and inherit the original * author date and name. * If we are revert, or if our cherry-pick results in a hand merge, * we had better say that the current user is responsible for that. */ if (!no_commit) { /* 6 is max possible length of our args array including NULL */ const char *args[6]; int res; int i = 0; args[i++] = "commit"; args[i++] = "-n"; if (signoff) args[i++] = "-s"; if (!edit) { args[i++] = "-F"; args[i++] = defmsg; } args[i] = NULL; res = run_command_v_opt(args, RUN_GIT_CMD); free(defmsg); return res; } free(defmsg); return 0; }
int cmd_commit(int argc, const char **argv, const char *prefix) { int header_len; struct strbuf sb; const char *index_file, *reflog_msg; char *nl, *p; unsigned char commit_sha1[20]; struct ref_lock *ref_lock; git_config(git_commit_config); argc = parse_and_validate_options(argc, argv, builtin_commit_usage); index_file = prepare_index(argc, argv, prefix); /* Set up everything for writing the commit object. This includes running hooks, writing the trees, and interacting with the user. */ if (!prepare_to_commit(index_file, prefix)) { rollback_index_files(); return 1; } /* * The commit object */ strbuf_init(&sb, 0); strbuf_addf(&sb, "tree %s\n", sha1_to_hex(active_cache_tree->sha1)); /* Determine parents */ if (initial_commit) { reflog_msg = "commit (initial)"; } else if (amend) { struct commit_list *c; struct commit *commit; reflog_msg = "commit (amend)"; commit = lookup_commit(head_sha1); if (!commit || parse_commit(commit)) die("could not parse HEAD commit"); for (c = commit->parents; c; c = c->next) add_parent(&sb, c->item->object.sha1); } else if (in_merge) { struct strbuf m; FILE *fp; reflog_msg = "commit (merge)"; add_parent(&sb, head_sha1); strbuf_init(&m, 0); fp = fopen(git_path("MERGE_HEAD"), "r"); if (fp == NULL) die("could not open %s for reading: %s", git_path("MERGE_HEAD"), strerror(errno)); while (strbuf_getline(&m, fp, '\n') != EOF) { unsigned char sha1[20]; if (get_sha1_hex(m.buf, sha1) < 0) die("Corrupt MERGE_HEAD file (%s)", m.buf); add_parent(&sb, sha1); } fclose(fp); strbuf_release(&m); } else { reflog_msg = "commit"; strbuf_addf(&sb, "parent %s\n", sha1_to_hex(head_sha1)); } strbuf_addf(&sb, "author %s\n", fmt_ident(author_name, author_email, author_date, IDENT_ERROR_ON_NO_NAME)); strbuf_addf(&sb, "committer %s\n", git_committer_info(IDENT_ERROR_ON_NO_NAME)); if (!is_encoding_utf8(git_commit_encoding)) strbuf_addf(&sb, "encoding %s\n", git_commit_encoding); strbuf_addch(&sb, '\n'); /* Finally, get the commit message */ header_len = sb.len; if (strbuf_read_file(&sb, git_path(commit_editmsg), 0) < 0) { rollback_index_files(); die("could not read commit message"); } /* Truncate the message just before the diff, if any. */ p = strstr(sb.buf, "\ndiff --git a/"); if (p != NULL) strbuf_setlen(&sb, p - sb.buf + 1); if (cleanup_mode != CLEANUP_NONE) stripspace(&sb, cleanup_mode == CLEANUP_ALL); if (sb.len < header_len || message_is_empty(&sb, header_len)) { rollback_index_files(); die("no commit message? aborting commit."); } strbuf_addch(&sb, '\0'); if (is_encoding_utf8(git_commit_encoding) && !is_utf8(sb.buf)) fprintf(stderr, commit_utf8_warn); if (write_sha1_file(sb.buf, sb.len - 1, commit_type, commit_sha1)) { rollback_index_files(); die("failed to write commit object"); } ref_lock = lock_any_ref_for_update("HEAD", initial_commit ? NULL : head_sha1, 0); nl = strchr(sb.buf + header_len, '\n'); if (nl) strbuf_setlen(&sb, nl + 1 - sb.buf); else strbuf_addch(&sb, '\n'); strbuf_remove(&sb, 0, header_len); strbuf_insert(&sb, 0, reflog_msg, strlen(reflog_msg)); strbuf_insert(&sb, strlen(reflog_msg), ": ", 2); if (!ref_lock) { rollback_index_files(); die("cannot lock HEAD ref"); } if (write_ref_sha1(ref_lock, commit_sha1, sb.buf) < 0) { rollback_index_files(); die("cannot update HEAD ref"); } unlink(git_path("MERGE_HEAD")); unlink(git_path("MERGE_MSG")); unlink(git_path("SQUASH_MSG")); if (commit_index_files()) die ("Repository has been updated, but unable to write\n" "new_index file. Check that disk is not full or quota is\n" "not exceeded, and then \"git reset HEAD\" to recover."); rerere(); run_hook(get_index_file(), "post-commit", NULL); if (!quiet) print_summary(prefix, commit_sha1); return 0; }
int cmd_commit(int argc, const char **argv, const char *prefix) { struct strbuf sb = STRBUF_INIT; const char *index_file, *reflog_msg; char *nl, *p; unsigned char commit_sha1[20]; struct ref_lock *ref_lock; struct commit_list *parents = NULL, **pptr = &parents; struct stat statbuf; int allow_fast_forward = 1; struct wt_status s; wt_status_prepare(&s); git_config(git_commit_config, &s); in_merge = file_exists(git_path("MERGE_HEAD")); s.in_merge = in_merge; if (s.use_color == -1) s.use_color = git_use_color_default; argc = parse_and_validate_options(argc, argv, builtin_commit_usage, prefix, &s); if (dry_run) { if (diff_use_color_default == -1) diff_use_color_default = git_use_color_default; return dry_run_commit(argc, argv, prefix, &s); } index_file = prepare_index(argc, argv, prefix, 0); /* Set up everything for writing the commit object. This includes running hooks, writing the trees, and interacting with the user. */ if (!prepare_to_commit(index_file, prefix, &s)) { rollback_index_files(); return 1; } /* Determine parents */ reflog_msg = getenv("GIT_REFLOG_ACTION"); if (initial_commit) { if (!reflog_msg) reflog_msg = "commit (initial)"; } else if (amend) { struct commit_list *c; struct commit *commit; if (!reflog_msg) reflog_msg = "commit (amend)"; commit = lookup_commit(head_sha1); if (!commit || parse_commit(commit)) die("could not parse HEAD commit"); for (c = commit->parents; c; c = c->next) pptr = &commit_list_insert(c->item, pptr)->next; } else if (in_merge) { struct strbuf m = STRBUF_INIT; FILE *fp; if (!reflog_msg) reflog_msg = "commit (merge)"; pptr = &commit_list_insert(lookup_commit(head_sha1), pptr)->next; fp = fopen(git_path("MERGE_HEAD"), "r"); if (fp == NULL) die_errno("could not open '%s' for reading", git_path("MERGE_HEAD")); while (strbuf_getline(&m, fp, '\n') != EOF) { unsigned char sha1[20]; if (get_sha1_hex(m.buf, sha1) < 0) die("Corrupt MERGE_HEAD file (%s)", m.buf); pptr = &commit_list_insert(lookup_commit(sha1), pptr)->next; } fclose(fp); strbuf_release(&m); if (!stat(git_path("MERGE_MODE"), &statbuf)) { if (strbuf_read_file(&sb, git_path("MERGE_MODE"), 0) < 0) die_errno("could not read MERGE_MODE"); if (!strcmp(sb.buf, "no-ff")) allow_fast_forward = 0; } if (allow_fast_forward) parents = reduce_heads(parents); } else { if (!reflog_msg) reflog_msg = "commit"; pptr = &commit_list_insert(lookup_commit(head_sha1), pptr)->next; } /* Finally, get the commit message */ strbuf_reset(&sb); if (strbuf_read_file(&sb, git_path(commit_editmsg), 0) < 0) { int saved_errno = errno; rollback_index_files(); die("could not read commit message: %s", strerror(saved_errno)); } /* Truncate the message just before the diff, if any. */ if (verbose) { p = strstr(sb.buf, "\ndiff --git "); if (p != NULL) strbuf_setlen(&sb, p - sb.buf + 1); } if (cleanup_mode != CLEANUP_NONE) stripspace(&sb, cleanup_mode == CLEANUP_ALL); if (message_is_empty(&sb) && !allow_empty_message) { rollback_index_files(); fprintf(stderr, "Aborting commit due to empty commit message.\n"); exit(1); } if (commit_tree(sb.buf, active_cache_tree->sha1, parents, commit_sha1, fmt_ident(author_name, author_email, author_date, IDENT_ERROR_ON_NO_NAME))) { rollback_index_files(); die("failed to write commit object"); } ref_lock = lock_any_ref_for_update("HEAD", initial_commit ? NULL : head_sha1, 0); nl = strchr(sb.buf, '\n'); if (nl) strbuf_setlen(&sb, nl + 1 - sb.buf); else strbuf_addch(&sb, '\n'); strbuf_insert(&sb, 0, reflog_msg, strlen(reflog_msg)); strbuf_insert(&sb, strlen(reflog_msg), ": ", 2); if (!ref_lock) { rollback_index_files(); die("cannot lock HEAD ref"); } if (write_ref_sha1(ref_lock, commit_sha1, sb.buf) < 0) { rollback_index_files(); die("cannot update HEAD ref"); } unlink(git_path("MERGE_HEAD")); unlink(git_path("MERGE_MSG")); unlink(git_path("MERGE_MODE")); unlink(git_path("SQUASH_MSG")); if (commit_index_files()) die ("Repository has been updated, but unable to write\n" "new_index file. Check that disk is not full or quota is\n" "not exceeded, and then \"git reset HEAD\" to recover."); rerere(0); run_hook(get_index_file(), "post-commit", NULL); if (amend && !no_post_rewrite) { struct notes_rewrite_cfg *cfg; cfg = init_copy_notes_for_rewrite("amend"); if (cfg) { copy_note_for_rewrite(cfg, head_sha1, commit_sha1); finish_copy_notes_for_rewrite(cfg); } run_rewrite_hook(head_sha1, commit_sha1); } if (!quiet) print_summary(prefix, commit_sha1); return 0; }
static int do_pick_commit(void) { unsigned char head[20]; struct commit *base, *next, *parent; const char *base_label, *next_label; struct commit_message msg = { NULL, NULL, NULL, NULL, NULL }; char *defmsg = NULL; struct strbuf msgbuf = STRBUF_INIT; int res; if (no_commit) { /* * We do not intend to commit immediately. We just want to * merge the differences in, so let's compute the tree * that represents the "current" state for merge-recursive * to work on. */ if (write_cache_as_tree(head, 0, NULL)) die ("Your index file is unmerged."); } else { if (get_sha1("HEAD", head)) die ("You do not have a valid HEAD"); if (index_differs_from("HEAD", 0)) die_dirty_index(me); } discard_cache(); if (!commit->parents) { if (action == REVERT) die ("Cannot revert a root commit"); parent = NULL; } else if (commit->parents->next) { /* Reverting or cherry-picking a merge commit */ int cnt; struct commit_list *p; if (!mainline) die("Commit %s is a merge but no -m option was given.", sha1_to_hex(commit->object.sha1)); for (cnt = 1, p = commit->parents; cnt != mainline && p; cnt++) p = p->next; if (cnt != mainline || !p) die("Commit %s does not have parent %d", sha1_to_hex(commit->object.sha1), mainline); parent = p->item; } else if (0 < mainline) die("Mainline was specified but commit %s is not a merge.", sha1_to_hex(commit->object.sha1)); else parent = commit->parents->item; if (allow_ff && parent && !hashcmp(parent->object.sha1, head)) return fast_forward_to(commit->object.sha1, head); if (parent && parse_commit(parent) < 0) die("%s: cannot parse parent commit %s", me, sha1_to_hex(parent->object.sha1)); if (get_message(commit->buffer, &msg) != 0) die("Cannot get commit message for %s", sha1_to_hex(commit->object.sha1)); /* * "commit" is an existing commit. We would want to apply * the difference it introduces since its first parent "prev" * on top of the current HEAD if we are cherry-pick. Or the * reverse of it if we are revert. */ defmsg = git_pathdup("MERGE_MSG"); if (action == REVERT) { base = commit; base_label = msg.label; next = parent; next_label = msg.parent_label; strbuf_addstr(&msgbuf, "Revert \""); strbuf_addstr(&msgbuf, msg.subject); strbuf_addstr(&msgbuf, "\"\n\nThis reverts commit "); strbuf_addstr(&msgbuf, sha1_to_hex(commit->object.sha1)); if (commit->parents->next) { strbuf_addstr(&msgbuf, ", reversing\nchanges made to "); strbuf_addstr(&msgbuf, sha1_to_hex(parent->object.sha1)); } strbuf_addstr(&msgbuf, ".\n"); } else { base = parent; base_label = msg.parent_label; next = commit; next_label = msg.label; add_message_to_msg(&msgbuf, msg.message); if (no_replay) { strbuf_addstr(&msgbuf, "(cherry picked from commit "); strbuf_addstr(&msgbuf, sha1_to_hex(commit->object.sha1)); strbuf_addstr(&msgbuf, ")\n"); } if (!no_commit) write_cherry_pick_head(); } if (!strategy || !strcmp(strategy, "recursive") || action == REVERT) { res = do_recursive_merge(base, next, base_label, next_label, head, &msgbuf); write_message(&msgbuf, defmsg); } else { struct commit_list *common = NULL; struct commit_list *remotes = NULL; write_message(&msgbuf, defmsg); commit_list_insert(base, &common); commit_list_insert(next, &remotes); res = try_merge_command(strategy, xopts_nr, xopts, common, sha1_to_hex(head), remotes); free_commit_list(common); free_commit_list(remotes); } if (res) { error("could not %s %s... %s", action == REVERT ? "revert" : "apply", find_unique_abbrev(commit->object.sha1, DEFAULT_ABBREV), msg.subject); print_advice(); rerere(allow_rerere_auto); } else { if (!no_commit) res = run_git_commit(defmsg); } free_message(&msg); free(defmsg); return res; }