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 | }
|
---|