builtin / merge-tree.con commit Merge branch 'jh/trace2-pretty-output' (93fc876)
   1#define USE_THE_INDEX_COMPATIBILITY_MACROS
   2#include "builtin.h"
   3#include "tree-walk.h"
   4#include "xdiff-interface.h"
   5#include "object-store.h"
   6#include "repository.h"
   7#include "blob.h"
   8#include "exec-cmd.h"
   9#include "merge-blobs.h"
  10
  11static const char merge_tree_usage[] = "git merge-tree <base-tree> <branch1> <branch2>";
  12
  13struct merge_list {
  14        struct merge_list *next;
  15        struct merge_list *link;        /* other stages for this object */
  16
  17        unsigned int stage : 2;
  18        unsigned int mode;
  19        const char *path;
  20        struct blob *blob;
  21};
  22
  23static struct merge_list *merge_result, **merge_result_end = &merge_result;
  24
  25static void add_merge_entry(struct merge_list *entry)
  26{
  27        *merge_result_end = entry;
  28        merge_result_end = &entry->next;
  29}
  30
  31static void merge_trees(struct tree_desc t[3], const char *base);
  32
  33static const char *explanation(struct merge_list *entry)
  34{
  35        switch (entry->stage) {
  36        case 0:
  37                return "merged";
  38        case 3:
  39                return "added in remote";
  40        case 2:
  41                if (entry->link)
  42                        return "added in both";
  43                return "added in local";
  44        }
  45
  46        /* Existed in base */
  47        entry = entry->link;
  48        if (!entry)
  49                return "removed in both";
  50
  51        if (entry->link)
  52                return "changed in both";
  53
  54        if (entry->stage == 3)
  55                return "removed in local";
  56        return "removed in remote";
  57}
  58
  59static void *result(struct merge_list *entry, unsigned long *size)
  60{
  61        enum object_type type;
  62        struct blob *base, *our, *their;
  63        const char *path = entry->path;
  64
  65        if (!entry->stage)
  66                return read_object_file(&entry->blob->object.oid, &type, size);
  67        base = NULL;
  68        if (entry->stage == 1) {
  69                base = entry->blob;
  70                entry = entry->link;
  71        }
  72        our = NULL;
  73        if (entry && entry->stage == 2) {
  74                our = entry->blob;
  75                entry = entry->link;
  76        }
  77        their = NULL;
  78        if (entry)
  79                their = entry->blob;
  80        return merge_blobs(the_repository->index, path,
  81                           base, our, their, size);
  82}
  83
  84static void *origin(struct merge_list *entry, unsigned long *size)
  85{
  86        enum object_type type;
  87        while (entry) {
  88                if (entry->stage == 2)
  89                        return read_object_file(&entry->blob->object.oid,
  90                                                &type, size);
  91                entry = entry->link;
  92        }
  93        return NULL;
  94}
  95
  96static int show_outf(void *priv_, mmbuffer_t *mb, int nbuf)
  97{
  98        int i;
  99        for (i = 0; i < nbuf; i++)
 100                printf("%.*s", (int) mb[i].size, mb[i].ptr);
 101        return 0;
 102}
 103
 104static void show_diff(struct merge_list *entry)
 105{
 106        unsigned long size;
 107        mmfile_t src, dst;
 108        xpparam_t xpp;
 109        xdemitconf_t xecfg;
 110        xdemitcb_t ecb;
 111
 112        xpp.flags = 0;
 113        memset(&xecfg, 0, sizeof(xecfg));
 114        xecfg.ctxlen = 3;
 115        ecb.out_hunk = NULL;
 116        ecb.out_line = show_outf;
 117        ecb.priv = NULL;
 118
 119        src.ptr = origin(entry, &size);
 120        if (!src.ptr)
 121                size = 0;
 122        src.size = size;
 123        dst.ptr = result(entry, &size);
 124        if (!dst.ptr)
 125                size = 0;
 126        dst.size = size;
 127        if (xdi_diff(&src, &dst, &xpp, &xecfg, &ecb))
 128                die("unable to generate diff");
 129        free(src.ptr);
 130        free(dst.ptr);
 131}
 132
 133static void show_result_list(struct merge_list *entry)
 134{
 135        printf("%s\n", explanation(entry));
 136        do {
 137                struct merge_list *link = entry->link;
 138                static const char *desc[4] = { "result", "base", "our", "their" };
 139                printf("  %-6s %o %s %s\n", desc[entry->stage], entry->mode, oid_to_hex(&entry->blob->object.oid), entry->path);
 140                entry = link;
 141        } while (entry);
 142}
 143
 144static void show_result(void)
 145{
 146        struct merge_list *walk;
 147
 148        walk = merge_result;
 149        while (walk) {
 150                show_result_list(walk);
 151                show_diff(walk);
 152                walk = walk->next;
 153        }
 154}
 155
 156/* An empty entry never compares same, not even to another empty entry */
 157static int same_entry(struct name_entry *a, struct name_entry *b)
 158{
 159        return  !is_null_oid(&a->oid) &&
 160                !is_null_oid(&b->oid) &&
 161                oideq(&a->oid, &b->oid) &&
 162                a->mode == b->mode;
 163}
 164
 165static int both_empty(struct name_entry *a, struct name_entry *b)
 166{
 167        return is_null_oid(&a->oid) && is_null_oid(&b->oid);
 168}
 169
 170static struct merge_list *create_entry(unsigned stage, unsigned mode, const struct object_id *oid, const char *path)
 171{
 172        struct merge_list *res = xcalloc(1, sizeof(*res));
 173
 174        res->stage = stage;
 175        res->path = path;
 176        res->mode = mode;
 177        res->blob = lookup_blob(the_repository, oid);
 178        return res;
 179}
 180
 181static char *traverse_path(const struct traverse_info *info, const struct name_entry *n)
 182{
 183        struct strbuf buf = STRBUF_INIT;
 184        strbuf_make_traverse_path(&buf, info, n->path, n->pathlen);
 185        return strbuf_detach(&buf, NULL);
 186}
 187
 188static void resolve(const struct traverse_info *info, struct name_entry *ours, struct name_entry *result)
 189{
 190        struct merge_list *orig, *final;
 191        const char *path;
 192
 193        /* If it's already ours, don't bother showing it */
 194        if (!ours)
 195                return;
 196
 197        path = traverse_path(info, result);
 198        orig = create_entry(2, ours->mode, &ours->oid, path);
 199        final = create_entry(0, result->mode, &result->oid, path);
 200
 201        final->link = orig;
 202
 203        add_merge_entry(final);
 204}
 205
 206static void unresolved_directory(const struct traverse_info *info,
 207                                 struct name_entry n[3])
 208{
 209        struct repository *r = the_repository;
 210        char *newbase;
 211        struct name_entry *p;
 212        struct tree_desc t[3];
 213        void *buf0, *buf1, *buf2;
 214
 215        for (p = n; p < n + 3; p++) {
 216                if (p->mode && S_ISDIR(p->mode))
 217                        break;
 218        }
 219        if (n + 3 <= p)
 220                return; /* there is no tree here */
 221
 222        newbase = traverse_path(info, p);
 223
 224#define ENTRY_OID(e) (((e)->mode && S_ISDIR((e)->mode)) ? &(e)->oid : NULL)
 225        buf0 = fill_tree_descriptor(r, t + 0, ENTRY_OID(n + 0));
 226        buf1 = fill_tree_descriptor(r, t + 1, ENTRY_OID(n + 1));
 227        buf2 = fill_tree_descriptor(r, t + 2, ENTRY_OID(n + 2));
 228#undef ENTRY_OID
 229
 230        merge_trees(t, newbase);
 231
 232        free(buf0);
 233        free(buf1);
 234        free(buf2);
 235        free(newbase);
 236}
 237
 238
 239static struct merge_list *link_entry(unsigned stage, const struct traverse_info *info, struct name_entry *n, struct merge_list *entry)
 240{
 241        const char *path;
 242        struct merge_list *link;
 243
 244        if (!n->mode)
 245                return entry;
 246        if (entry)
 247                path = entry->path;
 248        else
 249                path = traverse_path(info, n);
 250        link = create_entry(stage, n->mode, &n->oid, path);
 251        link->link = entry;
 252        return link;
 253}
 254
 255static void unresolved(const struct traverse_info *info, struct name_entry n[3])
 256{
 257        struct merge_list *entry = NULL;
 258        int i;
 259        unsigned dirmask = 0, mask = 0;
 260
 261        for (i = 0; i < 3; i++) {
 262                mask |= (1 << i);
 263                /*
 264                 * Treat missing entries as directories so that we return
 265                 * after unresolved_directory has handled this.
 266                 */
 267                if (!n[i].mode || S_ISDIR(n[i].mode))
 268                        dirmask |= (1 << i);
 269        }
 270
 271        unresolved_directory(info, n);
 272
 273        if (dirmask == mask)
 274                return;
 275
 276        if (n[2].mode && !S_ISDIR(n[2].mode))
 277                entry = link_entry(3, info, n + 2, entry);
 278        if (n[1].mode && !S_ISDIR(n[1].mode))
 279                entry = link_entry(2, info, n + 1, entry);
 280        if (n[0].mode && !S_ISDIR(n[0].mode))
 281                entry = link_entry(1, info, n + 0, entry);
 282
 283        add_merge_entry(entry);
 284}
 285
 286/*
 287 * Merge two trees together (t[1] and t[2]), using a common base (t[0])
 288 * as the origin.
 289 *
 290 * This walks the (sorted) trees in lock-step, checking every possible
 291 * name. Note that directories automatically sort differently from other
 292 * files (see "base_name_compare"), so you'll never see file/directory
 293 * conflicts, because they won't ever compare the same.
 294 *
 295 * IOW, if a directory changes to a filename, it will automatically be
 296 * seen as the directory going away, and the filename being created.
 297 *
 298 * Think of this as a three-way diff.
 299 *
 300 * The output will be either:
 301 *  - successful merge
 302 *       "0 mode sha1 filename"
 303 *    NOTE NOTE NOTE! FIXME! We really really need to walk the index
 304 *    in parallel with this too!
 305 *
 306 *  - conflict:
 307 *      "1 mode sha1 filename"
 308 *      "2 mode sha1 filename"
 309 *      "3 mode sha1 filename"
 310 *    where not all of the 1/2/3 lines may exist, of course.
 311 *
 312 * The successful merge rules are the same as for the three-way merge
 313 * in git-read-tree.
 314 */
 315static int threeway_callback(int n, unsigned long mask, unsigned long dirmask, struct name_entry *entry, struct traverse_info *info)
 316{
 317        /* Same in both? */
 318        if (same_entry(entry+1, entry+2) || both_empty(entry+1, entry+2)) {
 319                /* Modified, added or removed identically */
 320                resolve(info, NULL, entry+1);
 321                return mask;
 322        }
 323
 324        if (same_entry(entry+0, entry+1)) {
 325                if (!is_null_oid(&entry[2].oid) && !S_ISDIR(entry[2].mode)) {
 326                        /* We did not touch, they modified -- take theirs */
 327                        resolve(info, entry+1, entry+2);
 328                        return mask;
 329                }
 330                /*
 331                 * If we did not touch a directory but they made it
 332                 * into a file, we fall through and unresolved()
 333                 * recurses down.  Likewise for the opposite case.
 334                 */
 335        }
 336
 337        if (same_entry(entry+0, entry+2) || both_empty(entry+0, entry+2)) {
 338                /* We added, modified or removed, they did not touch -- take ours */
 339                resolve(info, NULL, entry+1);
 340                return mask;
 341        }
 342
 343        unresolved(info, entry);
 344        return mask;
 345}
 346
 347static void merge_trees(struct tree_desc t[3], const char *base)
 348{
 349        struct traverse_info info;
 350
 351        setup_traverse_info(&info, base);
 352        info.fn = threeway_callback;
 353        traverse_trees(&the_index, 3, t, &info);
 354}
 355
 356static void *get_tree_descriptor(struct repository *r,
 357                                 struct tree_desc *desc,
 358                                 const char *rev)
 359{
 360        struct object_id oid;
 361        void *buf;
 362
 363        if (repo_get_oid(r, rev, &oid))
 364                die("unknown rev %s", rev);
 365        buf = fill_tree_descriptor(r, desc, &oid);
 366        if (!buf)
 367                die("%s is not a tree", rev);
 368        return buf;
 369}
 370
 371int cmd_merge_tree(int argc, const char **argv, const char *prefix)
 372{
 373        struct repository *r = the_repository;
 374        struct tree_desc t[3];
 375        void *buf1, *buf2, *buf3;
 376
 377        if (argc != 4)
 378                usage(merge_tree_usage);
 379
 380        buf1 = get_tree_descriptor(r, t+0, argv[1]);
 381        buf2 = get_tree_descriptor(r, t+1, argv[2]);
 382        buf3 = get_tree_descriptor(r, t+2, argv[3]);
 383        merge_trees(t, "");
 384        free(buf1);
 385        free(buf2);
 386        free(buf3);
 387
 388        show_result();
 389        return 0;
 390}