File Coverage

deps/libgit2/src/libgit2/merge_driver.c
Criterion Covered Total %
statement 76 175 43.4
branch 32 106 30.1
condition n/a
subroutine n/a
pod n/a
total 108 281 38.4


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