git-blame: refactor code to emit "porcelain format" output
[gitweb.git] / merge-tree.c
index fd0c2111c48dc13b9d0d713ec7aa4a181daedb6a..2d1413efbbc33c51fd4820933dcb54164e12d706 100644 (file)
@@ -1,5 +1,6 @@
 #include "cache.h"
 #include "tree-walk.h"
+#include "xdiff-interface.h"
 #include "blob.h"
 
 static const char merge_tree_usage[] = "git-merge-tree <base-tree> <branch1> <branch2>";
@@ -52,6 +53,77 @@ static const char *explanation(struct merge_list *entry)
        return "removed in remote";
 }
 
+extern void *merge_file(struct blob *, struct blob *, struct blob *, unsigned long *);
+
+static void *result(struct merge_list *entry, unsigned long *size)
+{
+       enum object_type type;
+       struct blob *base, *our, *their;
+
+       if (!entry->stage)
+               return read_sha1_file(entry->blob->object.sha1, &type, size);
+       base = NULL;
+       if (entry->stage == 1) {
+               base = entry->blob;
+               entry = entry->link;
+       }
+       our = NULL;
+       if (entry && entry->stage == 2) {
+               our = entry->blob;
+               entry = entry->link;
+       }
+       their = NULL;
+       if (entry)
+               their = entry->blob;
+       return merge_file(base, our, their, size);
+}
+
+static void *origin(struct merge_list *entry, unsigned long *size)
+{
+       enum object_type type;
+       while (entry) {
+               if (entry->stage == 2)
+                       return read_sha1_file(entry->blob->object.sha1, &type, size);
+               entry = entry->link;
+       }
+       return NULL;
+}
+
+static int show_outf(void *priv_, mmbuffer_t *mb, int nbuf)
+{
+       int i;
+       for (i = 0; i < nbuf; i++)
+               printf("%.*s", (int) mb[i].size, mb[i].ptr);
+       return 0;
+}
+
+static void show_diff(struct merge_list *entry)
+{
+       unsigned long size;
+       mmfile_t src, dst;
+       xpparam_t xpp;
+       xdemitconf_t xecfg;
+       xdemitcb_t ecb;
+
+       xpp.flags = XDF_NEED_MINIMAL;
+       memset(&xecfg, 0, sizeof(xecfg));
+       xecfg.ctxlen = 3;
+       ecb.outf = show_outf;
+       ecb.priv = NULL;
+
+       src.ptr = origin(entry, &size);
+       if (!src.ptr)
+               size = 0;
+       src.size = size;
+       dst.ptr = result(entry, &size);
+       if (!dst.ptr)
+               size = 0;
+       dst.size = size;
+       xdi_diff(&src, &dst, &xpp, &xecfg, &ecb);
+       free(src.ptr);
+       free(dst.ptr);
+}
+
 static void show_result_list(struct merge_list *entry)
 {
        printf("%s\n", explanation(entry));
@@ -70,6 +142,7 @@ static void show_result(void)
        walk = merge_result;
        while (walk) {
                show_result_list(walk);
+               show_diff(walk);
                walk = walk->next;
        }
 }
@@ -79,15 +152,14 @@ static int same_entry(struct name_entry *a, struct name_entry *b)
 {
        return  a->sha1 &&
                b->sha1 &&
-               !memcmp(a->sha1, b->sha1, 20) &&
+               !hashcmp(a->sha1, b->sha1) &&
                a->mode == b->mode;
 }
 
 static struct merge_list *create_entry(unsigned stage, unsigned mode, const unsigned char *sha1, const char *path)
 {
-       struct merge_list *res = xmalloc(sizeof(*res));
+       struct merge_list *res = xcalloc(1, sizeof(*res));
 
-       memset(res, 0, sizeof(*res));
        res->stage = stage;
        res->path = path;
        res->mode = mode;
@@ -95,7 +167,13 @@ static struct merge_list *create_entry(unsigned stage, unsigned mode, const unsi
        return res;
 }
 
-static void resolve(const char *base, struct name_entry *branch1, struct name_entry *result)
+static char *traverse_path(const struct traverse_info *info, const struct name_entry *n)
+{
+       char *path = xmalloc(traverse_path_len(info, n) + 1);
+       return make_traverse_path(path, info, n);
+}
+
+static void resolve(const struct traverse_info *info, struct name_entry *branch1, struct name_entry *result)
 {
        struct merge_list *orig, *final;
        const char *path;
@@ -104,7 +182,7 @@ static void resolve(const char *base, struct name_entry *branch1, struct name_en
        if (!branch1)
                return;
 
-       path = strdup(mkpath("%s%s", base, result->path));
+       path = traverse_path(info, result);
        orig = create_entry(2, branch1->mode, branch1->sha1, path);
        final = create_entry(0, result->mode, result->sha1, path);
 
@@ -113,9 +191,8 @@ static void resolve(const char *base, struct name_entry *branch1, struct name_en
        add_merge_entry(final);
 }
 
-static int unresolved_directory(const char *base, struct name_entry n[3])
+static int unresolved_directory(const struct traverse_info *info, struct name_entry n[3])
 {
-       int baselen;
        char *newbase;
        struct name_entry *p;
        struct tree_desc t[3];
@@ -131,12 +208,7 @@ static int unresolved_directory(const char *base, struct name_entry n[3])
        }
        if (!S_ISDIR(p->mode))
                return 0;
-       baselen = strlen(base);
-       newbase = xmalloc(baselen + p->pathlen + 2);
-       memcpy(newbase, base, baselen);
-       memcpy(newbase + baselen, p->path, p->pathlen);
-       memcpy(newbase + baselen + p->pathlen, "/", 2);
-
+       newbase = traverse_path(info, p);
        buf0 = fill_tree_descriptor(t+0, n[0].sha1);
        buf1 = fill_tree_descriptor(t+1, n[1].sha1);
        buf2 = fill_tree_descriptor(t+2, n[2].sha1);
@@ -150,7 +222,7 @@ static int unresolved_directory(const char *base, struct name_entry n[3])
 }
 
 
-static struct merge_list *link_entry(unsigned stage, const char *base, struct name_entry *n, struct merge_list *entry)
+static struct merge_list *link_entry(unsigned stage, const struct traverse_info *info, struct name_entry *n, struct merge_list *entry)
 {
        const char *path;
        struct merge_list *link;
@@ -160,17 +232,17 @@ static struct merge_list *link_entry(unsigned stage, const char *base, struct na
        if (entry)
                path = entry->path;
        else
-               path = strdup(mkpath("%s%s", base, n->path));
+               path = traverse_path(info, n);
        link = create_entry(stage, n->mode, n->sha1, path);
        link->link = entry;
        return link;
 }
 
-static void unresolved(const char *base, struct name_entry n[3])
+static void unresolved(const struct traverse_info *info, struct name_entry n[3])
 {
        struct merge_list *entry = NULL;
 
-       if (unresolved_directory(base, n))
+       if (unresolved_directory(info, n))
                return;
 
        /*
@@ -178,9 +250,9 @@ static void unresolved(const char *base, struct name_entry n[3])
         * list has the stages in order - link_entry adds new
         * links at the front.
         */
-       entry = link_entry(3, base, n + 2, entry);
-       entry = link_entry(2, base, n + 1, entry);
-       entry = link_entry(1, base, n + 0, entry);
+       entry = link_entry(3, info, n + 2, entry);
+       entry = link_entry(2, info, n + 1, entry);
+       entry = link_entry(1, info, n + 0, entry);
 
        add_merge_entry(entry);
 }
@@ -214,36 +286,41 @@ static void unresolved(const char *base, struct name_entry n[3])
  * The successful merge rules are the same as for the three-way merge
  * in git-read-tree.
  */
-static void threeway_callback(int n, unsigned long mask, struct name_entry *entry, const char *base)
+static int threeway_callback(int n, unsigned long mask, unsigned long dirmask, struct name_entry *entry, struct traverse_info *info)
 {
        /* Same in both? */
        if (same_entry(entry+1, entry+2)) {
                if (entry[0].sha1) {
-                       resolve(base, NULL, entry+1);
-                       return;
+                       resolve(info, NULL, entry+1);
+                       return mask;
                }
        }
 
        if (same_entry(entry+0, entry+1)) {
                if (entry[2].sha1 && !S_ISDIR(entry[2].mode)) {
-                       resolve(base, entry+1, entry+2);
-                       return;
+                       resolve(info, entry+1, entry+2);
+                       return mask;
                }
        }
 
        if (same_entry(entry+0, entry+2)) {
                if (entry[1].sha1 && !S_ISDIR(entry[1].mode)) {
-                       resolve(base, NULL, entry+1);
-                       return;
+                       resolve(info, NULL, entry+1);
+                       return mask;
                }
        }
 
-       unresolved(base, entry);
+       unresolved(info, entry);
+       return mask;
 }
 
 static void merge_trees(struct tree_desc t[3], const char *base)
 {
-       traverse_trees(3, t, base, threeway_callback);
+       struct traverse_info info;
+
+       setup_traverse_info(&info, base);
+       info.fn = threeway_callback;
+       traverse_trees(3, t, &info);
 }
 
 static void *get_tree_descriptor(struct tree_desc *desc, const char *rev)
@@ -264,9 +341,11 @@ int main(int argc, char **argv)
        struct tree_desc t[3];
        void *buf1, *buf2, *buf3;
 
-       if (argc < 4)
+       if (argc != 4)
                usage(merge_tree_usage);
 
+       setup_git_directory();
+
        buf1 = get_tree_descriptor(t+0, argv[1]);
        buf2 = get_tree_descriptor(t+1, argv[2]);
        buf3 = get_tree_descriptor(t+2, argv[3]);