Commit 30a94ab7 by Edward Thomson Committed by Edward Thomson

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.
parent 7d307c1e
...@@ -17,6 +17,10 @@ v0.24 ...@@ -17,6 +17,10 @@ v0.24
### Changes or improvements ### 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 * Custom filters can now be registered with wildcard attributes, for
example `filter=*`. Consumers should examine the attributes parameter example `filter=*`. Consumers should examine the attributes parameter
of the `check` function for details. of the `check` function for details.
...@@ -83,6 +87,10 @@ v0.24 ...@@ -83,6 +87,10 @@ v0.24
### Breaking API changes ### 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, * 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 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 now prefixed with `GIT_MERGE_FLAG`, and the `tree_flags` field of the
......
...@@ -273,7 +273,16 @@ typedef struct { ...@@ -273,7 +273,16 @@ typedef struct {
*/ */
unsigned int recursion_limit; 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; git_merge_file_favor_t file_favor;
/** see `git_merge_file_flag_t` above */ /** see `git_merge_file_flag_t` above */
......
...@@ -885,6 +885,7 @@ static int merge_conflict_resolve_contents( ...@@ -885,6 +885,7 @@ static int merge_conflict_resolve_contents(
int *resolved, int *resolved,
git_merge_diff_list *diff_list, git_merge_diff_list *diff_list,
const git_merge_diff *conflict, const git_merge_diff *conflict,
const git_merge_options *merge_opts,
const git_merge_file_options *file_opts) const git_merge_file_options *file_opts)
{ {
git_merge_driver_source source = {0}; git_merge_driver_source source = {0};
...@@ -903,6 +904,7 @@ static int merge_conflict_resolve_contents( ...@@ -903,6 +904,7 @@ static int merge_conflict_resolve_contents(
return 0; return 0;
source.repo = diff_list->repo; source.repo = diff_list->repo;
source.default_driver = merge_opts->default_driver;
source.file_opts = file_opts; source.file_opts = file_opts;
source.ancestor = GIT_MERGE_INDEX_ENTRY_EXISTS(conflict->ancestor_entry) ? source.ancestor = GIT_MERGE_INDEX_ENTRY_EXISTS(conflict->ancestor_entry) ?
&conflict->ancestor_entry : NULL; &conflict->ancestor_entry : NULL;
...@@ -955,6 +957,7 @@ static int merge_conflict_resolve( ...@@ -955,6 +957,7 @@ static int merge_conflict_resolve(
int *out, int *out,
git_merge_diff_list *diff_list, git_merge_diff_list *diff_list,
const git_merge_diff *conflict, const git_merge_diff *conflict,
const git_merge_options *merge_opts,
const git_merge_file_options *file_opts) const git_merge_file_options *file_opts)
{ {
int resolved = 0; int resolved = 0;
...@@ -962,16 +965,20 @@ static int merge_conflict_resolve( ...@@ -962,16 +965,20 @@ static int merge_conflict_resolve(
*out = 0; *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; 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; 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; 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; goto done;
*out = resolved; *out = resolved;
...@@ -1687,6 +1694,7 @@ static int merge_normalize_opts( ...@@ -1687,6 +1694,7 @@ static int merge_normalize_opts(
const git_merge_options *given) const git_merge_options *given)
{ {
git_config *cfg = NULL; git_config *cfg = NULL;
git_config_entry *entry = NULL;
int error = 0; int error = 0;
assert(repo && opts); assert(repo && opts);
...@@ -1704,6 +1712,22 @@ static int merge_normalize_opts( ...@@ -1704,6 +1712,22 @@ static int merge_normalize_opts(
opts->rename_threshold = GIT_MERGE_DEFAULT_RENAME_THRESHOLD; 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) { if (!opts->target_limit) {
int limit = git_config__get_int_force(cfg, "merge.renamelimit", 0); int limit = git_config__get_int_force(cfg, "merge.renamelimit", 0);
...@@ -1726,7 +1750,9 @@ static int merge_normalize_opts( ...@@ -1726,7 +1750,9 @@ static int merge_normalize_opts(
opts->metric->payload = (void *)GIT_HASHSIG_SMART_WHITESPACE; 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( ...@@ -1938,7 +1964,7 @@ int git_merge__iterators(
int resolved = 0; int resolved = 0;
if ((error = merge_conflict_resolve( if ((error = merge_conflict_resolve(
&resolved, diff_list, conflict, &file_opts)) < 0) &resolved, diff_list, conflict, &opts, &file_opts)) < 0)
goto done; goto done;
if (!resolved) { if (!resolved) {
...@@ -1959,6 +1985,8 @@ done: ...@@ -1959,6 +1985,8 @@ done:
if (!given_opts || !given_opts->metric) if (!given_opts || !given_opts->metric)
git__free(opts.metric); git__free(opts.metric);
git__free((char *)opts.default_driver);
git_merge_diff_list__free(diff_list); git_merge_diff_list__free(diff_list);
git_iterator_free(empty_ancestor); git_iterator_free(empty_ancestor);
git_iterator_free(empty_ours); git_iterator_free(empty_ours);
......
...@@ -40,6 +40,7 @@ enum { ...@@ -40,6 +40,7 @@ enum {
struct git_merge_driver_source { struct git_merge_driver_source {
git_repository *repo; git_repository *repo;
const char *default_driver;
const git_merge_file_options *file_opts; const git_merge_file_options *file_opts;
const git_index_entry *ancestor; const git_index_entry *ancestor;
......
...@@ -307,23 +307,11 @@ git_merge_driver *git_merge_driver_lookup(const char *name) ...@@ -307,23 +307,11 @@ git_merge_driver *git_merge_driver_lookup(const char *name)
return entry->driver; 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( static int merge_driver_name_for_path(
const char **out, const char **out,
git_repository *repo, git_repository *repo,
const char *path) const char *path,
const char *default_driver)
{ {
const char *value; const char *value;
int error; int error;
...@@ -334,28 +322,37 @@ static int merge_driver_name_for_path( ...@@ -334,28 +322,37 @@ static int merge_driver_name_for_path(
return error; return error;
/* set: use the built-in 3-way merge driver ("text") */ /* 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; *out = merge_driver_name__text;
return 0;
}
/* unset: do not merge ("binary") */ /* unset: do not merge ("binary") */
if (GIT_ATTR_FALSE(value)) { else if (GIT_ATTR_FALSE(value))
*out = merge_driver_name__binary; *out = merge_driver_name__binary;
return 0;
}
if (GIT_ATTR_UNSPECIFIED(value)) { else if (GIT_ATTR_UNSPECIFIED(value) && default_driver)
/* TODO */ *out = default_driver;
/* if there's a merge.default configuration value, use it */
else if (GIT_ATTR_UNSPECIFIED(value))
*out = merge_driver_name__text; *out = merge_driver_name__text;
return 0;
} else
*out = value;
*out = value;
return 0; 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( int git_merge_driver_for_source(
git_merge_driver **driver_out, git_merge_driver **driver_out,
void **data_out, void **data_out,
...@@ -371,20 +368,22 @@ int git_merge_driver_for_source( ...@@ -371,20 +368,22 @@ int git_merge_driver_for_source(
src->ours ? src->ours->path : NULL, src->ours ? src->ours->path : NULL,
src->theirs ? src->theirs->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; 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); error = driver->check(driver, &data, driver_name, src);
if (error == GIT_PASSTHROUGH) if (error == GIT_PASSTHROUGH)
driver = &git_merge_driver__text; driver = &git_merge_driver__text;
else if (error == GIT_EMERGECONFLICT) else if (error == GIT_EMERGECONFLICT)
driver = &git_merge_driver__binary; driver = &git_merge_driver__binary;
else else
goto done; goto done;
}
error = 0; error = 0;
data = NULL; data = NULL;
......
Markdown is supported
0% or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment