diff options
Diffstat (limited to 'builtin')
-rw-r--r-- | builtin/notes.c | 106 |
1 files changed, 103 insertions, 3 deletions
diff --git a/builtin/notes.c b/builtin/notes.c index 309edc741e..b5385238ea 100644 --- a/builtin/notes.c +++ b/builtin/notes.c @@ -27,6 +27,8 @@ static const char * const git_notes_usage[] = { "git notes [--ref <notes_ref>] edit [<object>]", "git notes [--ref <notes_ref>] show [<object>]", "git notes [--ref <notes_ref>] merge [-v | -q] [-s <strategy> ] <notes_ref>", + "git notes merge --commit [-v | -q]", + "git notes merge --abort [-v | -q]", "git notes [--ref <notes_ref>] remove [<object>]", "git notes [--ref <notes_ref>] prune [-n | -v]", NULL @@ -65,6 +67,8 @@ static const char * const git_notes_show_usage[] = { static const char * const git_notes_merge_usage[] = { "git notes merge [<options>] <notes_ref>", + "git notes merge --commit [<options>]", + "git notes merge --abort [<options>]", NULL }; @@ -761,33 +765,119 @@ static int show(int argc, const char **argv, const char *prefix) return retval; } +static int merge_abort(struct notes_merge_options *o) +{ + int ret = 0; + + /* + * Remove .git/NOTES_MERGE_PARTIAL and .git/NOTES_MERGE_REF, and call + * notes_merge_abort() to remove .git/NOTES_MERGE_WORKTREE. + */ + + if (delete_ref("NOTES_MERGE_PARTIAL", NULL, 0)) + ret += error("Failed to delete ref NOTES_MERGE_PARTIAL"); + if (delete_ref("NOTES_MERGE_REF", NULL, REF_NODEREF)) + ret += error("Failed to delete ref NOTES_MERGE_REF"); + if (notes_merge_abort(o)) + ret += error("Failed to remove 'git notes merge' worktree"); + return ret; +} + +static int merge_commit(struct notes_merge_options *o) +{ + struct strbuf msg = STRBUF_INIT; + unsigned char sha1[20]; + struct notes_tree *t; + struct commit *partial; + struct pretty_print_context pretty_ctx; + + /* + * Read partial merge result from .git/NOTES_MERGE_PARTIAL, + * and target notes ref from .git/NOTES_MERGE_REF. + */ + + if (get_sha1("NOTES_MERGE_PARTIAL", sha1)) + die("Failed to read ref NOTES_MERGE_PARTIAL"); + else if (!(partial = lookup_commit_reference(sha1))) + die("Could not find commit from NOTES_MERGE_PARTIAL."); + else if (parse_commit(partial)) + die("Could not parse commit from NOTES_MERGE_PARTIAL."); + + t = xcalloc(1, sizeof(struct notes_tree)); + init_notes(t, "NOTES_MERGE_PARTIAL", combine_notes_overwrite, 0); + + o->local_ref = resolve_ref("NOTES_MERGE_REF", sha1, 0, 0); + if (!o->local_ref) + die("Failed to resolve NOTES_MERGE_REF"); + + if (notes_merge_commit(o, t, partial, sha1)) + die("Failed to finalize notes merge"); + + /* Reuse existing commit message in reflog message */ + memset(&pretty_ctx, 0, sizeof(pretty_ctx)); + format_commit_message(partial, "%s", &msg, &pretty_ctx); + strbuf_trim(&msg); + strbuf_insert(&msg, 0, "notes: ", 7); + update_ref(msg.buf, o->local_ref, sha1, NULL, 0, DIE_ON_ERR); + + free_notes(t); + strbuf_release(&msg); + return merge_abort(o); +} + static int merge(int argc, const char **argv, const char *prefix) { struct strbuf remote_ref = STRBUF_INIT, msg = STRBUF_INIT; unsigned char result_sha1[20]; struct notes_tree *t; struct notes_merge_options o; + int do_merge = 0, do_commit = 0, do_abort = 0; int verbosity = 0, result; const char *strategy = NULL; struct option options[] = { + OPT_GROUP("General options"), OPT__VERBOSITY(&verbosity), + OPT_GROUP("Merge options"), OPT_STRING('s', "strategy", &strategy, "strategy", "resolve notes conflicts using the given " "strategy (manual/ours/theirs/union)"), + OPT_GROUP("Committing unmerged notes"), + { OPTION_BOOLEAN, 0, "commit", &do_commit, NULL, + "finalize notes merge by committing unmerged notes", + PARSE_OPT_NOARG | PARSE_OPT_NONEG }, + OPT_GROUP("Aborting notes merge resolution"), + { OPTION_BOOLEAN, 0, "abort", &do_abort, NULL, + "abort notes merge", + PARSE_OPT_NOARG | PARSE_OPT_NONEG }, OPT_END() }; argc = parse_options(argc, argv, prefix, options, git_notes_merge_usage, 0); - if (argc != 1) { + if (strategy || do_commit + do_abort == 0) + do_merge = 1; + if (do_merge + do_commit + do_abort != 1) { + error("cannot mix --commit, --abort or -s/--strategy"); + usage_with_options(git_notes_merge_usage, options); + } + + if (do_merge && argc != 1) { error("Must specify a notes ref to merge"); usage_with_options(git_notes_merge_usage, options); + } else if (!do_merge && argc) { + error("too many parameters"); + usage_with_options(git_notes_merge_usage, options); } init_notes_merge_options(&o); o.verbosity = verbosity + NOTES_MERGE_VERBOSITY_DEFAULT; + if (do_abort) + return merge_abort(&o); + if (do_commit) + return merge_commit(&o); + o.local_ref = default_notes_ref(); strbuf_addstr(&remote_ref, argv[0]); expand_notes_ref(&remote_ref); @@ -820,9 +910,19 @@ static int merge(int argc, const char **argv, const char *prefix) /* Update default notes ref with new commit */ update_ref(msg.buf, default_notes_ref(), result_sha1, NULL, 0, DIE_ON_ERR); - else /* Merge has unresolved conflicts */ - printf("Automatic notes merge failed. Fix conflicts in %s.\n", + else { /* Merge has unresolved conflicts */ + /* Update .git/NOTES_MERGE_PARTIAL with partial merge result */ + update_ref(msg.buf, "NOTES_MERGE_PARTIAL", result_sha1, NULL, + 0, DIE_ON_ERR); + /* Store ref-to-be-updated into .git/NOTES_MERGE_REF */ + if (create_symref("NOTES_MERGE_REF", default_notes_ref(), NULL)) + die("Failed to store link to current notes ref (%s)", + default_notes_ref()); + printf("Automatic notes merge failed. Fix conflicts in %s and " + "commit the result with 'git notes merge --commit', or " + "abort the merge with 'git notes merge --abort'.\n", git_path(NOTES_MERGE_WORKTREE)); + } free_notes(t); strbuf_release(&remote_ref); |