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