2 * Copyright (C) the libgit2 contributors. All rights reserved.
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.
9 #include "repository.h"
10 #include "merge_file.h"
15 #include "git2/repository.h"
16 #include "git2/object.h"
17 #include "git2/index.h"
19 #include "xdiff/xdiff.h"
21 #define GIT_MERGE_FILE_SIDE_EXISTS(X) ((X)->mode != 0)
23 GIT_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
)
29 if (ours
&& theirs
&& strcmp(ours
->path
, theirs
->path
) == 0)
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
;
43 GIT_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
)
49 * If ancestor didn't exist and either ours or theirs is executable,
50 * assume executable. Otherwise, if any mode changed from the ancestor,
54 if ((ours
&& ours
->mode
== GIT_FILEMODE_BLOB_EXECUTABLE
) ||
55 (theirs
&& theirs
->mode
== GIT_FILEMODE_BLOB_EXECUTABLE
))
56 return GIT_FILEMODE_BLOB_EXECUTABLE
;
58 return GIT_FILEMODE_BLOB
;
59 } else if (ours
&& theirs
) {
60 if (ancestor
->mode
== ours
->mode
)
69 int git_merge_file__input_from_index(
70 git_merge_file_input
*input_out
,
71 git_odb_object
**odb_object_out
,
73 const git_index_entry
*entry
)
77 assert(input_out
&& odb_object_out
&& odb
&& entry
);
79 if ((error
= git_odb_read(odb_object_out
, odb
, &entry
->id
)) < 0)
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
);
91 static void merge_file_normalize_opts(
92 git_merge_file_options
*out
,
93 const git_merge_file_options
*given_opts
)
96 memcpy(out
, given_opts
, sizeof(git_merge_file_options
));
98 git_merge_file_options default_opts
= GIT_MERGE_FILE_OPTIONS_INIT
;
99 memcpy(out
, &default_opts
, sizeof(git_merge_file_options
));
103 static int git_merge_file__from_inputs(
104 git_merge_file_result
*out
,
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
)
111 mmfile_t ancestor_mmfile
= {0}, our_mmfile
= {0}, their_mmfile
= {0};
113 git_merge_file_options options
= GIT_MERGE_FILE_OPTIONS_INIT
;
118 memset(out
, 0x0, sizeof(git_merge_file_result
));
120 merge_file_normalize_opts(&options
, given_opts
);
122 memset(&xmparam
, 0x0, sizeof(xmparam_t
));
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
;
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
;
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
;
141 if (options
.favor
== GIT_MERGE_FILE_FAVOR_OURS
)
142 xmparam
.favor
= XDL_MERGE_FAVOR_OURS
;
143 else if (options
.favor
== GIT_MERGE_FILE_FAVOR_THEIRS
)
144 xmparam
.favor
= XDL_MERGE_FAVOR_THEIRS
;
145 else if (options
.favor
== GIT_MERGE_FILE_FAVOR_UNION
)
146 xmparam
.favor
= XDL_MERGE_FAVOR_UNION
;
148 xmparam
.level
= (options
.flags
& GIT_MERGE_FILE_SIMPLIFY_ALNUM
) ?
149 XDL_MERGE_ZEALOUS_ALNUM
: XDL_MERGE_ZEALOUS
;
151 if (options
.flags
& GIT_MERGE_FILE_STYLE_DIFF3
)
152 xmparam
.style
= XDL_MERGE_DIFF3
;
154 if ((xdl_result
= xdl_merge(&ancestor_mmfile
, &our_mmfile
,
155 &their_mmfile
, &xmparam
, &mmbuffer
)) < 0) {
156 giterr_set(GITERR_MERGE
, "Failed to merge files.");
161 if ((path
= merge_file_best_path(ancestor
, ours
, theirs
)) != NULL
&&
162 (out
->path
= strdup(path
)) == NULL
) {
167 out
->automergeable
= (xdl_result
== 0);
168 out
->ptr
= (unsigned char *)mmbuffer
.ptr
;
169 out
->len
= mmbuffer
.size
;
170 out
->mode
= merge_file_best_mode(ancestor
, ours
, theirs
);
174 git_merge_file_result_free(out
);
179 static git_merge_file_input
*git_merge_file__normalize_inputs(
180 git_merge_file_input
*out
,
181 const git_merge_file_input
*given
)
183 memcpy(out
, given
, sizeof(git_merge_file_input
));
186 out
->path
= "file.txt";
195 git_merge_file_result
*out
,
196 const git_merge_file_input
*ancestor
,
197 const git_merge_file_input
*ours
,
198 const git_merge_file_input
*theirs
,
199 const git_merge_file_options
*options
)
201 git_merge_file_input inputs
[3] = { {0} };
203 assert(out
&& ours
&& theirs
);
205 memset(out
, 0x0, sizeof(git_merge_file_result
));
208 ancestor
= git_merge_file__normalize_inputs(&inputs
[0], ancestor
);
210 ours
= git_merge_file__normalize_inputs(&inputs
[1], ours
);
211 theirs
= git_merge_file__normalize_inputs(&inputs
[2], theirs
);
213 return git_merge_file__from_inputs(out
, ancestor
, ours
, theirs
, options
);
216 int git_merge_file_from_index(
217 git_merge_file_result
*out
,
218 git_repository
*repo
,
219 const git_index_entry
*ancestor
,
220 const git_index_entry
*ours
,
221 const git_index_entry
*theirs
,
222 const git_merge_file_options
*options
)
224 git_merge_file_input inputs
[3] = { {0} },
225 *ancestor_input
= NULL
, *our_input
= NULL
, *their_input
= NULL
;
227 git_odb_object
*odb_object
[3] = { 0 };
230 assert(out
&& repo
&& ours
&& theirs
);
232 memset(out
, 0x0, sizeof(git_merge_file_result
));
234 if ((error
= git_repository_odb(&odb
, repo
)) < 0)
238 if ((error
= git_merge_file__input_from_index(
239 &inputs
[0], &odb_object
[0], odb
, ancestor
)) < 0)
242 ancestor_input
= &inputs
[0];
245 if ((error
= git_merge_file__input_from_index(
246 &inputs
[1], &odb_object
[1], odb
, ours
)) < 0)
249 our_input
= &inputs
[1];
251 if ((error
= git_merge_file__input_from_index(
252 &inputs
[2], &odb_object
[2], odb
, theirs
)) < 0)
255 their_input
= &inputs
[2];
257 if ((error
= git_merge_file__from_inputs(out
,
258 ancestor_input
, our_input
, their_input
, options
)) < 0)
262 git_odb_object_free(odb_object
[0]);
263 git_odb_object_free(odb_object
[1]);
264 git_odb_object_free(odb_object
[2]);
270 void git_merge_file_result_free(git_merge_file_result
*result
)
275 git__free((char *)result
->path
);
277 /* xdiff uses malloc() not git_malloc, so we use free(), not git_free() */
278 free((char *)result
->ptr
);