mirror of
https://git.proxmox.com/git/libgit2
synced 2025-05-28 00:25:29 +00:00
Introduce diff3 mode for checking out conflicts
This commit is contained in:
parent
6b92c99bcb
commit
e651e8e2b5
@ -152,6 +152,12 @@ typedef enum {
|
||||
/** Don't overwrite ignored files that exist in the checkout target */
|
||||
GIT_CHECKOUT_DONT_OVERWRITE_IGNORED = (1u << 19),
|
||||
|
||||
/** Write normal merge files for conflicts */
|
||||
GIT_CHECKOUT_CONFLICT_STYLE_MERGE = (1u << 20),
|
||||
|
||||
/** Include common ancestor data in diff3 format files for conflicts */
|
||||
GIT_CHECKOUT_CONFLICT_STYLE_DIFF3 = (1u << 21),
|
||||
|
||||
/**
|
||||
* THE FOLLOWING OPTIONS ARE NOT YET IMPLEMENTED
|
||||
*/
|
||||
@ -252,6 +258,7 @@ typedef struct git_checkout_opts {
|
||||
|
||||
const char *target_directory; /** alternative checkout path to workdir */
|
||||
|
||||
const char *ancestor_label; /** the name of the common ancestor side of conflicts */
|
||||
const char *our_label; /** the name of the "our" side of conflicts */
|
||||
const char *their_label; /** the name of the "their" side of conflicts */
|
||||
} git_checkout_opts;
|
||||
|
@ -1672,6 +1672,9 @@ static int checkout_write_merge(
|
||||
git_filebuf output = GIT_FILEBUF_INIT;
|
||||
int error = 0;
|
||||
|
||||
if (data->opts.checkout_strategy & GIT_CHECKOUT_CONFLICT_STYLE_DIFF3)
|
||||
merge_file_opts.style = GIT_MERGE_FILE_STYLE_DIFF3;
|
||||
|
||||
if ((conflict->ancestor &&
|
||||
(error = git_merge_file_input_from_index_entry(
|
||||
&ancestor, data->repo, conflict->ancestor)) < 0) ||
|
||||
@ -1681,7 +1684,7 @@ static int checkout_write_merge(
|
||||
&theirs, data->repo, conflict->theirs)) < 0)
|
||||
goto done;
|
||||
|
||||
ancestor.label = NULL;
|
||||
ancestor.label = data->opts.ancestor_label ? data->opts.ancestor_label : "ancestor";
|
||||
ours.label = data->opts.our_label ? data->opts.our_label : "ours";
|
||||
theirs.label = data->opts.their_label ? data->opts.their_label : "theirs";
|
||||
|
||||
|
24
src/merge.c
24
src/merge.c
@ -2165,6 +2165,8 @@ static int merge_normalize_opts(
|
||||
git_repository *repo,
|
||||
git_merge_opts *opts,
|
||||
const git_merge_opts *given,
|
||||
const git_merge_head *ancestor_head,
|
||||
const git_merge_head *our_head,
|
||||
size_t their_heads_len,
|
||||
const git_merge_head **their_heads)
|
||||
{
|
||||
@ -2184,8 +2186,20 @@ static int merge_normalize_opts(
|
||||
if (!opts->checkout_opts.checkout_strategy)
|
||||
opts->checkout_opts.checkout_strategy = default_checkout_strategy;
|
||||
|
||||
if (!opts->checkout_opts.our_label)
|
||||
opts->checkout_opts.our_label = "HEAD";
|
||||
/* TODO: for multiple ancestors in merge-recursive, this is "merged common ancestors" */
|
||||
if (!opts->checkout_opts.ancestor_label) {
|
||||
if (ancestor_head && ancestor_head->commit)
|
||||
opts->checkout_opts.ancestor_label = git_commit_summary(ancestor_head->commit);
|
||||
else
|
||||
opts->checkout_opts.ancestor_label = "ancestor";
|
||||
}
|
||||
|
||||
if (!opts->checkout_opts.our_label) {
|
||||
if (our_head && our_head->ref_name)
|
||||
opts->checkout_opts.our_label = our_head->ref_name;
|
||||
else
|
||||
opts->checkout_opts.our_label = "ours";
|
||||
}
|
||||
|
||||
if (!opts->checkout_opts.their_label) {
|
||||
if (their_heads_len == 1 && their_heads[0]->ref_name)
|
||||
@ -2480,9 +2494,6 @@ int git_merge(
|
||||
their_trees = git__calloc(their_heads_len, sizeof(git_tree *));
|
||||
GITERR_CHECK_ALLOC(their_trees);
|
||||
|
||||
if ((error = merge_normalize_opts(repo, &opts, given_opts, their_heads_len, their_heads)) < 0)
|
||||
goto on_error;
|
||||
|
||||
if ((error = git_repository__ensure_not_bare(repo, "merge")) < 0)
|
||||
goto on_error;
|
||||
|
||||
@ -2494,6 +2505,9 @@ int git_merge(
|
||||
error != GIT_ENOTFOUND)
|
||||
goto on_error;
|
||||
|
||||
if ((error = merge_normalize_opts(repo, &opts, given_opts, ancestor_head, our_head, their_heads_len, their_heads)) < 0)
|
||||
goto on_error;
|
||||
|
||||
if (their_heads_len == 1 &&
|
||||
ancestor_head != NULL &&
|
||||
(merge_check_uptodate(result, ancestor_head, their_heads[0]) ||
|
||||
|
@ -161,6 +161,9 @@ int git_merge_files(
|
||||
(opts && (opts->flags & GIT_MERGE_FILE_SIMPLIFY_ALNUM)) ?
|
||||
XDL_MERGE_ZEALOUS_ALNUM : XDL_MERGE_ZEALOUS;
|
||||
|
||||
if (opts && opts->style == GIT_MERGE_FILE_STYLE_DIFF3)
|
||||
xmparam.style = XDL_MERGE_DIFF3;
|
||||
|
||||
if ((xdl_result = xdl_merge(&ancestor->mmfile, &ours->mmfile,
|
||||
&theirs->mmfile, &xmparam, &mmbuffer)) < 0) {
|
||||
giterr_set(GITERR_MERGE, "Failed to merge files.");
|
||||
|
@ -39,9 +39,18 @@ typedef enum {
|
||||
GIT_MERGE_FILE_SIMPLIFY_ALNUM = (1 << 0),
|
||||
} git_merge_file_flags_t;
|
||||
|
||||
typedef enum {
|
||||
/* Create standard conflicted merge files */
|
||||
GIT_MERGE_FILE_STYLE_MERGE = 0,
|
||||
|
||||
/* Create diff3-style files */
|
||||
GIT_MERGE_FILE_STYLE_DIFF3 = 1,
|
||||
} git_merge_file_style_t;
|
||||
|
||||
typedef struct {
|
||||
git_merge_file_favor_t favor;
|
||||
git_merge_file_flags_t flags;
|
||||
git_merge_file_style_t style;
|
||||
} git_merge_file_options;
|
||||
|
||||
#define GIT_MERGE_FILE_OPTIONS_INIT {0}
|
||||
|
@ -93,9 +93,18 @@ static git_index *repo_index;
|
||||
"this file is automergeable\r\n" \
|
||||
"this file is changed in branch\r\n"
|
||||
|
||||
#define CONFLICTING_MERGE_FILE \
|
||||
"<<<<<<< HEAD\n" \
|
||||
"this file is changed in master and branch\n" \
|
||||
"=======\n" \
|
||||
"this file is changed in branch and master\n" \
|
||||
">>>>>>> 7cb63eed597130ba4abb87b3e544b85021905520\n"
|
||||
|
||||
#define CONFLICTING_DIFF3_FILE \
|
||||
"<<<<<<< HEAD\n" \
|
||||
"this file is changed in master and branch\n" \
|
||||
"||||||| initial\n" \
|
||||
"this file is a conflict\n" \
|
||||
"=======\n" \
|
||||
"this file is changed in branch and master\n" \
|
||||
">>>>>>> 7cb63eed597130ba4abb87b3e544b85021905520\n"
|
||||
@ -244,7 +253,7 @@ void test_merge_workdir_simple__automerge_crlf(void)
|
||||
#endif /* GIT_WIN32 */
|
||||
}
|
||||
|
||||
void test_merge_workdir_simple__diff3(void)
|
||||
void test_merge_workdir_simple__mergefile(void)
|
||||
{
|
||||
git_merge_result *result;
|
||||
git_buf conflicting_buf = GIT_BUF_INIT;
|
||||
@ -271,6 +280,44 @@ void test_merge_workdir_simple__diff3(void)
|
||||
cl_assert(result = merge_simple_branch(0, 0));
|
||||
cl_assert(!git_merge_result_is_fastforward(result));
|
||||
|
||||
cl_git_pass(git_futils_readbuffer(&conflicting_buf,
|
||||
TEST_REPO_PATH "/conflicting.txt"));
|
||||
cl_assert(strcmp(conflicting_buf.ptr, CONFLICTING_MERGE_FILE) == 0);
|
||||
git_buf_free(&conflicting_buf);
|
||||
|
||||
cl_assert(merge_test_index(repo_index, merge_index_entries, 8));
|
||||
cl_assert(merge_test_reuc(repo_index, merge_reuc_entries, 3));
|
||||
|
||||
git_merge_result_free(result);
|
||||
}
|
||||
|
||||
void test_merge_workdir_simple__diff3(void)
|
||||
{
|
||||
git_merge_result *result;
|
||||
git_buf conflicting_buf = GIT_BUF_INIT;
|
||||
|
||||
struct merge_index_entry merge_index_entries[] = {
|
||||
ADDED_IN_MASTER_INDEX_ENTRY,
|
||||
AUTOMERGEABLE_INDEX_ENTRY,
|
||||
CHANGED_IN_BRANCH_INDEX_ENTRY,
|
||||
CHANGED_IN_MASTER_INDEX_ENTRY,
|
||||
|
||||
{ 0100644, "d427e0b2e138501a3d15cc376077a3631e15bd46", 1, "conflicting.txt" },
|
||||
{ 0100644, "4e886e602529caa9ab11d71f86634bd1b6e0de10", 2, "conflicting.txt" },
|
||||
{ 0100644, "2bd0a343aeef7a2cf0d158478966a6e587ff3863", 3, "conflicting.txt" },
|
||||
|
||||
UNCHANGED_INDEX_ENTRY,
|
||||
};
|
||||
|
||||
struct merge_reuc_entry merge_reuc_entries[] = {
|
||||
AUTOMERGEABLE_REUC_ENTRY,
|
||||
REMOVED_IN_BRANCH_REUC_ENTRY,
|
||||
REMOVED_IN_MASTER_REUC_ENTRY
|
||||
};
|
||||
|
||||
cl_assert(result = merge_simple_branch(0, GIT_CHECKOUT_CONFLICT_STYLE_DIFF3));
|
||||
cl_assert(!git_merge_result_is_fastforward(result));
|
||||
|
||||
cl_git_pass(git_futils_readbuffer(&conflicting_buf,
|
||||
TEST_REPO_PATH "/conflicting.txt"));
|
||||
cl_assert(strcmp(conflicting_buf.ptr, CONFLICTING_DIFF3_FILE) == 0);
|
||||
|
Loading…
Reference in New Issue
Block a user