tree_entry_interesting(): fix depth limit with overlapping pathspecs
[gitweb.git] / notes-merge.c
index ada29067bd784f354e67e7b9ea7f94a0f678bb52..71c4d45fcd1ac49630bdb8bc7b0aec658011c21b 100644 (file)
@@ -8,6 +8,7 @@
 #include "dir.h"
 #include "notes.h"
 #include "notes-merge.h"
+#include "strbuf.h"
 
 struct notes_merge_pair {
        unsigned char obj[20], base[20], local[20], remote[20];
@@ -16,6 +17,7 @@ struct notes_merge_pair {
 void init_notes_merge_options(struct notes_merge_options *o)
 {
        memset(o, 0, sizeof(struct notes_merge_options));
+       strbuf_init(&(o->commit_msg), 0);
        o->verbosity = NOTES_MERGE_VERBOSITY_DEFAULT;
 }
 
@@ -278,7 +280,7 @@ static void check_notes_merge_worktree(struct notes_merge_options *o)
                                die("You have not concluded your previous "
                                    "notes merge (%s exists).\nPlease, use "
                                    "'git notes merge --commit' or 'git notes "
-                                   "merge --reset' to commit/abort the "
+                                   "merge --abort' to commit/abort the "
                                    "previous merge before you start a new "
                                    "notes merge.", git_path("NOTES_MERGE_*"));
                        else
@@ -384,6 +386,12 @@ static int merge_one_change_manual(struct notes_merge_options *o,
               sha1_to_hex(p->obj), sha1_to_hex(p->base),
               sha1_to_hex(p->local), sha1_to_hex(p->remote));
 
+       /* add "Conflicts:" section to commit message first time through */
+       if (!o->has_worktree)
+               strbuf_addstr(&(o->commit_msg), "\n\nConflicts:\n");
+
+       strbuf_addf(&(o->commit_msg), "\t%s\n", sha1_to_hex(p->obj));
+
        OUTPUT(o, 2, "Auto-merging notes for %s", sha1_to_hex(p->obj));
        check_notes_merge_worktree(o);
        if (is_null_sha1(p->local)) {
@@ -445,6 +453,13 @@ static int merge_one_change(struct notes_merge_options *o,
                        die("failed to concatenate notes "
                            "(combine_notes_concatenate)");
                return 0;
+       case NOTES_MERGE_RESOLVE_CAT_SORT_UNIQ:
+               OUTPUT(o, 2, "Concatenating unique lines in local and remote "
+                      "notes for %s", sha1_to_hex(p->obj));
+               if (add_note(t, p->obj, p->remote, combine_notes_cat_sort_uniq))
+                       die("failed to concatenate notes "
+                           "(combine_notes_cat_sort_uniq)");
+               return 0;
        }
        die("Unknown strategy (%i).", o->strategy);
 }
@@ -640,13 +655,83 @@ int notes_merge(struct notes_merge_options *o,
                struct commit_list *parents = NULL;
                commit_list_insert(remote, &parents); /* LIFO order */
                commit_list_insert(local, &parents);
-               create_notes_commit(local_tree, parents, o->commit_msg,
+               create_notes_commit(local_tree, parents, o->commit_msg.buf,
                                    result_sha1);
        }
 
 found_result:
        free_commit_list(bases);
+       strbuf_release(&(o->commit_msg));
        trace_printf("notes_merge(): result = %i, result_sha1 = %.7s\n",
               result, sha1_to_hex(result_sha1));
        return result;
 }
+
+int notes_merge_commit(struct notes_merge_options *o,
+                      struct notes_tree *partial_tree,
+                      struct commit *partial_commit,
+                      unsigned char *result_sha1)
+{
+       /*
+        * Iterate through files in .git/NOTES_MERGE_WORKTREE and add all
+        * found notes to 'partial_tree'. Write the updates notes tree to
+        * the DB, and commit the resulting tree object while reusing the
+        * commit message and parents from 'partial_commit'.
+        * Finally store the new commit object SHA1 into 'result_sha1'.
+        */
+       struct dir_struct dir;
+       const char *path = git_path(NOTES_MERGE_WORKTREE "/");
+       int path_len = strlen(path), i;
+       const char *msg = strstr(partial_commit->buffer, "\n\n");
+
+       OUTPUT(o, 3, "Committing notes in notes merge worktree at %.*s",
+              path_len - 1, path);
+
+       if (!msg || msg[2] == '\0')
+               die("partial notes commit has empty message");
+       msg += 2;
+
+       memset(&dir, 0, sizeof(dir));
+       read_directory(&dir, path, path_len, NULL);
+       for (i = 0; i < dir.nr; i++) {
+               struct dir_entry *ent = dir.entries[i];
+               struct stat st;
+               const char *relpath = ent->name + path_len;
+               unsigned char obj_sha1[20], blob_sha1[20];
+
+               if (ent->len - path_len != 40 || get_sha1_hex(relpath, obj_sha1)) {
+                       OUTPUT(o, 3, "Skipping non-SHA1 entry '%s'", ent->name);
+                       continue;
+               }
+
+               /* write file as blob, and add to partial_tree */
+               if (stat(ent->name, &st))
+                       die_errno("Failed to stat '%s'", ent->name);
+               if (index_path(blob_sha1, ent->name, &st, 1))
+                       die("Failed to write blob object from '%s'", ent->name);
+               if (add_note(partial_tree, obj_sha1, blob_sha1, NULL))
+                       die("Failed to add resolved note '%s' to notes tree",
+                           ent->name);
+               OUTPUT(o, 4, "Added resolved note for object %s: %s",
+                      sha1_to_hex(obj_sha1), sha1_to_hex(blob_sha1));
+       }
+
+       create_notes_commit(partial_tree, partial_commit->parents, msg,
+                           result_sha1);
+       OUTPUT(o, 4, "Finalized notes merge commit: %s",
+              sha1_to_hex(result_sha1));
+       return 0;
+}
+
+int notes_merge_abort(struct notes_merge_options *o)
+{
+       /* Remove .git/NOTES_MERGE_WORKTREE directory and all files within */
+       struct strbuf buf = STRBUF_INIT;
+       int ret;
+
+       strbuf_addstr(&buf, git_path(NOTES_MERGE_WORKTREE));
+       OUTPUT(o, 3, "Removing notes merge worktree at %s", buf.buf);
+       ret = remove_dir_recursively(&buf, 0);
+       strbuf_release(&buf);
+       return ret;
+}