]> git.proxmox.com Git - libgit2.git/blame - src/merge_file.c
New upstream version 1.4.3+dfsg.1
[libgit2.git] / src / merge_file.c
CommitLineData
bec65a5e
ET
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"
eae0bfdc 9
bec65a5e 10#include "repository.h"
05d47768 11#include "posix.h"
22a2d3d5 12#include "futils.h"
05d47768 13#include "index.h"
6c014bcc 14#include "diff_xdiff.h"
3f04219f 15#include "merge.h"
bec65a5e
ET
16
17#include "git2/repository.h"
18#include "git2/object.h"
19#include "git2/index.h"
e4352066 20#include "git2/merge.h"
bec65a5e
ET
21
22#include "xdiff/xdiff.h"
23
e4352066
ET
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
bec65a5e
ET
29#define GIT_MERGE_FILE_SIDE_EXISTS(X) ((X)->mode != 0)
30
22a2d3d5 31static int merge_file_input_from_index(
05d47768
ET
32 git_merge_file_input *input_out,
33 git_odb_object **odb_object_out,
34 git_odb *odb,
bec65a5e
ET
35 const git_index_entry *entry)
36{
bec65a5e 37 int error = 0;
1fed6b07 38
c25aa7cd
PP
39 GIT_ASSERT_ARG(input_out);
40 GIT_ASSERT_ARG(odb_object_out);
41 GIT_ASSERT_ARG(odb);
42 GIT_ASSERT_ARG(entry);
1fed6b07 43
05d47768 44 if ((error = git_odb_read(odb_object_out, odb, &entry->id)) < 0)
bec65a5e 45 goto done;
1fed6b07 46
05d47768
ET
47 input_out->path = entry->path;
48 input_out->mode = entry->mode;
49 input_out->ptr = (char *)git_odb_object_data(*odb_object_out);
50 input_out->size = git_odb_object_size(*odb_object_out);
1fed6b07 51
bec65a5e 52done:
bec65a5e
ET
53 return error;
54}
55
05d47768
ET
56static void merge_file_normalize_opts(
57 git_merge_file_options *out,
58 const git_merge_file_options *given_opts)
bec65a5e 59{
05d47768
ET
60 if (given_opts)
61 memcpy(out, given_opts, sizeof(git_merge_file_options));
62 else {
63 git_merge_file_options default_opts = GIT_MERGE_FILE_OPTIONS_INIT;
64 memcpy(out, &default_opts, sizeof(git_merge_file_options));
65 }
bec65a5e
ET
66}
67
e4352066 68static int merge_file__xdiff(
bec65a5e 69 git_merge_file_result *out,
05d47768
ET
70 const git_merge_file_input *ancestor,
71 const git_merge_file_input *ours,
72 const git_merge_file_input *theirs,
73 const git_merge_file_options *given_opts)
bec65a5e
ET
74{
75 xmparam_t xmparam;
05d47768 76 mmfile_t ancestor_mmfile = {0}, our_mmfile = {0}, their_mmfile = {0};
bec65a5e 77 mmbuffer_t mmbuffer;
05d47768
ET
78 git_merge_file_options options = GIT_MERGE_FILE_OPTIONS_INIT;
79 const char *path;
bec65a5e
ET
80 int xdl_result;
81 int error = 0;
82
bec65a5e
ET
83 memset(out, 0x0, sizeof(git_merge_file_result));
84
7dcd42a5 85 merge_file_normalize_opts(&options, given_opts);
1fed6b07 86
bec65a5e 87 memset(&xmparam, 0x0, sizeof(xmparam_t));
bec65a5e 88
e579e0f7
MB
89 if (ours->size > LONG_MAX ||
90 theirs->size > LONG_MAX ||
91 (ancestor && ancestor->size > LONG_MAX)) {
92 git_error_set(GIT_ERROR_MERGE, "failed to merge files");
93 error = -1;
94 goto done;
95 }
96
05d47768
ET
97 if (ancestor) {
98 xmparam.ancestor = (options.ancestor_label) ?
99 options.ancestor_label : ancestor->path;
100 ancestor_mmfile.ptr = (char *)ancestor->ptr;
e579e0f7 101 ancestor_mmfile.size = (long)ancestor->size;
05d47768 102 }
bec65a5e 103
05d47768
ET
104 xmparam.file1 = (options.our_label) ?
105 options.our_label : ours->path;
106 our_mmfile.ptr = (char *)ours->ptr;
e579e0f7 107 our_mmfile.size = (long)ours->size;
05d47768
ET
108
109 xmparam.file2 = (options.their_label) ?
110 options.their_label : theirs->path;
111 their_mmfile.ptr = (char *)theirs->ptr;
e579e0f7 112 their_mmfile.size = (long)theirs->size;
05d47768
ET
113
114 if (options.favor == GIT_MERGE_FILE_FAVOR_OURS)
bec65a5e 115 xmparam.favor = XDL_MERGE_FAVOR_OURS;
05d47768 116 else if (options.favor == GIT_MERGE_FILE_FAVOR_THEIRS)
bec65a5e 117 xmparam.favor = XDL_MERGE_FAVOR_THEIRS;
05d47768 118 else if (options.favor == GIT_MERGE_FILE_FAVOR_UNION)
db3462ce 119 xmparam.favor = XDL_MERGE_FAVOR_UNION;
bec65a5e 120
05d47768 121 xmparam.level = (options.flags & GIT_MERGE_FILE_SIMPLIFY_ALNUM) ?
c1d648c5
ET
122 XDL_MERGE_ZEALOUS_ALNUM : XDL_MERGE_ZEALOUS;
123
05d47768 124 if (options.flags & GIT_MERGE_FILE_STYLE_DIFF3)
e651e8e2 125 xmparam.style = XDL_MERGE_DIFF3;
e579e0f7
MB
126 if (options.flags & GIT_MERGE_FILE_STYLE_ZDIFF3)
127 xmparam.style = XDL_MERGE_ZEALOUS_DIFF3;
e651e8e2 128
13de9363 129 if (options.flags & GIT_MERGE_FILE_IGNORE_WHITESPACE)
45a86bbf 130 xmparam.xpp.flags |= XDF_IGNORE_WHITESPACE;
13de9363 131 if (options.flags & GIT_MERGE_FILE_IGNORE_WHITESPACE_CHANGE)
45a86bbf 132 xmparam.xpp.flags |= XDF_IGNORE_WHITESPACE_CHANGE;
13de9363 133 if (options.flags & GIT_MERGE_FILE_IGNORE_WHITESPACE_EOL)
45a86bbf
JG
134 xmparam.xpp.flags |= XDF_IGNORE_WHITESPACE_AT_EOL;
135
74c37c2a
JG
136 if (options.flags & GIT_MERGE_FILE_DIFF_PATIENCE)
137 xmparam.xpp.flags |= XDF_PATIENCE_DIFF;
138
139 if (options.flags & GIT_MERGE_FILE_DIFF_MINIMAL)
140 xmparam.xpp.flags |= XDF_NEED_MINIMAL;
141
eae0bfdc
PP
142 xmparam.marker_size = options.marker_size;
143
05d47768
ET
144 if ((xdl_result = xdl_merge(&ancestor_mmfile, &our_mmfile,
145 &their_mmfile, &xmparam, &mmbuffer)) < 0) {
ac3d33df 146 git_error_set(GIT_ERROR_MERGE, "failed to merge files");
bec65a5e
ET
147 error = -1;
148 goto done;
149 }
1fed6b07 150
3f04219f
ET
151 path = git_merge_file__best_path(
152 ancestor ? ancestor->path : NULL,
fcd1b786
PS
153 ours->path,
154 theirs->path);
3f04219f
ET
155
156 if (path != NULL && (out->path = git__strdup(path)) == NULL) {
05d47768
ET
157 error = -1;
158 goto done;
159 }
160
bec65a5e 161 out->automergeable = (xdl_result == 0);
7dcd42a5 162 out->ptr = (const char *)mmbuffer.ptr;
bec65a5e 163 out->len = mmbuffer.size;
3f04219f
ET
164 out->mode = git_merge_file__best_mode(
165 ancestor ? ancestor->mode : 0,
fcd1b786
PS
166 ours->mode,
167 theirs->mode);
bec65a5e
ET
168
169done:
05d47768
ET
170 if (error < 0)
171 git_merge_file_result_free(out);
172
bec65a5e
ET
173 return error;
174}
05d47768 175
e4352066
ET
176static bool merge_file__is_binary(const git_merge_file_input *file)
177{
178 size_t len = file ? file->size : 0;
179
6c014bcc 180 if (len > GIT_XDIFF_MAX_SIZE)
e4352066
ET
181 return true;
182 if (len > GIT_MERGE_FILE_BINARY_SIZE)
183 len = GIT_MERGE_FILE_BINARY_SIZE;
184
185 return len ? (memchr(file->ptr, 0, len) != NULL) : false;
186}
187
188static int merge_file__binary(
189 git_merge_file_result *out,
190 const git_merge_file_input *ours,
191 const git_merge_file_input *theirs,
192 const git_merge_file_options *given_opts)
193{
194 const git_merge_file_input *favored = NULL;
195
196 memset(out, 0x0, sizeof(git_merge_file_result));
197
198 if (given_opts && given_opts->favor == GIT_MERGE_FILE_FAVOR_OURS)
199 favored = ours;
200 else if (given_opts && given_opts->favor == GIT_MERGE_FILE_FAVOR_THEIRS)
201 favored = theirs;
202 else
203 goto done;
204
205 if ((out->path = git__strdup(favored->path)) == NULL ||
206 (out->ptr = git__malloc(favored->size)) == NULL)
207 goto done;
208
209 memcpy((char *)out->ptr, favored->ptr, favored->size);
210 out->len = favored->size;
211 out->mode = favored->mode;
212 out->automergeable = 1;
213
214done:
215 return 0;
216}
217
218static int merge_file__from_inputs(
219 git_merge_file_result *out,
220 const git_merge_file_input *ancestor,
221 const git_merge_file_input *ours,
222 const git_merge_file_input *theirs,
223 const git_merge_file_options *given_opts)
224{
225 if (merge_file__is_binary(ancestor) ||
226 merge_file__is_binary(ours) ||
227 merge_file__is_binary(theirs))
228 return merge_file__binary(out, ours, theirs, given_opts);
229
230 return merge_file__xdiff(out, ancestor, ours, theirs, given_opts);
231}
232
05d47768
ET
233static git_merge_file_input *git_merge_file__normalize_inputs(
234 git_merge_file_input *out,
235 const git_merge_file_input *given)
236{
237 memcpy(out, given, sizeof(git_merge_file_input));
238
239 if (!out->path)
240 out->path = "file.txt";
241
242 if (!out->mode)
243 out->mode = 0100644;
244
245 return out;
246}
247
248int git_merge_file(
249 git_merge_file_result *out,
250 const git_merge_file_input *ancestor,
251 const git_merge_file_input *ours,
252 const git_merge_file_input *theirs,
253 const git_merge_file_options *options)
254{
255 git_merge_file_input inputs[3] = { {0} };
256
c25aa7cd
PP
257 GIT_ASSERT_ARG(out);
258 GIT_ASSERT_ARG(ours);
259 GIT_ASSERT_ARG(theirs);
05d47768
ET
260
261 memset(out, 0x0, sizeof(git_merge_file_result));
262
263 if (ancestor)
264 ancestor = git_merge_file__normalize_inputs(&inputs[0], ancestor);
265
266 ours = git_merge_file__normalize_inputs(&inputs[1], ours);
267 theirs = git_merge_file__normalize_inputs(&inputs[2], theirs);
268
e4352066 269 return merge_file__from_inputs(out, ancestor, ours, theirs, options);
05d47768
ET
270}
271
272int git_merge_file_from_index(
273 git_merge_file_result *out,
274 git_repository *repo,
275 const git_index_entry *ancestor,
276 const git_index_entry *ours,
277 const git_index_entry *theirs,
278 const git_merge_file_options *options)
279{
e4352066
ET
280 git_merge_file_input *ancestor_ptr = NULL,
281 ancestor_input = {0}, our_input = {0}, their_input = {0};
05d47768
ET
282 git_odb *odb = NULL;
283 git_odb_object *odb_object[3] = { 0 };
284 int error = 0;
285
c25aa7cd
PP
286 GIT_ASSERT_ARG(out);
287 GIT_ASSERT_ARG(repo);
288 GIT_ASSERT_ARG(ours);
289 GIT_ASSERT_ARG(theirs);
05d47768
ET
290
291 memset(out, 0x0, sizeof(git_merge_file_result));
292
293 if ((error = git_repository_odb(&odb, repo)) < 0)
294 goto done;
295
296 if (ancestor) {
22a2d3d5 297 if ((error = merge_file_input_from_index(
e4352066 298 &ancestor_input, &odb_object[0], odb, ancestor)) < 0)
05d47768
ET
299 goto done;
300
e4352066 301 ancestor_ptr = &ancestor_input;
05d47768
ET
302 }
303
22a2d3d5
UG
304 if ((error = merge_file_input_from_index(&our_input, &odb_object[1], odb, ours)) < 0 ||
305 (error = merge_file_input_from_index(&their_input, &odb_object[2], odb, theirs)) < 0)
05d47768
ET
306 goto done;
307
e4352066
ET
308 error = merge_file__from_inputs(out,
309 ancestor_ptr, &our_input, &their_input, options);
05d47768
ET
310
311done:
312 git_odb_object_free(odb_object[0]);
313 git_odb_object_free(odb_object[1]);
314 git_odb_object_free(odb_object[2]);
315 git_odb_free(odb);
316
317 return error;
318}
319
320void git_merge_file_result_free(git_merge_file_result *result)
321{
322 if (result == NULL)
323 return;
324
7f930ded 325 git__free((char *)result->path);
e4352066 326 git__free((char *)result->ptr);
05d47768 327}