mirror of
https://git.proxmox.com/git/libgit2
synced 2025-05-03 17:57:25 +00:00
merge driver: allow custom default driver
Allow merge users to configure a custom default merge driver via `git_merge_options`. Similarly, honor the `merge.default` configuration option.
This commit is contained in:
parent
7d307c1edc
commit
30a94ab756
@ -17,6 +17,10 @@ v0.24
|
||||
|
||||
### Changes or improvements
|
||||
|
||||
* Custom merge drivers can now be registered, which allows callers to
|
||||
configure callbacks to honor `merge=driver` configuration in
|
||||
`.gitattributes`.
|
||||
|
||||
* Custom filters can now be registered with wildcard attributes, for
|
||||
example `filter=*`. Consumers should examine the attributes parameter
|
||||
of the `check` function for details.
|
||||
@ -83,6 +87,10 @@ v0.24
|
||||
|
||||
### Breaking API changes
|
||||
|
||||
* `git_merge_options` now provides a `default_driver` that can be used
|
||||
to provide the name of a merge driver to be used to handle files changed
|
||||
during a merge.
|
||||
|
||||
* The `git_merge_tree_flag_t` is now `git_merge_flag_t`. Subsequently,
|
||||
its members are no longer prefixed with `GIT_MERGE_TREE_FLAG` but are
|
||||
now prefixed with `GIT_MERGE_FLAG`, and the `tree_flags` field of the
|
||||
|
@ -273,7 +273,16 @@ typedef struct {
|
||||
*/
|
||||
unsigned int recursion_limit;
|
||||
|
||||
/** Flags for handling conflicting content. */
|
||||
/**
|
||||
* Default merge driver to be used when both sides of a merge have
|
||||
* changed. The default is the `text` driver.
|
||||
*/
|
||||
const char *default_driver;
|
||||
|
||||
/**
|
||||
* Flags for handling conflicting content, to be used with the standard
|
||||
* (`text`) merge driver.
|
||||
*/
|
||||
git_merge_file_favor_t file_favor;
|
||||
|
||||
/** see `git_merge_file_flag_t` above */
|
||||
|
40
src/merge.c
40
src/merge.c
@ -885,6 +885,7 @@ static int merge_conflict_resolve_contents(
|
||||
int *resolved,
|
||||
git_merge_diff_list *diff_list,
|
||||
const git_merge_diff *conflict,
|
||||
const git_merge_options *merge_opts,
|
||||
const git_merge_file_options *file_opts)
|
||||
{
|
||||
git_merge_driver_source source = {0};
|
||||
@ -903,6 +904,7 @@ static int merge_conflict_resolve_contents(
|
||||
return 0;
|
||||
|
||||
source.repo = diff_list->repo;
|
||||
source.default_driver = merge_opts->default_driver;
|
||||
source.file_opts = file_opts;
|
||||
source.ancestor = GIT_MERGE_INDEX_ENTRY_EXISTS(conflict->ancestor_entry) ?
|
||||
&conflict->ancestor_entry : NULL;
|
||||
@ -955,6 +957,7 @@ static int merge_conflict_resolve(
|
||||
int *out,
|
||||
git_merge_diff_list *diff_list,
|
||||
const git_merge_diff *conflict,
|
||||
const git_merge_options *merge_opts,
|
||||
const git_merge_file_options *file_opts)
|
||||
{
|
||||
int resolved = 0;
|
||||
@ -962,16 +965,20 @@ static int merge_conflict_resolve(
|
||||
|
||||
*out = 0;
|
||||
|
||||
if ((error = merge_conflict_resolve_trivial(&resolved, diff_list, conflict)) < 0)
|
||||
if ((error = merge_conflict_resolve_trivial(
|
||||
&resolved, diff_list, conflict)) < 0)
|
||||
goto done;
|
||||
|
||||
if (!resolved && (error = merge_conflict_resolve_one_removed(&resolved, diff_list, conflict)) < 0)
|
||||
if (!resolved && (error = merge_conflict_resolve_one_removed(
|
||||
&resolved, diff_list, conflict)) < 0)
|
||||
goto done;
|
||||
|
||||
if (!resolved && (error = merge_conflict_resolve_one_renamed(&resolved, diff_list, conflict)) < 0)
|
||||
if (!resolved && (error = merge_conflict_resolve_one_renamed(
|
||||
&resolved, diff_list, conflict)) < 0)
|
||||
goto done;
|
||||
|
||||
if (!resolved && (error = merge_conflict_resolve_contents(&resolved, diff_list, conflict, file_opts)) < 0)
|
||||
if (!resolved && (error = merge_conflict_resolve_contents(
|
||||
&resolved, diff_list, conflict, merge_opts, file_opts)) < 0)
|
||||
goto done;
|
||||
|
||||
*out = resolved;
|
||||
@ -1687,6 +1694,7 @@ static int merge_normalize_opts(
|
||||
const git_merge_options *given)
|
||||
{
|
||||
git_config *cfg = NULL;
|
||||
git_config_entry *entry = NULL;
|
||||
int error = 0;
|
||||
|
||||
assert(repo && opts);
|
||||
@ -1704,6 +1712,22 @@ static int merge_normalize_opts(
|
||||
opts->rename_threshold = GIT_MERGE_DEFAULT_RENAME_THRESHOLD;
|
||||
}
|
||||
|
||||
if (given && given->default_driver) {
|
||||
opts->default_driver = git__strdup(given->default_driver);
|
||||
GITERR_CHECK_ALLOC(opts->default_driver);
|
||||
} else {
|
||||
error = git_config_get_entry(&entry, cfg, "merge.default");
|
||||
|
||||
if (error == 0) {
|
||||
opts->default_driver = git__strdup(entry->value);
|
||||
GITERR_CHECK_ALLOC(opts->default_driver);
|
||||
} else if (error == GIT_ENOTFOUND) {
|
||||
error = 0;
|
||||
} else {
|
||||
goto done;
|
||||
}
|
||||
}
|
||||
|
||||
if (!opts->target_limit) {
|
||||
int limit = git_config__get_int_force(cfg, "merge.renamelimit", 0);
|
||||
|
||||
@ -1726,7 +1750,9 @@ static int merge_normalize_opts(
|
||||
opts->metric->payload = (void *)GIT_HASHSIG_SMART_WHITESPACE;
|
||||
}
|
||||
|
||||
return 0;
|
||||
done:
|
||||
git_config_entry_free(entry);
|
||||
return error;
|
||||
}
|
||||
|
||||
|
||||
@ -1938,7 +1964,7 @@ int git_merge__iterators(
|
||||
int resolved = 0;
|
||||
|
||||
if ((error = merge_conflict_resolve(
|
||||
&resolved, diff_list, conflict, &file_opts)) < 0)
|
||||
&resolved, diff_list, conflict, &opts, &file_opts)) < 0)
|
||||
goto done;
|
||||
|
||||
if (!resolved) {
|
||||
@ -1959,6 +1985,8 @@ done:
|
||||
if (!given_opts || !given_opts->metric)
|
||||
git__free(opts.metric);
|
||||
|
||||
git__free((char *)opts.default_driver);
|
||||
|
||||
git_merge_diff_list__free(diff_list);
|
||||
git_iterator_free(empty_ancestor);
|
||||
git_iterator_free(empty_ours);
|
||||
|
@ -40,6 +40,7 @@ enum {
|
||||
|
||||
struct git_merge_driver_source {
|
||||
git_repository *repo;
|
||||
const char *default_driver;
|
||||
const git_merge_file_options *file_opts;
|
||||
|
||||
const git_index_entry *ancestor;
|
||||
|
@ -307,23 +307,11 @@ git_merge_driver *git_merge_driver_lookup(const char *name)
|
||||
return entry->driver;
|
||||
}
|
||||
|
||||
static git_merge_driver *merge_driver_lookup_with_default(const char *name)
|
||||
{
|
||||
git_merge_driver *driver = git_merge_driver_lookup(name);
|
||||
|
||||
if (driver == NULL)
|
||||
driver = git_merge_driver_lookup("*");
|
||||
|
||||
if (driver == NULL)
|
||||
driver = &git_merge_driver__text;
|
||||
|
||||
return driver;
|
||||
}
|
||||
|
||||
static int merge_driver_name_for_path(
|
||||
const char **out,
|
||||
git_repository *repo,
|
||||
const char *path)
|
||||
const char *path,
|
||||
const char *default_driver)
|
||||
{
|
||||
const char *value;
|
||||
int error;
|
||||
@ -334,28 +322,37 @@ static int merge_driver_name_for_path(
|
||||
return error;
|
||||
|
||||
/* set: use the built-in 3-way merge driver ("text") */
|
||||
if (GIT_ATTR_TRUE(value)) {
|
||||
if (GIT_ATTR_TRUE(value))
|
||||
*out = merge_driver_name__text;
|
||||
return 0;
|
||||
}
|
||||
|
||||
/* unset: do not merge ("binary") */
|
||||
if (GIT_ATTR_FALSE(value)) {
|
||||
else if (GIT_ATTR_FALSE(value))
|
||||
*out = merge_driver_name__binary;
|
||||
return 0;
|
||||
}
|
||||
|
||||
if (GIT_ATTR_UNSPECIFIED(value)) {
|
||||
/* TODO */
|
||||
/* if there's a merge.default configuration value, use it */
|
||||
else if (GIT_ATTR_UNSPECIFIED(value) && default_driver)
|
||||
*out = default_driver;
|
||||
|
||||
else if (GIT_ATTR_UNSPECIFIED(value))
|
||||
*out = merge_driver_name__text;
|
||||
return 0;
|
||||
}
|
||||
|
||||
*out = value;
|
||||
|
||||
else
|
||||
*out = value;
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
|
||||
GIT_INLINE(git_merge_driver *) merge_driver_lookup_with_wildcard(
|
||||
const char *name)
|
||||
{
|
||||
git_merge_driver *driver = git_merge_driver_lookup(name);
|
||||
|
||||
if (driver == NULL)
|
||||
driver = git_merge_driver_lookup("*");
|
||||
|
||||
return driver;
|
||||
}
|
||||
|
||||
int git_merge_driver_for_source(
|
||||
git_merge_driver **driver_out,
|
||||
void **data_out,
|
||||
@ -371,20 +368,22 @@ int git_merge_driver_for_source(
|
||||
src->ours ? src->ours->path : NULL,
|
||||
src->theirs ? src->theirs->path : NULL);
|
||||
|
||||
if ((error = merge_driver_name_for_path(&driver_name, src->repo, path)) < 0)
|
||||
if ((error = merge_driver_name_for_path(
|
||||
&driver_name, src->repo, path, src->default_driver)) < 0)
|
||||
return error;
|
||||
|
||||
driver = merge_driver_lookup_with_default(driver_name);
|
||||
driver = merge_driver_lookup_with_wildcard(driver_name);
|
||||
|
||||
if (driver->check)
|
||||
if (driver && driver->check) {
|
||||
error = driver->check(driver, &data, driver_name, src);
|
||||
|
||||
if (error == GIT_PASSTHROUGH)
|
||||
driver = &git_merge_driver__text;
|
||||
else if (error == GIT_EMERGECONFLICT)
|
||||
driver = &git_merge_driver__binary;
|
||||
else
|
||||
goto done;
|
||||
if (error == GIT_PASSTHROUGH)
|
||||
driver = &git_merge_driver__text;
|
||||
else if (error == GIT_EMERGECONFLICT)
|
||||
driver = &git_merge_driver__binary;
|
||||
else
|
||||
goto done;
|
||||
}
|
||||
|
||||
error = 0;
|
||||
data = NULL;
|
||||
|
Loading…
Reference in New Issue
Block a user