builtin / merge-tree.con commit test-lib: introduce test_commit_bulk (b1c36cb)
   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        char *path = xmallocz(traverse_path_len(info, n) + the_hash_algo->rawsz);
 184        return make_traverse_path(path, info, n);
 185}
 186
 187static void resolve(const struct traverse_info *info, struct name_entry *ours, struct name_entry *result)
 188{
 189        struct merge_list *orig, *final;
 190        const char *path;
 191
 192        /* If it's already ours, don't bother showing it */
 193        if (!ours)
 194                return;
 195
 196        path = traverse_path(info, result);
 197        orig = create_entry(2, ours->mode, &ours->oid, path);
 198        final = create_entry(0, result->mode, &result->oid, path);
 199
 200        final->link = orig;
 201
 202        add_merge_entry(final);
 203}
 204
 205static void unresolved_directory(const struct traverse_info *info,
 206                                 struct name_entry n[3])
 207{
 208        char *newbase;
 209        struct name_entry *p;
 210        struct tree_desc t[3];
 211        void *buf0, *buf1, *buf2;
 212
 213        for (p = n; p < n + 3; p++) {
 214                if (p->mode && S_ISDIR(p->mode))
 215                        break;
 216        }
 217        if (n + 3 <= p)
 218                return; /* there is no tree here */
 219
 220        newbase = traverse_path(info, p);
 221
 222#define ENTRY_OID(e) (((e)->mode && S_ISDIR((e)->mode)) ? &(e)->oid : NULL)
 223        buf0 = fill_tree_descriptor(t + 0, ENTRY_OID(n + 0));
 224        buf1 = fill_tree_descriptor(t + 1, ENTRY_OID(n + 1));
 225        buf2 = fill_tree_descriptor(t + 2, ENTRY_OID(n + 2));
 226#undef ENTRY_OID
 227
 228        merge_trees(t, newbase);
 229
 230        free(buf0);
 231        free(buf1);
 232        free(buf2);
 233        free(newbase);
 234}
 235
 236
 237static struct merge_list *link_entry(unsigned stage, const struct traverse_info *info, struct name_entry *n, struct merge_list *entry)
 238{
 239        const char *path;
 240        struct merge_list *link;
 241
 242        if (!n->mode)
 243                return entry;
 244        if (entry)
 245                path = entry->path;
 246        else
 247                path = traverse_path(info, n);
 248        link = create_entry(stage, n->mode, &n->oid, path);
 249        link->link = entry;
 250        return link;
 251}
 252
 253static void unresolved(const struct traverse_info *info, struct name_entry n[3])
 254{
 255        struct merge_list *entry = NULL;
 256        int i;
 257        unsigned dirmask = 0, mask = 0;
 258
 259        for (i = 0; i < 3; i++) {
 260                mask |= (1 << i);
 261                /*
 262                 * Treat missing entries as directories so that we return
 263                 * after unresolved_directory has handled this.
 264                 */
 265                if (!n[i].mode || S_ISDIR(n[i].mode))
 266                        dirmask |= (1 << i);
 267        }
 268
 269        unresolved_directory(info, n);
 270
 271        if (dirmask == mask)
 272                return;
 273
 274        if (n[2].mode && !S_ISDIR(n[2].mode))
 275                entry = link_entry(3, info, n + 2, entry);
 276        if (n[1].mode && !S_ISDIR(n[1].mode))
 277                entry = link_entry(2, info, n + 1, entry);
 278        if (n[0].mode && !S_ISDIR(n[0].mode))
 279                entry = link_entry(1, info, n + 0, entry);
 280
 281        add_merge_entry(entry);
 282}
 283
 284/*
 285 * Merge two trees together (t[1] and t[2]), using a common base (t[0])
 286 * as the origin.
 287 *
 288 * This walks the (sorted) trees in lock-step, checking every possible
 289 * name. Note that directories automatically sort differently from other
 290 * files (see "base_name_compare"), so you'll never see file/directory
 291 * conflicts, because they won't ever compare the same.
 292 *
 293 * IOW, if a directory changes to a filename, it will automatically be
 294 * seen as the directory going away, and the filename being created.
 295 *
 296 * Think of this as a three-way diff.
 297 *
 298 * The output will be either:
 299 *  - successful merge
 300 *       "0 mode sha1 filename"
 301 *    NOTE NOTE NOTE! FIXME! We really really need to walk the index
 302 *    in parallel with this too!
 303 *
 304 *  - conflict:
 305 *      "1 mode sha1 filename"
 306 *      "2 mode sha1 filename"
 307 *      "3 mode sha1 filename"
 308 *    where not all of the 1/2/3 lines may exist, of course.
 309 *
 310 * The successful merge rules are the same as for the three-way merge
 311 * in git-read-tree.
 312 */
 313static int threeway_callback(int n, unsigned long mask, unsigned long dirmask, struct name_entry *entry, struct traverse_info *info)
 314{
 315        /* Same in both? */
 316        if (same_entry(entry+1, entry+2) || both_empty(entry+1, entry+2)) {
 317                /* Modified, added or removed identically */
 318                resolve(info, NULL, entry+1);
 319                return mask;
 320        }
 321
 322        if (same_entry(entry+0, entry+1)) {
 323                if (!is_null_oid(&entry[2].oid) && !S_ISDIR(entry[2].mode)) {
 324                        /* We did not touch, they modified -- take theirs */
 325                        resolve(info, entry+1, entry+2);
 326                        return mask;
 327                }
 328                /*
 329                 * If we did not touch a directory but they made it
 330                 * into a file, we fall through and unresolved()
 331                 * recurses down.  Likewise for the opposite case.
 332                 */
 333        }
 334
 335        if (same_entry(entry+0, entry+2) || both_empty(entry+0, entry+2)) {
 336                /* We added, modified or removed, they did not touch -- take ours */
 337                resolve(info, NULL, entry+1);
 338                return mask;
 339        }
 340
 341        unresolved(info, entry);
 342        return mask;
 343}
 344
 345static void merge_trees(struct tree_desc t[3], const char *base)
 346{
 347        struct traverse_info info;
 348
 349        setup_traverse_info(&info, base);
 350        info.fn = threeway_callback;
 351        traverse_trees(&the_index, 3, t, &info);
 352}
 353
 354static void *get_tree_descriptor(struct tree_desc *desc, const char *rev)
 355{
 356        struct object_id oid;
 357        void *buf;
 358
 359        if (get_oid(rev, &oid))
 360                die("unknown rev %s", rev);
 361        buf = fill_tree_descriptor(desc, &oid);
 362        if (!buf)
 363                die("%s is not a tree", rev);
 364        return buf;
 365}
 366
 367int cmd_merge_tree(int argc, const char **argv, const char *prefix)
 368{
 369        struct tree_desc t[3];
 370        void *buf1, *buf2, *buf3;
 371
 372        if (argc != 4)
 373                usage(merge_tree_usage);
 374
 375        buf1 = get_tree_descriptor(t+0, argv[1]);
 376        buf2 = get_tree_descriptor(t+1, argv[2]);
 377        buf3 = get_tree_descriptor(t+2, argv[3]);
 378        merge_trees(t, "");
 379        free(buf1);
 380        free(buf2);
 381        free(buf3);
 382
 383        show_result();
 384        return 0;
 385}