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