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