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