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.
8 #include "merge_driver.h"
13 #include "git2/merge.h"
14 #include "git2/sys/merge.h"
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";
20 struct merge_driver_registry
{
26 git_merge_driver
*driver
;
28 char name
[GIT_FLEX_ARRAY
];
29 } git_merge_driver_entry
;
31 static struct merge_driver_registry merge_driver_registry
;
33 static void git_merge_driver_global_shutdown(void);
35 git_repository
* git_merge_driver_source_repo(const git_merge_driver_source
*src
)
41 const git_index_entry
* git_merge_driver_source_ancestor(const git_merge_driver_source
*src
)
47 const git_index_entry
* git_merge_driver_source_ours(const git_merge_driver_source
*src
)
53 const git_index_entry
* git_merge_driver_source_theirs(const git_merge_driver_source
*src
)
59 const git_merge_file_options
* git_merge_driver_source_file_options(const git_merge_driver_source
*src
)
62 return src
->file_opts
;
65 int git_merge_driver__builtin_apply(
66 git_merge_driver
*self
,
67 const char **path_out
,
70 const char *filter_name
,
71 const git_merge_driver_source
*src
)
73 git_merge_driver__builtin
*driver
= (git_merge_driver__builtin
*)self
;
74 git_merge_file_options file_opts
= GIT_MERGE_FILE_OPTIONS_INIT
;
75 git_merge_file_result result
= {0};
78 GIT_UNUSED(filter_name
);
81 memcpy(&file_opts
, src
->file_opts
, sizeof(git_merge_file_options
));
84 file_opts
.favor
= driver
->favor
;
86 if ((error
= git_merge_file_from_index(&result
, src
->repo
,
87 src
->ancestor
, src
->ours
, src
->theirs
, &file_opts
)) < 0)
90 if (!result
.automergeable
&&
91 !(file_opts
.flags
& GIT_MERGE_FILE_FAVOR__CONFLICTED
)) {
92 error
= GIT_EMERGECONFLICT
;
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
);
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);
106 merged_out
->ptr
= (char *)result
.ptr
;
107 merged_out
->size
= result
.len
;
108 merged_out
->asize
= result
.len
;
112 git_merge_file_result_free(&result
);
116 static int merge_driver_binary_apply(
117 git_merge_driver
*self
,
118 const char **path_out
,
121 const char *filter_name
,
122 const git_merge_driver_source
*src
)
125 GIT_UNUSED(path_out
);
126 GIT_UNUSED(mode_out
);
127 GIT_UNUSED(merged_out
);
128 GIT_UNUSED(filter_name
);
131 return GIT_EMERGECONFLICT
;
134 static int merge_driver_entry_cmp(const void *a
, const void *b
)
136 const git_merge_driver_entry
*entry_a
= a
;
137 const git_merge_driver_entry
*entry_b
= b
;
139 return strcmp(entry_a
->name
, entry_b
->name
);
142 static int merge_driver_entry_search(const void *a
, const void *b
)
144 const char *name_a
= a
;
145 const git_merge_driver_entry
*entry_b
= b
;
147 return strcmp(name_a
, entry_b
->name
);
150 git_merge_driver__builtin git_merge_driver__text
= {
152 GIT_MERGE_DRIVER_VERSION
,
155 git_merge_driver__builtin_apply
,
157 GIT_MERGE_FILE_FAVOR_NORMAL
160 git_merge_driver__builtin git_merge_driver__union
= {
162 GIT_MERGE_DRIVER_VERSION
,
165 git_merge_driver__builtin_apply
,
167 GIT_MERGE_FILE_FAVOR_UNION
170 git_merge_driver git_merge_driver__binary
= {
171 GIT_MERGE_DRIVER_VERSION
,
174 merge_driver_binary_apply
177 /* Note: callers must lock the registry before calling this function */
178 static int merge_driver_registry_insert(
179 const char *name
, git_merge_driver
*driver
)
181 git_merge_driver_entry
*entry
;
183 entry
= git__calloc(1, sizeof(git_merge_driver_entry
) + strlen(name
) + 1);
184 GIT_ERROR_CHECK_ALLOC(entry
);
186 strcpy(entry
->name
, name
);
187 entry
->driver
= driver
;
189 return git_vector_insert_sorted(
190 &merge_driver_registry
.drivers
, entry
, NULL
);
193 int git_merge_driver_global_init(void)
197 if (git_rwlock_init(&merge_driver_registry
.lock
) < 0)
200 if ((error
= git_vector_init(&merge_driver_registry
.drivers
, 3,
201 merge_driver_entry_cmp
)) < 0)
204 if ((error
= merge_driver_registry_insert(
205 merge_driver_name__text
, &git_merge_driver__text
.base
)) < 0 ||
206 (error
= merge_driver_registry_insert(
207 merge_driver_name__union
, &git_merge_driver__union
.base
)) < 0 ||
208 (error
= merge_driver_registry_insert(
209 merge_driver_name__binary
, &git_merge_driver__binary
)) < 0)
212 git__on_shutdown(git_merge_driver_global_shutdown
);
216 git_vector_free_deep(&merge_driver_registry
.drivers
);
221 static void git_merge_driver_global_shutdown(void)
223 git_merge_driver_entry
*entry
;
226 if (git_rwlock_wrlock(&merge_driver_registry
.lock
) < 0)
229 git_vector_foreach(&merge_driver_registry
.drivers
, i
, entry
) {
230 if (entry
->driver
->shutdown
)
231 entry
->driver
->shutdown(entry
->driver
);
236 git_vector_free(&merge_driver_registry
.drivers
);
238 git_rwlock_wrunlock(&merge_driver_registry
.lock
);
239 git_rwlock_free(&merge_driver_registry
.lock
);
242 /* Note: callers must lock the registry before calling this function */
243 static int merge_driver_registry_find(size_t *pos
, const char *name
)
245 return git_vector_search2(pos
, &merge_driver_registry
.drivers
,
246 merge_driver_entry_search
, name
);
249 /* Note: callers must lock the registry before calling this function */
250 static git_merge_driver_entry
*merge_driver_registry_lookup(
251 size_t *pos
, const char *name
)
253 git_merge_driver_entry
*entry
= NULL
;
255 if (!merge_driver_registry_find(pos
, name
))
256 entry
= git_vector_get(&merge_driver_registry
.drivers
, *pos
);
261 int git_merge_driver_register(const char *name
, git_merge_driver
*driver
)
265 assert(name
&& driver
);
267 if (git_rwlock_wrlock(&merge_driver_registry
.lock
) < 0) {
268 git_error_set(GIT_ERROR_OS
, "failed to lock merge driver registry");
272 if (!merge_driver_registry_find(NULL
, name
)) {
273 git_error_set(GIT_ERROR_MERGE
, "attempt to reregister existing driver '%s'",
279 error
= merge_driver_registry_insert(name
, driver
);
282 git_rwlock_wrunlock(&merge_driver_registry
.lock
);
286 int git_merge_driver_unregister(const char *name
)
288 git_merge_driver_entry
*entry
;
292 if (git_rwlock_wrlock(&merge_driver_registry
.lock
) < 0) {
293 git_error_set(GIT_ERROR_OS
, "failed to lock merge driver registry");
297 if ((entry
= merge_driver_registry_lookup(&pos
, name
)) == NULL
) {
298 git_error_set(GIT_ERROR_MERGE
, "cannot find merge driver '%s' to unregister",
300 error
= GIT_ENOTFOUND
;
304 git_vector_remove(&merge_driver_registry
.drivers
, pos
);
306 if (entry
->initialized
&& entry
->driver
->shutdown
) {
307 entry
->driver
->shutdown(entry
->driver
);
308 entry
->initialized
= false;
314 git_rwlock_wrunlock(&merge_driver_registry
.lock
);
318 git_merge_driver
*git_merge_driver_lookup(const char *name
)
320 git_merge_driver_entry
*entry
;
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.
329 if (name
== merge_driver_name__text
)
330 return &git_merge_driver__text
.base
;
331 else if (name
== merge_driver_name__binary
)
332 return &git_merge_driver__binary
;
334 if (git_rwlock_rdlock(&merge_driver_registry
.lock
) < 0) {
335 git_error_set(GIT_ERROR_OS
, "failed to lock merge driver registry");
339 entry
= merge_driver_registry_lookup(&pos
, name
);
341 git_rwlock_rdunlock(&merge_driver_registry
.lock
);
344 git_error_set(GIT_ERROR_MERGE
, "cannot use an unregistered filter");
348 if (!entry
->initialized
) {
349 if (entry
->driver
->initialize
&&
350 (error
= entry
->driver
->initialize(entry
->driver
)) < 0)
353 entry
->initialized
= 1;
356 return entry
->driver
;
359 static int merge_driver_name_for_path(
361 git_repository
*repo
,
363 const char *default_driver
)
370 if ((error
= git_attr_get(&value
, repo
, 0, path
, "merge")) < 0)
373 /* set: use the built-in 3-way merge driver ("text") */
374 if (GIT_ATTR_IS_TRUE(value
))
375 *out
= merge_driver_name__text
;
377 /* unset: do not merge ("binary") */
378 else if (GIT_ATTR_IS_FALSE(value
))
379 *out
= merge_driver_name__binary
;
381 else if (GIT_ATTR_IS_UNSPECIFIED(value
) && default_driver
)
382 *out
= default_driver
;
384 else if (GIT_ATTR_IS_UNSPECIFIED(value
))
385 *out
= merge_driver_name__text
;
394 GIT_INLINE(git_merge_driver
*) merge_driver_lookup_with_wildcard(
397 git_merge_driver
*driver
= git_merge_driver_lookup(name
);
400 driver
= git_merge_driver_lookup("*");
405 int git_merge_driver_for_source(
406 const char **name_out
,
407 git_merge_driver
**driver_out
,
408 const git_merge_driver_source
*src
)
410 const char *path
, *driver_name
;
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
);
418 if ((error
= merge_driver_name_for_path(
419 &driver_name
, src
->repo
, path
, src
->default_driver
)) < 0)
422 *name_out
= driver_name
;
423 *driver_out
= merge_driver_lookup_with_wildcard(driver_name
);