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 "merge_driver.h"
9 -
10 - #include "vector.h"
11 - #include "global.h"
12 - #include "merge.h"
13 - #include "git2/merge.h"
14 - #include "git2/sys/merge.h"
15 -
16 - static const char *merge_driver_name__text = "text";
17 - static const char *merge_driver_name__union = "union";
18 - static const char *merge_driver_name__binary = "binary";
19 -
20 - struct merge_driver_registry {
21 - git_rwlock lock;
22 - git_vector drivers;
23 - };
24 -
25 - typedef struct {
26 - git_merge_driver *driver;
27 - int initialized;
28 - char name[GIT_FLEX_ARRAY];
29 - } git_merge_driver_entry;
30 -
31 - static struct merge_driver_registry merge_driver_registry;
32 -
33 - static void git_merge_driver_global_shutdown(void);
34 -
35 ##### 2 git_repository* git_merge_driver_source_repo(const git_merge_driver_source *src)
36 - {
37 ##### 2,3 assert(src);
38 ##### 4 return src->repo;
39 - }
40 -
41 ##### 2 const git_index_entry* git_merge_driver_source_ancestor(const git_merge_driver_source *src)
42 - {
43 ##### 2,3 assert(src);
44 ##### 4 return src->ancestor;
45 - }
46 -
47 ##### 2 const git_index_entry* git_merge_driver_source_ours(const git_merge_driver_source *src)
48 - {
49 ##### 2,3 assert(src);
50 ##### 4 return src->ours;
51 - }
52 -
53 ##### 2 const git_index_entry* git_merge_driver_source_theirs(const git_merge_driver_source *src)
54 - {
55 ##### 2,3 assert(src);
56 ##### 4 return src->theirs;
57 - }
58 -
59 ##### 2 const git_merge_file_options* git_merge_driver_source_file_options(const git_merge_driver_source *src)
60 - {
61 ##### 2,3 assert(src);
62 ##### 4 return src->file_opts;
63 - }
64 -
65 307 2 int git_merge_driver__builtin_apply(
66 - git_merge_driver *self,
67 - const char **path_out,
68 - uint32_t *mode_out,
69 - git_buf *merged_out,
70 - const char *filter_name,
71 - const git_merge_driver_source *src)
72 - {
73 307 2 git_merge_driver__builtin *driver = (git_merge_driver__builtin *)self;
74 307 2 git_merge_file_options file_opts = GIT_MERGE_FILE_OPTIONS_INIT;
75 307 2 git_merge_file_result result = {0};
76 - int error;
77 -
78 - GIT_UNUSED(filter_name);
79 -
80 307 2 if (src->file_opts)
81 307 3 memcpy(&file_opts, src->file_opts, sizeof(git_merge_file_options));
82 -
83 307 4 if (driver->favor)
84 14 5 file_opts.favor = driver->favor;
85 -
86 307 6,7 if ((error = git_merge_file_from_index(&result, src->repo,
87 - src->ancestor, src->ours, src->theirs, &file_opts)) < 0)
88 ##### 8 goto done;
89 -
90 307 9,10 if (!result.automergeable &&
91 166 10 !(file_opts.flags & GIT_MERGE_FILE_FAVOR__CONFLICTED)) {
92 155 11 error = GIT_EMERGECONFLICT;
93 155 11 goto done;
94 - }
95 -
96 152 12-21 *path_out = git_merge_file__best_path(
97 152 18,19 src->ancestor ? src->ancestor->path : NULL,
98 152 15,16 src->ours ? src->ours->path : NULL,
99 152 12,13 src->theirs ? src->theirs->path : NULL);
100 -
101 152 22-31 *mode_out = git_merge_file__best_mode(
102 152 28,29 src->ancestor ? src->ancestor->mode : 0,
103 152 25,26 src->ours ? src->ours->mode : 0,
104 152 22,23 src->theirs ? src->theirs->mode : 0);
105 -
106 152 32 merged_out->ptr = (char *)result.ptr;
107 152 32 merged_out->size = result.len;
108 152 32 merged_out->asize = result.len;
109 152 32 result.ptr = NULL;
110 -
111 - done:
112 307 33 git_merge_file_result_free(&result);
113 307 34 return error;
114 - }
115 -
116 3 2 static int merge_driver_binary_apply(
117 - git_merge_driver *self,
118 - const char **path_out,
119 - uint32_t *mode_out,
120 - git_buf *merged_out,
121 - const char *filter_name,
122 - const git_merge_driver_source *src)
123 - {
124 - GIT_UNUSED(self);
125 - GIT_UNUSED(path_out);
126 - GIT_UNUSED(mode_out);
127 - GIT_UNUSED(merged_out);
128 - GIT_UNUSED(filter_name);
129 - GIT_UNUSED(src);
130 -
131 3 2 return GIT_EMERGECONFLICT;
132 - }
133 -
134 105 2 static int merge_driver_entry_cmp(const void *a, const void *b)
135 - {
136 105 2 const git_merge_driver_entry *entry_a = a;
137 105 2 const git_merge_driver_entry *entry_b = b;
138 -
139 105 2 return strcmp(entry_a->name, entry_b->name);
140 - }
141 -
142 238 2 static int merge_driver_entry_search(const void *a, const void *b)
143 - {
144 238 2 const char *name_a = a;
145 238 2 const git_merge_driver_entry *entry_b = b;
146 -
147 238 2 return strcmp(name_a, entry_b->name);
148 - }
149 -
150 - git_merge_driver__builtin git_merge_driver__text = {
151 - {
152 - GIT_MERGE_DRIVER_VERSION,
153 - NULL,
154 - NULL,
155 - git_merge_driver__builtin_apply,
156 - },
157 - GIT_MERGE_FILE_FAVOR_NORMAL
158 - };
159 -
160 - git_merge_driver__builtin git_merge_driver__union = {
161 - {
162 - GIT_MERGE_DRIVER_VERSION,
163 - NULL,
164 - NULL,
165 - git_merge_driver__builtin_apply,
166 - },
167 - GIT_MERGE_FILE_FAVOR_UNION
168 - };
169 -
170 - git_merge_driver git_merge_driver__binary = {
171 - GIT_MERGE_DRIVER_VERSION,
172 - NULL,
173 - NULL,
174 - merge_driver_binary_apply
175 - };
176 -
177 - /* Note: callers must lock the registry before calling this function */
178 58 2 static int merge_driver_registry_insert(
179 - const char *name, git_merge_driver *driver)
180 - {
181 - git_merge_driver_entry *entry;
182 -
183 58 2 entry = git__calloc(1, sizeof(git_merge_driver_entry) + strlen(name) + 1);
184 58 3,4 GIT_ERROR_CHECK_ALLOC(entry);
185 -
186 58 5 strcpy(entry->name, name);
187 58 5 entry->driver = driver;
188 -
189 58 5 return git_vector_insert_sorted(
190 - &merge_driver_registry.drivers, entry, NULL);
191 - }
192 -
193 9 2 int git_merge_driver_global_init(void)
194 - {
195 - int error;
196 -
197 9 2,3 if (git_rwlock_init(&merge_driver_registry.lock) < 0)
198 ##### 4 return -1;
199 -
200 9 5,6 if ((error = git_vector_init(&merge_driver_registry.drivers, 3,
201 - merge_driver_entry_cmp)) < 0)
202 ##### 7 goto done;
203 -
204 9 8,9 if ((error = merge_driver_registry_insert(
205 9 10,11 merge_driver_name__text, &git_merge_driver__text.base)) < 0 ||
206 9 10 (error = merge_driver_registry_insert(
207 9 12,13 merge_driver_name__union, &git_merge_driver__union.base)) < 0 ||
208 9 12 (error = merge_driver_registry_insert(
209 - merge_driver_name__binary, &git_merge_driver__binary)) < 0)
210 - goto done;
211 -
212 9 14 git__on_shutdown(git_merge_driver_global_shutdown);
213 -
214 - done:
215 9 15 if (error < 0)
216 ##### 16 git_vector_free_deep(&merge_driver_registry.drivers);
217 -
218 9 17 return error;
219 - }
220 -
221 9 2 static void git_merge_driver_global_shutdown(void)
222 - {
223 - git_merge_driver_entry *entry;
224 - size_t i;
225 -
226 9 2,3 if (git_rwlock_wrlock(&merge_driver_registry.lock) < 0)
227 9 4,15 return;
228 -
229 36 5,9-11 git_vector_foreach(&merge_driver_registry.drivers, i, entry) {
230 27 6 if (entry->driver->shutdown)
231 ##### 7 entry->driver->shutdown(entry->driver);
232 -
233 27 8 git__free(entry);
234 - }
235 -
236 9 12 git_vector_free(&merge_driver_registry.drivers);
237 -
238 9 13 git_rwlock_wrunlock(&merge_driver_registry.lock);
239 9 14 git_rwlock_free(&merge_driver_registry.lock);
240 - }
241 -
242 - /* Note: callers must lock the registry before calling this function */
243 81 2 static int merge_driver_registry_find(size_t *pos, const char *name)
244 - {
245 81 2 return git_vector_search2(pos, &merge_driver_registry.drivers,
246 - merge_driver_entry_search, name);
247 - }
248 -
249 - /* Note: callers must lock the registry before calling this function */
250 50 2 static git_merge_driver_entry *merge_driver_registry_lookup(
251 - size_t *pos, const char *name)
252 - {
253 50 2 git_merge_driver_entry *entry = NULL;
254 -
255 50 2,3 if (!merge_driver_registry_find(pos, name))
256 47 4 entry = git_vector_get(&merge_driver_registry.drivers, *pos);
257 -
258 50 5 return entry;
259 - }
260 -
261 31 2 int git_merge_driver_register(const char *name, git_merge_driver *driver)
262 - {
263 - int error;
264 -
265 31 2-4 assert(name && driver);
266 -
267 31 5,6 if (git_rwlock_wrlock(&merge_driver_registry.lock) < 0) {
268 ##### 7 git_error_set(GIT_ERROR_OS, "failed to lock merge driver registry");
269 ##### 8 return -1;
270 - }
271 -
272 31 9,10 if (!merge_driver_registry_find(NULL, name)) {
273 ##### 11 git_error_set(GIT_ERROR_MERGE, "attempt to reregister existing driver '%s'",
274 - name);
275 ##### 12 error = GIT_EEXISTS;
276 ##### 12 goto done;
277 - }
278 -
279 31 13 error = merge_driver_registry_insert(name, driver);
280 -
281 - done:
282 31 14 git_rwlock_wrunlock(&merge_driver_registry.lock);
283 31 15 return error;
284 - }
285 -
286 31 2 int git_merge_driver_unregister(const char *name)
287 - {
288 - git_merge_driver_entry *entry;
289 - size_t pos;
290 31 2 int error = 0;
291 -
292 31 2,3 if (git_rwlock_wrlock(&merge_driver_registry.lock) < 0) {
293 ##### 4 git_error_set(GIT_ERROR_OS, "failed to lock merge driver registry");
294 ##### 5 return -1;
295 - }
296 -
297 31 6,7 if ((entry = merge_driver_registry_lookup(&pos, name)) == NULL) {
298 ##### 8 git_error_set(GIT_ERROR_MERGE, "cannot find merge driver '%s' to unregister",
299 - name);
300 ##### 9 error = GIT_ENOTFOUND;
301 ##### 9 goto done;
302 - }
303 -
304 31 10 git_vector_remove(&merge_driver_registry.drivers, pos);
305 -
306 31 11,12 if (entry->initialized && entry->driver->shutdown) {
307 10 13 entry->driver->shutdown(entry->driver);
308 10 14 entry->initialized = false;
309 - }
310 -
311 31 15 git__free(entry);
312 -
313 - done:
314 31 16 git_rwlock_wrunlock(&merge_driver_registry.lock);
315 31 17 return error;
316 - }
317 -
318 309 2 git_merge_driver *git_merge_driver_lookup(const char *name)
319 - {
320 - git_merge_driver_entry *entry;
321 - size_t pos;
322 - int error;
323 -
324 - /* If we've decided the merge driver to use internally - and not
325 - * based on user configuration (in merge_driver_name_for_path)
326 - * then we can use a hardcoded name to compare instead of bothering
327 - * to take a lock and look it up in the vector.
328 - */
329 309 2 if (name == merge_driver_name__text)
330 289 3 return &git_merge_driver__text.base;
331 20 4 else if (name == merge_driver_name__binary)
332 1 5 return &git_merge_driver__binary;
333 -
334 19 6,7 if (git_rwlock_rdlock(&merge_driver_registry.lock) < 0) {
335 ##### 8 git_error_set(GIT_ERROR_OS, "failed to lock merge driver registry");
336 ##### 9 return NULL;
337 - }
338 -
339 19 10 entry = merge_driver_registry_lookup(&pos, name);
340 -
341 19 11 git_rwlock_rdunlock(&merge_driver_registry.lock);
342 -
343 19 12 if (entry == NULL) {
344 3 13 git_error_set(GIT_ERROR_MERGE, "cannot use an unregistered filter");
345 3 14 return NULL;
346 - }
347 -
348 16 15 if (!entry->initialized) {
349 12 16-18 if (entry->driver->initialize &&
350 10 17 (error = entry->driver->initialize(entry->driver)) < 0)
351 ##### 19 return NULL;
352 -
353 12 20 entry->initialized = 1;
354 - }
355 -
356 16 21 return entry->driver;
357 - }
358 -
359 307 2 static int merge_driver_name_for_path(
360 - const char **out,
361 - git_repository *repo,
362 - const char *path,
363 - const char *default_driver)
364 - {
365 - const char *value;
366 - int error;
367 -
368 307 2 *out = NULL;
369 -
370 307 2,3 if ((error = git_attr_get(&value, repo, 0, path, "merge")) < 0)
371 ##### 4 return error;
372 -
373 - /* set: use the built-in 3-way merge driver ("text") */
374 307 5,6 if (GIT_ATTR_IS_TRUE(value))
375 1 7 *out = merge_driver_name__text;
376 -
377 - /* unset: do not merge ("binary") */
378 306 8,9 else if (GIT_ATTR_IS_FALSE(value))
379 1 10 *out = merge_driver_name__binary;
380 -
381 305 11-13 else if (GIT_ATTR_IS_UNSPECIFIED(value) && default_driver)
382 10 14 *out = default_driver;
383 -
384 295 15,16 else if (GIT_ATTR_IS_UNSPECIFIED(value))
385 288 17 *out = merge_driver_name__text;
386 -
387 - else
388 7 18 *out = value;
389 -
390 307 19 return 0;
391 - }
392 -
393 -
394 307 2 GIT_INLINE(git_merge_driver *) merge_driver_lookup_with_wildcard(
395 - const char *name)
396 - {
397 307 2 git_merge_driver *driver = git_merge_driver_lookup(name);
398 -
399 307 3 if (driver == NULL)
400 2 4 driver = git_merge_driver_lookup("*");
401 -
402 307 5 return driver;
403 - }
404 -
405 307 2 int git_merge_driver_for_source(
406 - const char **name_out,
407 - git_merge_driver **driver_out,
408 - const git_merge_driver_source *src)
409 - {
410 - const char *path, *driver_name;
411 307 2 int error = 0;
412 -
413 307 2-11 path = git_merge_file__best_path(
414 307 8,9 src->ancestor ? src->ancestor->path : NULL,
415 307 5,6 src->ours ? src->ours->path : NULL,
416 307 2,3 src->theirs ? src->theirs->path : NULL);
417 -
418 307 12,13 if ((error = merge_driver_name_for_path(
419 - &driver_name, src->repo, path, src->default_driver)) < 0)
420 ##### 14 return error;
421 -
422 307 15 *name_out = driver_name;
423 307 15 *driver_out = merge_driver_lookup_with_wildcard(driver_name);
424 307 16 return error;
425 - }
426 -