]> git.proxmox.com Git - libgit2.git/blame - src/merge_file.c
Merge pull request #3097 from libgit2/cmn/submodule-config-state
[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"
9#include "repository.h"
10#include "merge_file.h"
05d47768
ET
11#include "posix.h"
12#include "fileops.h"
13#include "index.h"
bec65a5e
ET
14
15#include "git2/repository.h"
16#include "git2/object.h"
17#include "git2/index.h"
18
19#include "xdiff/xdiff.h"
20
21#define GIT_MERGE_FILE_SIDE_EXISTS(X) ((X)->mode != 0)
22
23GIT_INLINE(const char *) merge_file_best_path(
24 const git_merge_file_input *ancestor,
25 const git_merge_file_input *ours,
26 const git_merge_file_input *theirs)
27{
05d47768
ET
28 if (!ancestor) {
29 if (ours && theirs && strcmp(ours->path, theirs->path) == 0)
bec65a5e
ET
30 return ours->path;
31
32 return NULL;
33 }
1fed6b07 34
05d47768
ET
35 if (ours && strcmp(ancestor->path, ours->path) == 0)
36 return theirs ? theirs->path : NULL;
37 else if(theirs && strcmp(ancestor->path, theirs->path) == 0)
38 return ours ? ours->path : NULL;
1fed6b07 39
bec65a5e
ET
40 return NULL;
41}
42
43GIT_INLINE(int) merge_file_best_mode(
44 const git_merge_file_input *ancestor,
45 const git_merge_file_input *ours,
46 const git_merge_file_input *theirs)
47{
48 /*
49 * If ancestor didn't exist and either ours or theirs is executable,
50 * assume executable. Otherwise, if any mode changed from the ancestor,
51 * use that one.
52 */
05d47768
ET
53 if (!ancestor) {
54 if ((ours && ours->mode == GIT_FILEMODE_BLOB_EXECUTABLE) ||
55 (theirs && theirs->mode == GIT_FILEMODE_BLOB_EXECUTABLE))
bec65a5e 56 return GIT_FILEMODE_BLOB_EXECUTABLE;
1fed6b07 57
bec65a5e 58 return GIT_FILEMODE_BLOB;
05d47768
ET
59 } else if (ours && theirs) {
60 if (ancestor->mode == ours->mode)
61 return theirs->mode;
1fed6b07 62
bec65a5e 63 return ours->mode;
05d47768 64 }
1fed6b07 65
bec65a5e
ET
66 return 0;
67}
68
05d47768
ET
69int git_merge_file__input_from_index(
70 git_merge_file_input *input_out,
71 git_odb_object **odb_object_out,
72 git_odb *odb,
bec65a5e
ET
73 const git_index_entry *entry)
74{
bec65a5e 75 int error = 0;
1fed6b07 76
05d47768 77 assert(input_out && odb_object_out && odb && entry);
1fed6b07 78
05d47768 79 if ((error = git_odb_read(odb_object_out, odb, &entry->id)) < 0)
bec65a5e 80 goto done;
1fed6b07 81
05d47768
ET
82 input_out->path = entry->path;
83 input_out->mode = entry->mode;
84 input_out->ptr = (char *)git_odb_object_data(*odb_object_out);
85 input_out->size = git_odb_object_size(*odb_object_out);
1fed6b07 86
bec65a5e 87done:
bec65a5e
ET
88 return error;
89}
90
05d47768
ET
91static void merge_file_normalize_opts(
92 git_merge_file_options *out,
93 const git_merge_file_options *given_opts)
bec65a5e 94{
05d47768
ET
95 if (given_opts)
96 memcpy(out, given_opts, sizeof(git_merge_file_options));
97 else {
98 git_merge_file_options default_opts = GIT_MERGE_FILE_OPTIONS_INIT;
99 memcpy(out, &default_opts, sizeof(git_merge_file_options));
100 }
bec65a5e
ET
101}
102
05d47768 103static int git_merge_file__from_inputs(
bec65a5e 104 git_merge_file_result *out,
05d47768
ET
105 const git_merge_file_input *ancestor,
106 const git_merge_file_input *ours,
107 const git_merge_file_input *theirs,
108 const git_merge_file_options *given_opts)
bec65a5e
ET
109{
110 xmparam_t xmparam;
05d47768 111 mmfile_t ancestor_mmfile = {0}, our_mmfile = {0}, their_mmfile = {0};
bec65a5e 112 mmbuffer_t mmbuffer;
05d47768
ET
113 git_merge_file_options options = GIT_MERGE_FILE_OPTIONS_INIT;
114 const char *path;
bec65a5e
ET
115 int xdl_result;
116 int error = 0;
117
bec65a5e
ET
118 memset(out, 0x0, sizeof(git_merge_file_result));
119
7dcd42a5 120 merge_file_normalize_opts(&options, given_opts);
1fed6b07 121
bec65a5e 122 memset(&xmparam, 0x0, sizeof(xmparam_t));
bec65a5e 123
05d47768
ET
124 if (ancestor) {
125 xmparam.ancestor = (options.ancestor_label) ?
126 options.ancestor_label : ancestor->path;
127 ancestor_mmfile.ptr = (char *)ancestor->ptr;
128 ancestor_mmfile.size = ancestor->size;
129 }
bec65a5e 130
05d47768
ET
131 xmparam.file1 = (options.our_label) ?
132 options.our_label : ours->path;
133 our_mmfile.ptr = (char *)ours->ptr;
134 our_mmfile.size = ours->size;
135
136 xmparam.file2 = (options.their_label) ?
137 options.their_label : theirs->path;
138 their_mmfile.ptr = (char *)theirs->ptr;
139 their_mmfile.size = theirs->size;
140
141 if (options.favor == GIT_MERGE_FILE_FAVOR_OURS)
bec65a5e 142 xmparam.favor = XDL_MERGE_FAVOR_OURS;
05d47768 143 else if (options.favor == GIT_MERGE_FILE_FAVOR_THEIRS)
bec65a5e 144 xmparam.favor = XDL_MERGE_FAVOR_THEIRS;
05d47768 145 else if (options.favor == GIT_MERGE_FILE_FAVOR_UNION)
db3462ce 146 xmparam.favor = XDL_MERGE_FAVOR_UNION;
bec65a5e 147
05d47768 148 xmparam.level = (options.flags & GIT_MERGE_FILE_SIMPLIFY_ALNUM) ?
c1d648c5
ET
149 XDL_MERGE_ZEALOUS_ALNUM : XDL_MERGE_ZEALOUS;
150
05d47768 151 if (options.flags & GIT_MERGE_FILE_STYLE_DIFF3)
e651e8e2
ET
152 xmparam.style = XDL_MERGE_DIFF3;
153
13de9363 154 if (options.flags & GIT_MERGE_FILE_IGNORE_WHITESPACE)
45a86bbf 155 xmparam.xpp.flags |= XDF_IGNORE_WHITESPACE;
13de9363 156 if (options.flags & GIT_MERGE_FILE_IGNORE_WHITESPACE_CHANGE)
45a86bbf 157 xmparam.xpp.flags |= XDF_IGNORE_WHITESPACE_CHANGE;
13de9363 158 if (options.flags & GIT_MERGE_FILE_IGNORE_WHITESPACE_EOL)
45a86bbf
JG
159 xmparam.xpp.flags |= XDF_IGNORE_WHITESPACE_AT_EOL;
160
74c37c2a
JG
161 if (options.flags & GIT_MERGE_FILE_DIFF_PATIENCE)
162 xmparam.xpp.flags |= XDF_PATIENCE_DIFF;
163
164 if (options.flags & GIT_MERGE_FILE_DIFF_MINIMAL)
165 xmparam.xpp.flags |= XDF_NEED_MINIMAL;
166
05d47768
ET
167 if ((xdl_result = xdl_merge(&ancestor_mmfile, &our_mmfile,
168 &their_mmfile, &xmparam, &mmbuffer)) < 0) {
bec65a5e
ET
169 giterr_set(GITERR_MERGE, "Failed to merge files.");
170 error = -1;
171 goto done;
172 }
1fed6b07 173
05d47768
ET
174 if ((path = merge_file_best_path(ancestor, ours, theirs)) != NULL &&
175 (out->path = strdup(path)) == NULL) {
176 error = -1;
177 goto done;
178 }
179
bec65a5e 180 out->automergeable = (xdl_result == 0);
7dcd42a5 181 out->ptr = (const char *)mmbuffer.ptr;
bec65a5e 182 out->len = mmbuffer.size;
05d47768 183 out->mode = merge_file_best_mode(ancestor, ours, theirs);
bec65a5e
ET
184
185done:
05d47768
ET
186 if (error < 0)
187 git_merge_file_result_free(out);
188
bec65a5e
ET
189 return error;
190}
05d47768
ET
191
192static git_merge_file_input *git_merge_file__normalize_inputs(
193 git_merge_file_input *out,
194 const git_merge_file_input *given)
195{
196 memcpy(out, given, sizeof(git_merge_file_input));
197
198 if (!out->path)
199 out->path = "file.txt";
200
201 if (!out->mode)
202 out->mode = 0100644;
203
204 return out;
205}
206
207int git_merge_file(
208 git_merge_file_result *out,
209 const git_merge_file_input *ancestor,
210 const git_merge_file_input *ours,
211 const git_merge_file_input *theirs,
212 const git_merge_file_options *options)
213{
214 git_merge_file_input inputs[3] = { {0} };
215
216 assert(out && ours && theirs);
217
218 memset(out, 0x0, sizeof(git_merge_file_result));
219
220 if (ancestor)
221 ancestor = git_merge_file__normalize_inputs(&inputs[0], ancestor);
222
223 ours = git_merge_file__normalize_inputs(&inputs[1], ours);
224 theirs = git_merge_file__normalize_inputs(&inputs[2], theirs);
225
226 return git_merge_file__from_inputs(out, ancestor, ours, theirs, options);
227}
228
229int git_merge_file_from_index(
230 git_merge_file_result *out,
231 git_repository *repo,
232 const git_index_entry *ancestor,
233 const git_index_entry *ours,
234 const git_index_entry *theirs,
235 const git_merge_file_options *options)
236{
237 git_merge_file_input inputs[3] = { {0} },
238 *ancestor_input = NULL, *our_input = NULL, *their_input = NULL;
239 git_odb *odb = NULL;
240 git_odb_object *odb_object[3] = { 0 };
241 int error = 0;
242
243 assert(out && repo && ours && theirs);
244
245 memset(out, 0x0, sizeof(git_merge_file_result));
246
247 if ((error = git_repository_odb(&odb, repo)) < 0)
248 goto done;
249
250 if (ancestor) {
251 if ((error = git_merge_file__input_from_index(
252 &inputs[0], &odb_object[0], odb, ancestor)) < 0)
253 goto done;
254
255 ancestor_input = &inputs[0];
256 }
257
258 if ((error = git_merge_file__input_from_index(
259 &inputs[1], &odb_object[1], odb, ours)) < 0)
260 goto done;
261
262 our_input = &inputs[1];
263
264 if ((error = git_merge_file__input_from_index(
265 &inputs[2], &odb_object[2], odb, theirs)) < 0)
266 goto done;
267
268 their_input = &inputs[2];
269
270 if ((error = git_merge_file__from_inputs(out,
271 ancestor_input, our_input, their_input, options)) < 0)
272 goto done;
273
274done:
275 git_odb_object_free(odb_object[0]);
276 git_odb_object_free(odb_object[1]);
277 git_odb_object_free(odb_object[2]);
278 git_odb_free(odb);
279
280 return error;
281}
282
283void git_merge_file_result_free(git_merge_file_result *result)
284{
285 if (result == NULL)
286 return;
287
7f930ded 288 git__free((char *)result->path);
05d47768
ET
289
290 /* xdiff uses malloc() not git_malloc, so we use free(), not git_free() */
7f930ded 291 free((char *)result->ptr);
05d47768 292}