merge-file.con commit git-svn: check_cherry_pick should exclude commits already in our history (a3c7505)
   1#include "cache.h"
   2#include "run-command.h"
   3#include "xdiff-interface.h"
   4#include "ll-merge.h"
   5#include "blob.h"
   6
   7static int fill_mmfile_blob(mmfile_t *f, struct blob *obj)
   8{
   9        void *buf;
  10        unsigned long size;
  11        enum object_type type;
  12
  13        buf = read_sha1_file(obj->object.sha1, &type, &size);
  14        if (!buf)
  15                return -1;
  16        if (type != OBJ_BLOB)
  17                return -1;
  18        f->ptr = buf;
  19        f->size = size;
  20        return 0;
  21}
  22
  23static void free_mmfile(mmfile_t *f)
  24{
  25        free(f->ptr);
  26}
  27
  28static void *three_way_filemerge(const char *path, mmfile_t *base, mmfile_t *our, mmfile_t *their, unsigned long *size)
  29{
  30        int merge_status;
  31        mmbuffer_t res;
  32
  33        /*
  34         * This function is only used by cmd_merge_tree, which
  35         * does not respect the merge.conflictstyle option.
  36         * There is no need to worry about a label for the
  37         * common ancestor.
  38         */
  39        merge_status = ll_merge(&res, path, base, NULL,
  40                                our, ".our", their, ".their", 0);
  41        if (merge_status < 0)
  42                return NULL;
  43
  44        *size = res.size;
  45        return res.ptr;
  46}
  47
  48static int common_outf(void *priv_, mmbuffer_t *mb, int nbuf)
  49{
  50        int i;
  51        mmfile_t *dst = priv_;
  52
  53        for (i = 0; i < nbuf; i++) {
  54                memcpy(dst->ptr + dst->size, mb[i].ptr, mb[i].size);
  55                dst->size += mb[i].size;
  56        }
  57        return 0;
  58}
  59
  60static int generate_common_file(mmfile_t *res, mmfile_t *f1, mmfile_t *f2)
  61{
  62        unsigned long size = f1->size < f2->size ? f1->size : f2->size;
  63        void *ptr = xmalloc(size);
  64        xpparam_t xpp;
  65        xdemitconf_t xecfg;
  66        xdemitcb_t ecb;
  67
  68        memset(&xpp, 0, sizeof(xpp));
  69        xpp.flags = 0;
  70        memset(&xecfg, 0, sizeof(xecfg));
  71        xecfg.ctxlen = 3;
  72        xecfg.flags = XDL_EMIT_COMMON;
  73        ecb.outf = common_outf;
  74
  75        res->ptr = ptr;
  76        res->size = 0;
  77
  78        ecb.priv = res;
  79        return xdi_diff(f1, f2, &xpp, &xecfg, &ecb);
  80}
  81
  82void *merge_file(const char *path, struct blob *base, struct blob *our, struct blob *their, unsigned long *size)
  83{
  84        void *res = NULL;
  85        mmfile_t f1, f2, common;
  86
  87        /*
  88         * Removed in either branch?
  89         *
  90         * NOTE! This depends on the caller having done the
  91         * proper warning about removing a file that got
  92         * modified in the other branch!
  93         */
  94        if (!our || !their) {
  95                enum object_type type;
  96                if (base)
  97                        return NULL;
  98                if (!our)
  99                        our = their;
 100                return read_sha1_file(our->object.sha1, &type, size);
 101        }
 102
 103        if (fill_mmfile_blob(&f1, our) < 0)
 104                goto out_no_mmfile;
 105        if (fill_mmfile_blob(&f2, their) < 0)
 106                goto out_free_f1;
 107
 108        if (base) {
 109                if (fill_mmfile_blob(&common, base) < 0)
 110                        goto out_free_f2_f1;
 111        } else {
 112                if (generate_common_file(&common, &f1, &f2) < 0)
 113                        goto out_free_f2_f1;
 114        }
 115        res = three_way_filemerge(path, &common, &f1, &f2, size);
 116        free_mmfile(&common);
 117out_free_f2_f1:
 118        free_mmfile(&f2);
 119out_free_f1:
 120        free_mmfile(&f1);
 121out_no_mmfile:
 122        return res;
 123}