Line Flow Count Block(s) Source
1 - /*
2 - * Copyright (C) the libgit2 contributors. All rights reserved.
3 - *
4 - * This file is part of libgit2, distributed under the GNU GPL v2 with
5 - * a Linking Exception. For full terms see the included COPYING file.
6 - */
7 -
8 - #include "common.h"
9 -
10 - #include "repository.h"
11 - #include "posix.h"
12 - #include "futils.h"
13 - #include "index.h"
14 - #include "diff_xdiff.h"
15 - #include "merge.h"
16 -
17 - #include "git2/repository.h"
18 - #include "git2/object.h"
19 - #include "git2/index.h"
20 - #include "git2/merge.h"
21 -
22 - #include "xdiff/xdiff.h"
23 -
24 - /* only examine the first 8000 bytes for binaryness.
25 - * https://github.com/git/git/blob/77bd3ea9f54f1584147b594abc04c26ca516d987/xdiff-interface.c#L197
26 - */
27 - #define GIT_MERGE_FILE_BINARY_SIZE 8000
28 -
29 - #define GIT_MERGE_FILE_SIDE_EXISTS(X) ((X)->mode != 0)
30 -
31 1233 2 static int merge_file_input_from_index(
32 - git_merge_file_input *input_out,
33 - git_odb_object **odb_object_out,
34 - git_odb *odb,
35 - const git_index_entry *entry)
36 - {
37 1233 2 int error = 0;
38 -
39 1233 2-6 assert(input_out && odb_object_out && odb && entry);
40 -
41 1233 7,8 if ((error = git_odb_read(odb_object_out, odb, &entry->id)) < 0)
42 ##### 9 goto done;
43 -
44 1233 10 input_out->path = entry->path;
45 1233 10 input_out->mode = entry->mode;
46 1233 10 input_out->ptr = (char *)git_odb_object_data(*odb_object_out);
47 1233 11,12 input_out->size = git_odb_object_size(*odb_object_out);
48 -
49 - done:
50 1233 13 return error;
51 - }
52 -
53 428 2 static void merge_file_normalize_opts(
54 - git_merge_file_options *out,
55 - const git_merge_file_options *given_opts)
56 - {
57 428 2 if (given_opts)
58 424 3 memcpy(out, given_opts, sizeof(git_merge_file_options));
59 - else {
60 4 4 git_merge_file_options default_opts = GIT_MERGE_FILE_OPTIONS_INIT;
61 4 4 memcpy(out, &default_opts, sizeof(git_merge_file_options));
62 - }
63 428 5 }
64 -
65 428 2 static int merge_file__xdiff(
66 - git_merge_file_result *out,
67 - const git_merge_file_input *ancestor,
68 - const git_merge_file_input *ours,
69 - const git_merge_file_input *theirs,
70 - const git_merge_file_options *given_opts)
71 - {
72 - xmparam_t xmparam;
73 428 2 mmfile_t ancestor_mmfile = {0}, our_mmfile = {0}, their_mmfile = {0};
74 - mmbuffer_t mmbuffer;
75 428 2 git_merge_file_options options = GIT_MERGE_FILE_OPTIONS_INIT;
76 - const char *path;
77 - int xdl_result;
78 428 2 int error = 0;
79 -
80 428 2 memset(out, 0x0, sizeof(git_merge_file_result));
81 -
82 428 2 merge_file_normalize_opts(&options, given_opts);
83 -
84 428 3 memset(&xmparam, 0x0, sizeof(xmparam_t));
85 -
86 428 3 if (ancestor) {
87 398 4,7 xmparam.ancestor = (options.ancestor_label) ?
88 398 4-6 options.ancestor_label : ancestor->path;
89 398 7 ancestor_mmfile.ptr = (char *)ancestor->ptr;
90 398 7 ancestor_mmfile.size = ancestor->size;
91 - }
92 -
93 428 8,11 xmparam.file1 = (options.our_label) ?
94 428 8-10 options.our_label : ours->path;
95 428 11 our_mmfile.ptr = (char *)ours->ptr;
96 428 11 our_mmfile.size = ours->size;
97 -
98 428 11,14 xmparam.file2 = (options.their_label) ?
99 428 11-13 options.their_label : theirs->path;
100 428 14 their_mmfile.ptr = (char *)theirs->ptr;
101 428 14 their_mmfile.size = theirs->size;
102 -
103 428 14 if (options.favor == GIT_MERGE_FILE_FAVOR_OURS)
104 7 15 xmparam.favor = XDL_MERGE_FAVOR_OURS;
105 421 16 else if (options.favor == GIT_MERGE_FILE_FAVOR_THEIRS)
106 4 17 xmparam.favor = XDL_MERGE_FAVOR_THEIRS;
107 417 18 else if (options.favor == GIT_MERGE_FILE_FAVOR_UNION)
108 3 19 xmparam.favor = XDL_MERGE_FAVOR_UNION;
109 -
110 428 20,23 xmparam.level = (options.flags & GIT_MERGE_FILE_SIMPLIFY_ALNUM) ?
111 428 20-22 XDL_MERGE_ZEALOUS_ALNUM : XDL_MERGE_ZEALOUS;
112 -
113 428 23 if (options.flags & GIT_MERGE_FILE_STYLE_DIFF3)
114 8 24 xmparam.style = XDL_MERGE_DIFF3;
115 -
116 428 25 if (options.flags & GIT_MERGE_FILE_IGNORE_WHITESPACE)
117 ##### 26 xmparam.xpp.flags |= XDF_IGNORE_WHITESPACE;
118 428 27 if (options.flags & GIT_MERGE_FILE_IGNORE_WHITESPACE_CHANGE)
119 3 28 xmparam.xpp.flags |= XDF_IGNORE_WHITESPACE_CHANGE;
120 428 29 if (options.flags & GIT_MERGE_FILE_IGNORE_WHITESPACE_EOL)
121 2 30 xmparam.xpp.flags |= XDF_IGNORE_WHITESPACE_AT_EOL;
122 -
123 428 31 if (options.flags & GIT_MERGE_FILE_DIFF_PATIENCE)
124 ##### 32 xmparam.xpp.flags |= XDF_PATIENCE_DIFF;
125 -
126 428 33 if (options.flags & GIT_MERGE_FILE_DIFF_MINIMAL)
127 ##### 34 xmparam.xpp.flags |= XDF_NEED_MINIMAL;
128 -
129 428 35 xmparam.marker_size = options.marker_size;
130 -
131 428 35,36 if ((xdl_result = xdl_merge(&ancestor_mmfile, &our_mmfile,
132 - &their_mmfile, &xmparam, &mmbuffer)) < 0) {
133 ##### 37 git_error_set(GIT_ERROR_MERGE, "failed to merge files");
134 ##### 38 error = -1;
135 ##### 38 goto done;
136 - }
137 -
138 428 39-42 path = git_merge_file__best_path(
139 - ancestor ? ancestor->path : NULL,
140 - ours->path,
141 - theirs->path);
142 -
143 428 43-45 if (path != NULL && (out->path = git__strdup(path)) == NULL) {
144 ##### 46 error = -1;
145 ##### 46 goto done;
146 - }
147 -
148 428 47 out->automergeable = (xdl_result == 0);
149 428 47 out->ptr = (const char *)mmbuffer.ptr;
150 428 47 out->len = mmbuffer.size;
151 428 47-51 out->mode = git_merge_file__best_mode(
152 - ancestor ? ancestor->mode : 0,
153 - ours->mode,
154 - theirs->mode);
155 -
156 - done:
157 428 52 if (error < 0)
158 ##### 53 git_merge_file_result_free(out);
159 -
160 428 54 return error;
161 - }
162 -
163 1289 2 static bool merge_file__is_binary(const git_merge_file_input *file)
164 - {
165 1289 2-4 size_t len = file ? file->size : 0;
166 -
167 1289 5 if (len > GIT_XDIFF_MAX_SIZE)
168 1 6 return true;
169 1288 7 if (len > GIT_MERGE_FILE_BINARY_SIZE)
170 ##### 8 len = GIT_MERGE_FILE_BINARY_SIZE;
171 -
172 1288 9 return len ? (memchr(file->ptr, 0, len) != NULL) : false;
173 - }
174 -
175 4 2 static int merge_file__binary(
176 - git_merge_file_result *out,
177 - const git_merge_file_input *ours,
178 - const git_merge_file_input *theirs,
179 - const git_merge_file_options *given_opts)
180 - {
181 4 2 const git_merge_file_input *favored = NULL;
182 -
183 4 2 memset(out, 0x0, sizeof(git_merge_file_result));
184 -
185 4 2,3 if (given_opts && given_opts->favor == GIT_MERGE_FILE_FAVOR_OURS)
186 1 4 favored = ours;
187 3 5,6 else if (given_opts && given_opts->favor == GIT_MERGE_FILE_FAVOR_THEIRS)
188 ##### 7 favored = theirs;
189 - else
190 - goto done;
191 -
192 1 8,9,11 if ((out->path = git__strdup(favored->path)) == NULL ||
193 1 10 (out->ptr = git__malloc(favored->size)) == NULL)
194 - goto done;
195 -
196 1 12 memcpy((char *)out->ptr, favored->ptr, favored->size);
197 1 12 out->len = favored->size;
198 1 12 out->mode = favored->mode;
199 1 12 out->automergeable = 1;
200 -
201 - done:
202 4 13 return 0;
203 - }
204 -
205 432 2 static int merge_file__from_inputs(
206 - git_merge_file_result *out,
207 - const git_merge_file_input *ancestor,
208 - const git_merge_file_input *ours,
209 - const git_merge_file_input *theirs,
210 - const git_merge_file_options *given_opts)
211 - {
212 432 2,3,5 if (merge_file__is_binary(ancestor) ||
213 429 4,7 merge_file__is_binary(ours) ||
214 428 6 merge_file__is_binary(theirs))
215 4 8 return merge_file__binary(out, ours, theirs, given_opts);
216 -
217 428 9 return merge_file__xdiff(out, ancestor, ours, theirs, given_opts);
218 - }
219 -
220 32 2 static git_merge_file_input *git_merge_file__normalize_inputs(
221 - git_merge_file_input *out,
222 - const git_merge_file_input *given)
223 - {
224 32 2 memcpy(out, given, sizeof(git_merge_file_input));
225 -
226 32 2 if (!out->path)
227 ##### 3 out->path = "file.txt";
228 -
229 32 4 if (!out->mode)
230 ##### 5 out->mode = 0100644;
231 -
232 32 6 return out;
233 - }
234 -
235 11 2 int git_merge_file(
236 - git_merge_file_result *out,
237 - const git_merge_file_input *ancestor,
238 - const git_merge_file_input *ours,
239 - const git_merge_file_input *theirs,
240 - const git_merge_file_options *options)
241 - {
242 11 2 git_merge_file_input inputs[3] = { {0} };
243 -
244 11 2-5 assert(out && ours && theirs);
245 -
246 11 6 memset(out, 0x0, sizeof(git_merge_file_result));
247 -
248 11 6 if (ancestor)
249 10 7 ancestor = git_merge_file__normalize_inputs(&inputs[0], ancestor);
250 -
251 11 8 ours = git_merge_file__normalize_inputs(&inputs[1], ours);
252 11 9 theirs = git_merge_file__normalize_inputs(&inputs[2], theirs);
253 -
254 11 10 return merge_file__from_inputs(out, ancestor, ours, theirs, options);
255 - }
256 -
257 421 2 int git_merge_file_from_index(
258 - git_merge_file_result *out,
259 - git_repository *repo,
260 - const git_index_entry *ancestor,
261 - const git_index_entry *ours,
262 - const git_index_entry *theirs,
263 - const git_merge_file_options *options)
264 - {
265 421 2 git_merge_file_input *ancestor_ptr = NULL,
266 421 2 ancestor_input = {0}, our_input = {0}, their_input = {0};
267 421 2 git_odb *odb = NULL;
268 421 2 git_odb_object *odb_object[3] = { 0 };
269 421 2 int error = 0;
270 -
271 421 2-6 assert(out && repo && ours && theirs);
272 -
273 421 7 memset(out, 0x0, sizeof(git_merge_file_result));
274 -
275 421 7,8 if ((error = git_repository_odb(&odb, repo)) < 0)
276 ##### 9 goto done;
277 -
278 421 10 if (ancestor) {
279 391 11,12 if ((error = merge_file_input_from_index(
280 - &ancestor_input, &odb_object[0], odb, ancestor)) < 0)
281 ##### 13 goto done;
282 -
283 391 14 ancestor_ptr = &ancestor_input;
284 - }
285 -
286 421 15-18 if ((error = merge_file_input_from_index(&our_input, &odb_object[1], odb, ours)) < 0 ||
287 421 17 (error = merge_file_input_from_index(&their_input, &odb_object[2], odb, theirs)) < 0)
288 - goto done;
289 -
290 421 19 error = merge_file__from_inputs(out,
291 - ancestor_ptr, &our_input, &their_input, options);
292 -
293 - done:
294 421 20 git_odb_object_free(odb_object[0]);
295 421 21 git_odb_object_free(odb_object[1]);
296 421 22 git_odb_object_free(odb_object[2]);
297 421 23 git_odb_free(odb);
298 -
299 421 24 return error;
300 - }
301 -
302 752 2 void git_merge_file_result_free(git_merge_file_result *result)
303 - {
304 752 2 if (result == NULL)
305 752 3,6 return;
306 -
307 752 4 git__free((char *)result->path);
308 752 5 git__free((char *)result->ptr);
309 - }