forked from libgit2/libgit2
-
Notifications
You must be signed in to change notification settings - Fork 0
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
merge driver: introduce custom merge drivers
Consumers can now register custom merged drivers with `git_merge_driver_register`. This allows consumers to support the merge drivers, as configured in `.gitattributes`. Consumers will be asked to perform the file-level merge when a custom driver is configured.
- Loading branch information
Showing
6 changed files
with
1,061 additions
and
129 deletions.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,230 @@ | ||
/* | ||
* Copyright (C) the libgit2 contributors. All rights reserved. | ||
* | ||
* This file is part of libgit2, distributed under the GNU GPL v2 with | ||
* a Linking Exception. For full terms see the included COPYING file. | ||
*/ | ||
#ifndef INCLUDE_sys_git_merge_h__ | ||
#define INCLUDE_sys_git_merge_h__ | ||
|
||
/** | ||
* @file git2/sys/merge.h | ||
* @brief Git merge driver backend and plugin routines | ||
* @defgroup git_backend Git custom backend APIs | ||
* @ingroup Git | ||
* @{ | ||
*/ | ||
GIT_BEGIN_DECL | ||
|
||
typedef struct git_merge_driver git_merge_driver; | ||
|
||
/** | ||
* Look up a merge driver by name | ||
* | ||
* @param name The name of the merge driver | ||
* @return Pointer to the merge driver object or NULL if not found | ||
*/ | ||
GIT_EXTERN(git_merge_driver *) git_merge_driver_lookup(const char *name); | ||
|
||
#define GIT_MERGE_DRIVER_TEXT "text" | ||
#define GIT_MERGE_DRIVER_BINARY "binary" | ||
#define GIT_MERGE_DRIVER_UNION "union" | ||
|
||
/** | ||
* A merge driver source represents the file to be merged | ||
*/ | ||
typedef struct git_merge_driver_source git_merge_driver_source; | ||
|
||
/** Get the repository that the source data is coming from. */ | ||
GIT_EXTERN(git_repository *) git_merge_driver_source_repo( | ||
const git_merge_driver_source *src); | ||
|
||
/** Gets the ancestor of the file to merge. */ | ||
GIT_EXTERN(git_index_entry *) git_merge_driver_source_ancestor( | ||
const git_merge_driver_source *src); | ||
|
||
/** Gets the ours side of the file to merge. */ | ||
GIT_EXTERN(git_index_entry *) git_merge_driver_source_ours( | ||
const git_merge_driver_source *src); | ||
|
||
/** Gets the theirs side of the file to merge. */ | ||
GIT_EXTERN(git_index_entry *) git_merge_driver_source_theirs( | ||
const git_merge_driver_source *src); | ||
|
||
/** Gets the merge file options that the merge was invoked with */ | ||
GIT_EXTERN(git_merge_file_options *) git_merge_driver_source_file_options( | ||
const git_merge_driver_source *src); | ||
|
||
|
||
/* | ||
* struct git_merge_driver | ||
* | ||
* The merge driver lifecycle: | ||
* - initialize - first use of merge driver | ||
* - shutdown - merge driver removed/unregistered from system | ||
* - check - considering using merge driver for file | ||
* - apply - apply merge driver to the file | ||
* - cleanup - done with file | ||
*/ | ||
|
||
/** | ||
* Initialize callback on merge driver | ||
* | ||
* Specified as `driver.initialize`, this is an optional callback invoked | ||
* before a merge driver is first used. It will be called once at most. | ||
* | ||
* If non-NULL, the merge driver's `initialize` callback will be invoked | ||
* right before the first use of the driver, so you can defer expensive | ||
* initialization operations (in case libgit2 is being used in a way that | ||
* doesn't need the merge driver). | ||
*/ | ||
typedef int (*git_merge_driver_init_fn)(git_merge_driver *self); | ||
|
||
/** | ||
* Shutdown callback on merge driver | ||
* | ||
* Specified as `driver.shutdown`, this is an optional callback invoked | ||
* when the merge driver is unregistered or when libgit2 is shutting down. | ||
* It will be called once at most and should release resources as needed. | ||
* This may be called even if the `initialize` callback was not made. | ||
* | ||
* Typically this function will free the `git_merge_driver` object itself. | ||
*/ | ||
typedef void (*git_merge_driver_shutdown_fn)(git_merge_driver *self); | ||
|
||
/** | ||
* Callback to decide if a given conflict can be resolved with this merge | ||
* driver. | ||
* | ||
* Specified as `driver.check`, this is an optional callback that checks | ||
* if the given conflict can be resolved with this merge driver. | ||
* | ||
* It should return 0 if the merge driver should be applied (i.e. success), | ||
* `GIT_PASSTHROUGH` if the driver is not available, which is the equivalent | ||
* of an unregistered or nonexistent merge driver. In this case, the default | ||
* (`text`) driver will be run. This is useful if you register a wildcard | ||
* merge driver but are not interested in handling the requested file (and | ||
* should just fallback). The driver can also return `GIT_EMERGECONFLICT` | ||
* if the driver is not able to produce a merge result, and the file will | ||
* remain conflicted. Any other errors will fail and return to the caller. | ||
* | ||
* The `name` will be set to the name of the driver as configured in the | ||
* attributes. | ||
* | ||
* The `src` contains the data about the file to be merged. | ||
* | ||
* The `payload` will be a pointer to a reference payload for the driver. | ||
* This will start as NULL, but `check` can assign to this pointer for | ||
* later use by the `apply` callback. Note that the value should be heap | ||
* allocated (not stack), so that it doesn't go away before the `apply` | ||
* callback can use it. If a driver allocates and assigns a value to the | ||
* `payload`, it will need a `cleanup` callback to free the payload. | ||
*/ | ||
typedef int (*git_merge_driver_check_fn)( | ||
git_merge_driver *self, | ||
void **payload, | ||
const char *name, | ||
const git_merge_driver_source *src); | ||
|
||
/** | ||
* Callback to actually perform the merge. | ||
* | ||
* Specified as `driver.apply`, this is the callback that actually does the | ||
* merge. If it can successfully perform a merge, it should populate | ||
* `path_out` with a pointer to the filename to accept, `mode_out` with | ||
* the resultant mode, and `merged_out` with the buffer of the merged file | ||
* and then return 0. If the driver returns `GIT_PASSTHROUGH`, then the | ||
* default merge driver should instead be run. It can also return | ||
* `GIT_EMERGECONFLICT` if the driver is not able to produce a merge result, | ||
* and the file will remain conflicted. Any other errors will fail and | ||
* return to the caller. | ||
* | ||
* The `src` contains the data about the file to be merged. | ||
* | ||
* The `payload` value will refer to any payload that was set by the | ||
* `check` callback. It may be read from or written to as needed. | ||
*/ | ||
typedef int (*git_merge_driver_apply_fn)( | ||
git_merge_driver *self, | ||
void **payload, | ||
const char **path_out, | ||
uint32_t *mode_out, | ||
git_buf *merged_out, | ||
const git_merge_driver_source *src); | ||
|
||
/** | ||
* Callback to clean up after merge has been performed. | ||
* | ||
* Specified as `driver.cleanup`, this is an optional callback invoked | ||
* after the driver has been run. If the `check` or `apply` callbacks | ||
* allocated a `payload` to keep per-source merge driver state, use this | ||
* callback to free that payload and release resources as required. | ||
*/ | ||
typedef void (*git_merge_driver_cleanup_fn)( | ||
git_merge_driver *self, | ||
void *payload); | ||
|
||
/** | ||
* Merge driver structure used to register custom merge drivers. | ||
* | ||
* To associate extra data with a driver, allocate extra data and put the | ||
* `git_merge_driver` struct at the start of your data buffer, then cast | ||
* the `self` pointer to your larger structure when your callback is invoked. | ||
* | ||
* `version` should be set to GIT_MERGE_DRIVER_VERSION | ||
* | ||
* The `initialize`, `shutdown`, `check`, `apply`, and `cleanup` | ||
* callbacks are all documented above with the respective function pointer | ||
* typedefs. | ||
*/ | ||
struct git_merge_driver { | ||
unsigned int version; | ||
|
||
git_merge_driver_init_fn initialize; | ||
git_merge_driver_shutdown_fn shutdown; | ||
git_merge_driver_check_fn check; | ||
git_merge_driver_apply_fn apply; | ||
git_merge_driver_cleanup_fn cleanup; | ||
}; | ||
|
||
#define GIT_MERGE_DRIVER_VERSION 1 | ||
|
||
/** | ||
* Register a merge driver under a given name. | ||
* | ||
* As mentioned elsewhere, the initialize callback will not be invoked | ||
* immediately. It is deferred until the driver is used in some way. | ||
* | ||
* Currently the merge driver registry is not thread safe, so any | ||
* registering or deregistering of merge drivers must be done outside of | ||
* any possible usage of the drivers (i.e. during application setup or | ||
* shutdown). | ||
* | ||
* @param name The name of this driver to match an attribute. Attempting | ||
* to register with an in-use name will return GIT_EEXISTS. | ||
* @param driver The merge driver definition. This pointer will be stored | ||
* as is by libgit2 so it must be a durable allocation (either | ||
* static or on the heap). | ||
* @return 0 on successful registry, error code <0 on failure | ||
*/ | ||
GIT_EXTERN(int) git_merge_driver_register( | ||
const char *name, git_merge_driver *driver); | ||
|
||
/** | ||
* Remove the merge driver with the given name. | ||
* | ||
* Attempting to remove the builtin libgit2 merge drivers is not permitted | ||
* and will return an error. | ||
* | ||
* Currently the merge driver registry is not thread safe, so any | ||
* registering or deregistering of drivers must be done outside of any | ||
* possible usage of the drivers (i.e. during application setup or shutdown). | ||
* | ||
* @param name The name under which the merge driver was registered | ||
* @return 0 on success, error code <0 on failure | ||
*/ | ||
GIT_EXTERN(int) git_merge_driver_unregister(const char *name); | ||
|
||
/** @} */ | ||
GIT_END_DECL | ||
#endif |
Oops, something went wrong.