merge-file.con commit Add revspec documentation for ':path', ':[0-3]:path' and git-describe (6b09c78)
   1#include "cache.h"
   2#include "run-command.h"
   3#include "xdiff-interface.h"
   4#include "blob.h"
   5
   6static void rm_temp_file(const char *filename)
   7{
   8        unlink(filename);
   9        free((void *)filename);
  10}
  11
  12static const char *write_temp_file(mmfile_t *f)
  13{
  14        int fd;
  15        const char *tmp = getenv("TMPDIR");
  16        char *filename;
  17
  18        if (!tmp)
  19                tmp = "/tmp";
  20        filename = mkpath("%s/%s", tmp, "git-tmp-XXXXXX");
  21        fd = mkstemp(filename);
  22        if (fd < 0)
  23                return NULL;
  24        filename = xstrdup(filename);
  25        if (f->size != xwrite(fd, f->ptr, f->size)) {
  26                rm_temp_file(filename);
  27                return NULL;
  28        }
  29        close(fd);
  30        return filename;
  31}
  32
  33static void *read_temp_file(const char *filename, unsigned long *size)
  34{
  35        struct stat st;
  36        char *buf = NULL;
  37        int fd = open(filename, O_RDONLY);
  38        if (fd < 0)
  39                return NULL;
  40        if (!fstat(fd, &st)) {
  41                *size = st.st_size;
  42                buf = xmalloc(st.st_size);
  43                if (st.st_size != xread(fd, buf, st.st_size)) {
  44                        free(buf);
  45                        buf = NULL;
  46                }
  47        }
  48        close(fd);
  49        return buf;
  50}
  51
  52static int fill_mmfile_blob(mmfile_t *f, struct blob *obj)
  53{
  54        void *buf;
  55        unsigned long size;
  56        char type[20];
  57
  58        buf = read_sha1_file(obj->object.sha1, type, &size);
  59        if (!buf)
  60                return -1;
  61        if (strcmp(type, blob_type))
  62                return -1;
  63        f->ptr = buf;
  64        f->size = size;
  65        return 0;
  66}
  67
  68static void free_mmfile(mmfile_t *f)
  69{
  70        free(f->ptr);
  71}
  72
  73static void *three_way_filemerge(mmfile_t *base, mmfile_t *our, mmfile_t *their, unsigned long *size)
  74{
  75        void *res;
  76        const char *t1, *t2, *t3;
  77
  78        t1 = write_temp_file(base);
  79        t2 = write_temp_file(our);
  80        t3 = write_temp_file(their);
  81        res = NULL;
  82        if (t1 && t2 && t3) {
  83                int code = run_command("merge", t2, t1, t3, NULL);
  84                if (!code || code == -1)
  85                        res = read_temp_file(t2, size);
  86        }
  87        rm_temp_file(t1);
  88        rm_temp_file(t2);
  89        rm_temp_file(t3);
  90        return res;
  91}
  92
  93static int common_outf(void *priv_, mmbuffer_t *mb, int nbuf)
  94{
  95        int i;
  96        mmfile_t *dst = priv_;
  97
  98        for (i = 0; i < nbuf; i++) {
  99                memcpy(dst->ptr + dst->size, mb[i].ptr, mb[i].size);
 100                dst->size += mb[i].size;
 101        }
 102        return 0;
 103}
 104
 105static int generate_common_file(mmfile_t *res, mmfile_t *f1, mmfile_t *f2)
 106{
 107        unsigned long size = f1->size < f2->size ? f1->size : f2->size;
 108        void *ptr = xmalloc(size);
 109        xpparam_t xpp;
 110        xdemitconf_t xecfg;
 111        xdemitcb_t ecb;
 112
 113        xpp.flags = XDF_NEED_MINIMAL;
 114        xecfg.ctxlen = 3;
 115        xecfg.flags = XDL_EMIT_COMMON;
 116        ecb.outf = common_outf;
 117
 118        res->ptr = ptr;
 119        res->size = 0;
 120
 121        ecb.priv = res;
 122        return xdl_diff(f1, f2, &xpp, &xecfg, &ecb);
 123}
 124
 125void *merge_file(struct blob *base, struct blob *our, struct blob *their, unsigned long *size)
 126{
 127        void *res = NULL;
 128        mmfile_t f1, f2, common;
 129
 130        /*
 131         * Removed in either branch?
 132         *
 133         * NOTE! This depends on the caller having done the
 134         * proper warning about removing a file that got
 135         * modified in the other branch!
 136         */
 137        if (!our || !their) {
 138                char type[20];
 139                if (base)
 140                        return NULL;
 141                if (!our)
 142                        our = their;
 143                return read_sha1_file(our->object.sha1, type, size);
 144        }
 145
 146        if (fill_mmfile_blob(&f1, our) < 0)
 147                goto out_no_mmfile;
 148        if (fill_mmfile_blob(&f2, their) < 0)
 149                goto out_free_f1;
 150
 151        if (base) {
 152                if (fill_mmfile_blob(&common, base) < 0)
 153                        goto out_free_f2_f1;
 154        } else {
 155                if (generate_common_file(&common, &f1, &f2) < 0)
 156                        goto out_free_f2_f1;
 157        }
 158        res = three_way_filemerge(&common, &f1, &f2, size);
 159        free_mmfile(&common);
 160out_free_f2_f1:
 161        free_mmfile(&f2);
 162out_free_f1:
 163        free_mmfile(&f1);
 164out_no_mmfile:
 165        return res;
 166}