apply --reverse: tie it all together.
[git] / merge-file.c
1 #include "cache.h"
2 #include "run-command.h"
3 #include "xdiff-interface.h"
4 #include "blob.h"
5
6 static void rm_temp_file(const char *filename)
7 {
8         unlink(filename);
9         free((void *)filename);
10 }
11
12 static 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 = strdup(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
33 static 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
52 static 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
68 static void free_mmfile(mmfile_t *f)
69 {
70         free(f->ptr);
71 }
72
73 static 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
93 static 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
105 static 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
125 void *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);
160 out_free_f2_f1:
161         free_mmfile(&f2);
162 out_free_f1:
163         free_mmfile(&f1);
164 out_no_mmfile:
165         return res;
166 }