Commit a27f31d8 by Carlos Martín Nieto

Merge pull request #3513 from ethomson/merge_recursive

Recursive Merge
parents e0ab1ca0 5b9c63c3
...@@ -34,6 +34,14 @@ v0.23 + 1 ...@@ -34,6 +34,14 @@ v0.23 + 1
### Breaking API changes ### Breaking API changes
* 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
`git_merge_options` structure is now named `flags`.
* The `git_merge_file_flags_t` enum is now `git_merge_file_flag_t` for
consistency with other enum type names.
* `git_cert` descendent types now have a proper `parent` member * `git_cert` descendent types now have a proper `parent` member
* It is the responsibility of the refdb backend to decide what to do * It is the responsibility of the refdb backend to decide what to do
......
...@@ -62,8 +62,8 @@ GIT_EXTERN(int) git_merge_file_init_input( ...@@ -62,8 +62,8 @@ GIT_EXTERN(int) git_merge_file_init_input(
unsigned int version); unsigned int version);
/** /**
* Flags for `git_merge_tree` options. A combination of these flags can be * Flags for `git_merge` options. A combination of these flags can be
* passed in via the `tree_flags` value in the `git_merge_options`. * passed in via the `flags` value in the `git_merge_options`.
*/ */
typedef enum { typedef enum {
/** /**
...@@ -71,20 +71,28 @@ typedef enum { ...@@ -71,20 +71,28 @@ typedef enum {
* side or the common ancestor and the "theirs" side. This will enable * side or the common ancestor and the "theirs" side. This will enable
* the ability to merge between a modified and renamed file. * the ability to merge between a modified and renamed file.
*/ */
GIT_MERGE_TREE_FIND_RENAMES = (1 << 0), GIT_MERGE_FIND_RENAMES = (1 << 0),
/** /**
* If a conflict occurs, exit immediately instead of attempting to * If a conflict occurs, exit immediately instead of attempting to
* continue resolving conflicts. The merge operation will fail with * continue resolving conflicts. The merge operation will fail with
* GIT_EMERGECONFLICT and no index will be returned. * GIT_EMERGECONFLICT and no index will be returned.
*/ */
GIT_MERGE_TREE_FAIL_ON_CONFLICT = (1 << 1), GIT_MERGE_FAIL_ON_CONFLICT = (1 << 1),
/** /**
* Do not write the REUC extension on the generated index * Do not write the REUC extension on the generated index
*/ */
GIT_MERGE_TREE_SKIP_REUC = (1 << 2), GIT_MERGE_SKIP_REUC = (1 << 2),
} git_merge_tree_flag_t;
/**
* If the commits being merged have multiple merge bases, do not build
* a recursive merge base (by merging the multiple merge bases),
* instead simply use the first base. This flag provides a similar
* merge base to `git-merge-resolve`.
*/
GIT_MERGE_NO_RECURSIVE = (1 << 3),
} git_merge_flag_t;
/** /**
* Merge file favor options for `git_merge_options` instruct the file-level * Merge file favor options for `git_merge_options` instruct the file-level
...@@ -152,7 +160,7 @@ typedef enum { ...@@ -152,7 +160,7 @@ typedef enum {
/** Take extra time to find minimal diff */ /** Take extra time to find minimal diff */
GIT_MERGE_FILE_DIFF_MINIMAL = (1 << 7), GIT_MERGE_FILE_DIFF_MINIMAL = (1 << 7),
} git_merge_file_flags_t; } git_merge_file_flag_t;
/** /**
* Options for merging a file * Options for merging a file
...@@ -181,8 +189,8 @@ typedef struct { ...@@ -181,8 +189,8 @@ typedef struct {
/** The file to favor in region conflicts. */ /** The file to favor in region conflicts. */
git_merge_file_favor_t favor; git_merge_file_favor_t favor;
/** see `git_merge_file_flags_t` above */ /** see `git_merge_file_flag_t` above */
unsigned int flags; git_merge_file_flag_t flags;
} git_merge_file_options; } git_merge_file_options;
#define GIT_MERGE_FILE_OPTIONS_VERSION 1 #define GIT_MERGE_FILE_OPTIONS_VERSION 1
...@@ -232,11 +240,13 @@ typedef struct { ...@@ -232,11 +240,13 @@ typedef struct {
*/ */
typedef struct { typedef struct {
unsigned int version; unsigned int version;
git_merge_tree_flag_t tree_flags;
/** See `git_merge_flag_t` above */
git_merge_flag_t flags;
/** /**
* Similarity to consider a file renamed (default 50). If * Similarity to consider a file renamed (default 50). If
* `GIT_MERGE_TREE_FIND_RENAMES` is enabled, added files will be compared * `GIT_MERGE_FIND_RENAMES` is enabled, added files will be compared
* with deleted files to determine their similarity. Files that are * with deleted files to determine their similarity. Files that are
* more similar than the rename threshold (percentage-wise) will be * more similar than the rename threshold (percentage-wise) will be
* treated as a rename. * treated as a rename.
...@@ -255,11 +265,19 @@ typedef struct { ...@@ -255,11 +265,19 @@ typedef struct {
/** Pluggable similarity metric; pass NULL to use internal metric */ /** Pluggable similarity metric; pass NULL to use internal metric */
git_diff_similarity_metric *metric; git_diff_similarity_metric *metric;
/**
* Maximum number of times to merge common ancestors to build a
* virtual merge base when faced with criss-cross merges. When this
* limit is reached, the next ancestor will simply be used instead of
* attempting to merge it. The default is unlimited.
*/
unsigned int recursion_limit;
/** Flags for handling conflicting content. */ /** Flags for handling conflicting content. */
git_merge_file_favor_t file_favor; git_merge_file_favor_t file_favor;
/** see `git_merge_file_flags_t` above */ /** see `git_merge_file_flag_t` above */
unsigned int file_flags; git_merge_file_flag_t file_flags;
} git_merge_options; } git_merge_options;
#define GIT_MERGE_OPTIONS_VERSION 1 #define GIT_MERGE_OPTIONS_VERSION 1
......
...@@ -7,12 +7,16 @@ ...@@ -7,12 +7,16 @@
#include "common.h" #include "common.h"
#include "annotated_commit.h" #include "annotated_commit.h"
#include "refs.h"
#include "cache.h"
#include "git2/commit.h" #include "git2/commit.h"
#include "git2/refs.h" #include "git2/refs.h"
#include "git2/repository.h" #include "git2/repository.h"
#include "git2/annotated_commit.h" #include "git2/annotated_commit.h"
#include "git2/revparse.h" #include "git2/revparse.h"
#include "git2/tree.h"
#include "git2/index.h"
static int annotated_commit_init( static int annotated_commit_init(
git_annotated_commit **out, git_annotated_commit **out,
...@@ -22,14 +26,17 @@ static int annotated_commit_init( ...@@ -22,14 +26,17 @@ static int annotated_commit_init(
const char *remote_url) const char *remote_url)
{ {
git_annotated_commit *annotated_commit; git_annotated_commit *annotated_commit;
git_commit *commit = NULL;
int error = 0; int error = 0;
assert(out && id); assert(out && id);
*out = NULL; *out = NULL;
annotated_commit = git__calloc(1, sizeof(git_annotated_commit)); if ((error = git_commit_lookup(&commit, repo, id)) < 0 ||
GITERR_CHECK_ALLOC(annotated_commit); (error = git_annotated_commit_from_commit(&annotated_commit,
commit)) < 0)
goto done;
if (ref_name) { if (ref_name) {
annotated_commit->ref_name = git__strdup(ref_name); annotated_commit->ref_name = git__strdup(ref_name);
...@@ -41,15 +48,10 @@ static int annotated_commit_init( ...@@ -41,15 +48,10 @@ static int annotated_commit_init(
GITERR_CHECK_ALLOC(annotated_commit->remote_url); GITERR_CHECK_ALLOC(annotated_commit->remote_url);
} }
git_oid_fmt(annotated_commit->id_str, id);
annotated_commit->id_str[GIT_OID_HEXSZ] = '\0';
if ((error = git_commit_lookup(&annotated_commit->commit, repo, id)) < 0) {
git_annotated_commit_free(annotated_commit);
return error;
}
*out = annotated_commit; *out = annotated_commit;
done:
git_commit_free(commit);
return error; return error;
} }
...@@ -75,6 +77,51 @@ int git_annotated_commit_from_ref( ...@@ -75,6 +77,51 @@ int git_annotated_commit_from_ref(
return error; return error;
} }
int git_annotated_commit_from_head(
git_annotated_commit **out,
git_repository *repo)
{
git_reference *head;
int error;
assert(out && repo);
*out = NULL;
if ((error = git_reference_lookup(&head, repo, GIT_HEAD_FILE)) < 0)
return -1;
error = git_annotated_commit_from_ref(out, repo, head);
git_reference_free(head);
return error;
}
int git_annotated_commit_from_commit(
git_annotated_commit **out,
git_commit *commit)
{
git_annotated_commit *annotated_commit;
assert(out && commit);
*out = NULL;
annotated_commit = git__calloc(1, sizeof(git_annotated_commit));
GITERR_CHECK_ALLOC(annotated_commit);
annotated_commit->type = GIT_ANNOTATED_COMMIT_REAL;
git_cached_obj_incref(commit);
annotated_commit->commit = commit;
git_oid_fmt(annotated_commit->id_str, git_commit_id(commit));
annotated_commit->id_str[GIT_OID_HEXSZ] = '\0';
*out = annotated_commit;
return 0;
}
int git_annotated_commit_lookup( int git_annotated_commit_lookup(
git_annotated_commit **out, git_annotated_commit **out,
git_repository *repo, git_repository *repo,
...@@ -136,14 +183,20 @@ void git_annotated_commit_free(git_annotated_commit *annotated_commit) ...@@ -136,14 +183,20 @@ void git_annotated_commit_free(git_annotated_commit *annotated_commit)
if (annotated_commit == NULL) if (annotated_commit == NULL)
return; return;
if (annotated_commit->commit != NULL) switch (annotated_commit->type) {
git_commit_free(annotated_commit->commit); case GIT_ANNOTATED_COMMIT_REAL:
git_commit_free(annotated_commit->commit);
if (annotated_commit->ref_name != NULL) git_tree_free(annotated_commit->tree);
git__free(annotated_commit->ref_name); git__free(annotated_commit->ref_name);
git__free(annotated_commit->remote_url);
if (annotated_commit->remote_url != NULL) break;
git__free(annotated_commit->remote_url); case GIT_ANNOTATED_COMMIT_VIRTUAL:
git_index_free(annotated_commit->index);
git_array_clear(annotated_commit->parents);
break;
default:
abort();
}
git__free(annotated_commit); git__free(annotated_commit);
} }
...@@ -7,11 +7,31 @@ ...@@ -7,11 +7,31 @@
#ifndef INCLUDE_annotated_commit_h__ #ifndef INCLUDE_annotated_commit_h__
#define INCLUDE_annotated_commit_h__ #define INCLUDE_annotated_commit_h__
#include "oidarray.h"
#include "git2/oid.h" #include "git2/oid.h"
/** Internal structure for merge inputs */ typedef enum {
GIT_ANNOTATED_COMMIT_REAL = 1,
GIT_ANNOTATED_COMMIT_VIRTUAL = 2,
} git_annotated_commit_t;
/**
* Internal structure for merge inputs. An annotated commit is generally
* "real" and backed by an actual commit in the repository, but merge will
* internally create "virtual" commits that are in-memory intermediate
* commits backed by an index.
*/
struct git_annotated_commit { struct git_annotated_commit {
git_annotated_commit_t type;
/* real commit */
git_commit *commit; git_commit *commit;
git_tree *tree;
/* virtual commit structure */
git_index *index;
git_array_oid_t parents;
char *ref_name; char *ref_name;
char *remote_url; char *remote_url;
...@@ -19,4 +39,9 @@ struct git_annotated_commit { ...@@ -19,4 +39,9 @@ struct git_annotated_commit {
char id_str[GIT_OID_HEXSZ+1]; char id_str[GIT_OID_HEXSZ+1];
}; };
extern int git_annotated_commit_from_head(git_annotated_commit **out,
git_repository *repo);
extern int git_annotated_commit_from_commit(git_annotated_commit **out,
git_commit *commit);
#endif #endif
...@@ -19,8 +19,8 @@ ...@@ -19,8 +19,8 @@
#define GIT_MERGE_MODE_FILE "MERGE_MODE" #define GIT_MERGE_MODE_FILE "MERGE_MODE"
#define GIT_MERGE_FILE_MODE 0666 #define GIT_MERGE_FILE_MODE 0666
#define GIT_MERGE_TREE_RENAME_THRESHOLD 50 #define GIT_MERGE_DEFAULT_RENAME_THRESHOLD 50
#define GIT_MERGE_TREE_TARGET_LIMIT 1000 #define GIT_MERGE_DEFAULT_TARGET_LIMIT 1000
/** Types of changes when files are merged from branch to branch. */ /** Types of changes when files are merged from branch to branch. */
typedef enum { typedef enum {
......
...@@ -300,7 +300,7 @@ void test_cherrypick_workdir__rename(void) ...@@ -300,7 +300,7 @@ void test_cherrypick_workdir__rename(void)
{ 0100644, "28d9eb4208074ad1cc84e71ccc908b34573f05d2", 0, "file3.txt.renamed" }, { 0100644, "28d9eb4208074ad1cc84e71ccc908b34573f05d2", 0, "file3.txt.renamed" },
}; };
opts.merge_opts.tree_flags |= GIT_MERGE_TREE_FIND_RENAMES; opts.merge_opts.flags |= GIT_MERGE_FIND_RENAMES;
opts.merge_opts.rename_threshold = 50; opts.merge_opts.rename_threshold = 50;
git_oid_fromstr(&head_oid, "cfc4f0999a8367568e049af4f72e452d40828a15"); git_oid_fromstr(&head_oid, "cfc4f0999a8367568e049af4f72e452d40828a15");
...@@ -335,7 +335,7 @@ void test_cherrypick_workdir__both_renamed(void) ...@@ -335,7 +335,7 @@ void test_cherrypick_workdir__both_renamed(void)
{ 0100644, "28d9eb4208074ad1cc84e71ccc908b34573f05d2", 2, "file3.txt.renamed_on_branch" }, { 0100644, "28d9eb4208074ad1cc84e71ccc908b34573f05d2", 2, "file3.txt.renamed_on_branch" },
}; };
opts.merge_opts.tree_flags |= GIT_MERGE_TREE_FIND_RENAMES; opts.merge_opts.flags |= GIT_MERGE_FIND_RENAMES;
opts.merge_opts.rename_threshold = 50; opts.merge_opts.rename_threshold = 50;
git_oid_fromstr(&head_oid, "44cd2ed2052c9c68f9a439d208e9614dc2a55c70"); git_oid_fromstr(&head_oid, "44cd2ed2052c9c68f9a439d208e9614dc2a55c70");
......
#define AUTOMERGEABLE_MERGED_FILE \
"this file is changed in master\n" \
"this file is automergeable\n" \
"this file is automergeable\n" \
"this file is automergeable\n" \
"this file is automergeable\n" \
"this file is automergeable\n" \
"this file is automergeable\n" \
"this file is automergeable\n" \
"this file is changed in branch\n"
#define AUTOMERGEABLE_MERGED_FILE_CRLF \
"this file is changed in master\r\n" \
"this file is automergeable\r\n" \
"this file is automergeable\r\n" \
"this file is automergeable\r\n" \
"this file is automergeable\r\n" \
"this file is automergeable\r\n" \
"this file is automergeable\r\n" \
"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"
#define CONFLICTING_UNION_FILE \
"this file is changed in master and branch\n" \
"this file is changed in branch and master\n"
#define CONFLICTING_RECURSIVE_F1_TO_F2 \
"VEAL SOUP.\n" \
"\n" \
"<<<<<<< HEAD\n" \
"PUT INTO A POT THREE QUARTS OF WATER, three onions cut small, ONE\n" \
"=======\n" \
"PUT INTO A POT THREE QUARTS OF WATER, three onions cut not too small, one\n" \
">>>>>>> branchF-2\n" \
"spoonful of black pepper pounded, and two of salt, with two or three\n" \
"slices of lean ham; let it boil steadily two hours; skim it\n" \
"occasionally, then put into it a shin of veal, let it boil two hours\n" \
"longer; take out the slices of ham, and skim off the grease if any\n" \
"should rise, take a gill of good cream, mix with it two table-spoonsful\n" \
"of flour very nicely, and the yelks of two eggs beaten well, strain this\n" \
"mixture, and add some chopped parsley; pour some soup on by degrees,\n" \
"stir it well, and pour it into the pot, continuing to stir until it has\n" \
"boiled two or three minutes to take off the raw taste of the eggs. If\n" \
"the cream be not perfectly sweet, and the eggs quite new, the thickening\n" \
"will curdle in the soup. For a change you may put a dozen ripe tomatos\n" \
"in, first taking off their skins, by letting them stand a few minutes in\n" \
"hot water, when they may be easily peeled. When made in this way you\n" \
"must thicken it with the flour only. Any part of the veal may be used,\n" \
"but the shin or knuckle is the nicest.\n" \
"\n" \
"<<<<<<< HEAD\n" \
"This certainly is a mighty fine recipe.\n" \
"=======\n" \
"This is a mighty fine recipe!\n" \
">>>>>>> branchF-2\n"
#define CONFLICTING_RECURSIVE_H1_TO_H2_WITH_DIFF3 \
"VEAL SOUP.\n" \
"\n" \
"<<<<<<< HEAD\n" \
"put into a pot three quarts of water, three onions cut small, one\n" \
"||||||| merged common ancestors\n" \
"<<<<<<< Temporary merge branch 1\n" \
"Put into a pot three quarts of water, THREE ONIONS CUT SMALL, one\n" \
"||||||| merged common ancestors\n" \
"Put into a pot three quarts of water, three onions cut small, one\n" \
"=======\n" \
"PUT INTO A POT three quarts of water, three onions cut small, one\n" \
">>>>>>> Temporary merge branch 2\n" \
"=======\n" \
"Put Into A Pot Three Quarts of Water, Three Onions Cut Small, One\n" \
">>>>>>> branchH-2\n" \
"spoonful of black pepper pounded, and two of salt, with two or three\n" \
"slices of lean ham; let it boil steadily two hours; skim it\n" \
"occasionally, then put into it a shin of veal, let it boil two hours\n" \
"longer; take out the slices of ham, and skim off the grease if any\n" \
"should rise, take a gill of good cream, mix with it two table-spoonsful\n" \
"of flour very nicely, and the yelks of two eggs beaten well, strain this\n" \
"mixture, and add some chopped parsley; pour some soup on by degrees,\n" \
"stir it well, and pour it into the pot, continuing to stir until it has\n" \
"boiled two or three minutes to take off the raw taste of the eggs. If\n" \
"the cream be not perfectly sweet, and the eggs quite new, the thickening\n" \
"will curdle in the soup. For a change you may put a dozen ripe tomatos\n" \
"in, first taking off their skins, by letting them stand a few minutes in\n" \
"hot water, when they may be easily peeled. When made in this way you\n" \
"must thicken it with the flour only. Any part of the veal may be used,\n" \
"but the shin or knuckle is the nicest.\n"
...@@ -4,6 +4,7 @@ ...@@ -4,6 +4,7 @@
#include "buffer.h" #include "buffer.h"
#include "merge.h" #include "merge.h"
#include "merge_helpers.h" #include "merge_helpers.h"
#include "conflict_data.h"
#include "refs.h" #include "refs.h"
#include "fileops.h" #include "fileops.h"
#include "diff_xdiff.h" #include "diff_xdiff.h"
......
...@@ -4,6 +4,7 @@ ...@@ -4,6 +4,7 @@
#include "tree.h" #include "tree.h"
#include "merge_helpers.h" #include "merge_helpers.h"
#include "merge.h" #include "merge.h"
#include "index.h"
#include "git2/merge.h" #include "git2/merge.h"
#include "git2/sys/index.h" #include "git2/sys/index.h"
#include "git2/annotated_commit.h" #include "git2/annotated_commit.h"
...@@ -239,7 +240,7 @@ int merge_test_index(git_index *index, const struct merge_index_entry expected[] ...@@ -239,7 +240,7 @@ int merge_test_index(git_index *index, const struct merge_index_entry expected[]
const git_index_entry *index_entry; const git_index_entry *index_entry;
/* /*
dump_index_entries(&index->entries); merge__dump_index_entries(&index->entries);
*/ */
if (git_index_entrycount(index) != expected_len) if (git_index_entrycount(index) != expected_len)
......
...@@ -4,49 +4,6 @@ ...@@ -4,49 +4,6 @@
#include "merge.h" #include "merge.h"
#include "git2/merge.h" #include "git2/merge.h"
#define AUTOMERGEABLE_MERGED_FILE \
"this file is changed in master\n" \
"this file is automergeable\n" \
"this file is automergeable\n" \
"this file is automergeable\n" \
"this file is automergeable\n" \
"this file is automergeable\n" \
"this file is automergeable\n" \
"this file is automergeable\n" \
"this file is changed in branch\n"
#define AUTOMERGEABLE_MERGED_FILE_CRLF \
"this file is changed in master\r\n" \
"this file is automergeable\r\n" \
"this file is automergeable\r\n" \
"this file is automergeable\r\n" \
"this file is automergeable\r\n" \
"this file is automergeable\r\n" \
"this file is automergeable\r\n" \
"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"
#define CONFLICTING_UNION_FILE \
"this file is changed in master and branch\n" \
"this file is changed in branch and master\n"
struct merge_index_entry { struct merge_index_entry {
uint16_t mode; uint16_t mode;
char oid_str[GIT_OID_HEXSZ+1]; char oid_str[GIT_OID_HEXSZ+1];
......
...@@ -3,8 +3,9 @@ ...@@ -3,8 +3,9 @@
#include "git2/merge.h" #include "git2/merge.h"
#include "buffer.h" #include "buffer.h"
#include "merge.h" #include "merge.h"
#include "../merge_helpers.h"
#include "fileops.h" #include "fileops.h"
#include "../merge_helpers.h"
#include "../conflict_data.h"
static git_repository *repo; static git_repository *repo;
......
...@@ -3,6 +3,7 @@ ...@@ -3,6 +3,7 @@
#include "git2/merge.h" #include "git2/merge.h"
#include "merge.h" #include "merge.h"
#include "../merge_helpers.h" #include "../merge_helpers.h"
#include "../conflict_data.h"
static git_repository *repo; static git_repository *repo;
...@@ -134,7 +135,7 @@ void test_merge_trees_commits__fail_on_conflict(void) ...@@ -134,7 +135,7 @@ void test_merge_trees_commits__fail_on_conflict(void)
git_index *index; git_index *index;
git_merge_options opts = GIT_MERGE_OPTIONS_INIT; git_merge_options opts = GIT_MERGE_OPTIONS_INIT;
opts.tree_flags |= GIT_MERGE_TREE_FAIL_ON_CONFLICT; opts.flags |= GIT_MERGE_FAIL_ON_CONFLICT;
cl_git_fail_with(GIT_EMERGECONFLICT, cl_git_fail_with(GIT_EMERGECONFLICT,
merge_trees_from_branches(&index, repo, "df_side1", "df_side2", &opts)); merge_trees_from_branches(&index, repo, "df_side1", "df_side2", &opts));
......
...@@ -47,7 +47,7 @@ static void test_find_differences( ...@@ -47,7 +47,7 @@ static void test_find_differences(
git_iterator_options iter_opts = GIT_ITERATOR_OPTIONS_INIT; git_iterator_options iter_opts = GIT_ITERATOR_OPTIONS_INIT;
git_merge_options opts = GIT_MERGE_OPTIONS_INIT; git_merge_options opts = GIT_MERGE_OPTIONS_INIT;
opts.tree_flags |= GIT_MERGE_TREE_FIND_RENAMES; opts.flags |= GIT_MERGE_FIND_RENAMES;
opts.target_limit = 1000; opts.target_limit = 1000;
opts.rename_threshold = 50; opts.rename_threshold = 50;
......
#include "clar_libgit2.h"
#include "git2/repository.h"
#include "git2/merge.h"
#include "merge.h"
#include "../merge_helpers.h"
#include "../conflict_data.h"
static git_repository *repo;
#define TEST_REPO_PATH "merge-recursive"
void test_merge_workdir_recursive__initialize(void)
{
repo = cl_git_sandbox_init(TEST_REPO_PATH);
}
void test_merge_workdir_recursive__cleanup(void)
{
cl_git_sandbox_cleanup();
}
void test_merge_workdir_recursive__writes_conflict_with_virtual_base(void)
{
git_index *index;
git_merge_options opts = GIT_MERGE_OPTIONS_INIT;
git_buf conflicting_buf = GIT_BUF_INIT;
struct merge_index_entry merge_index_entries[] = {
{ 0100644, "ffb36e513f5fdf8a6ba850a20142676a2ac4807d", 0, "asparagus.txt" },
{ 0100644, "68f6182f4c85d39e1309d97c7e456156dc9c0096", 0, "beef.txt" },
{ 0100644, "4b7c5650008b2e747fe1809eeb5a1dde0e80850a", 0, "bouilli.txt" },
{ 0100644, "c4e6cca3ec6ae0148ed231f97257df8c311e015f", 0, "gravy.txt" },
{ 0100644, "68af1fc7407fd9addf1701a87eb1c95c7494c598", 0, "oyster.txt" },
{ 0100644, "fa567f568ed72157c0c617438d077695b99d9aac", 1, "veal.txt" },
{ 0100644, "21950d5e4e4d1a871b4dfcf72ecb6b9c162c434e", 2, "veal.txt" },
{ 0100644, "3855170cef875708da06ab9ad7fc6a73b531cda1", 3, "veal.txt" },
};
cl_git_pass(merge_branches(repo, GIT_REFS_HEADS_DIR "branchF-1", GIT_REFS_HEADS_DIR "branchF-2", &opts, NULL));
cl_git_pass(git_repository_index(&index, repo));
cl_assert(merge_test_index(index, merge_index_entries, 8));
cl_git_pass(git_futils_readbuffer(&conflicting_buf, "merge-recursive/veal.txt"));
cl_assert_equal_s(CONFLICTING_RECURSIVE_F1_TO_F2, conflicting_buf.ptr);
git_index_free(index);
git_buf_free(&conflicting_buf);
}
void test_merge_workdir_recursive__conflicting_merge_base_with_diff3(void)
{
git_index *index;
git_merge_options opts = GIT_MERGE_OPTIONS_INIT;
git_checkout_options checkout_opts = GIT_CHECKOUT_OPTIONS_INIT;
git_buf conflicting_buf = GIT_BUF_INIT;
struct merge_index_entry merge_index_entries[] = {
{ 0100644, "ffb36e513f5fdf8a6ba850a20142676a2ac4807d", 0, "asparagus.txt" },
{ 0100644, "68f6182f4c85d39e1309d97c7e456156dc9c0096", 0, "beef.txt" },
{ 0100644, "4b7c5650008b2e747fe1809eeb5a1dde0e80850a", 0, "bouilli.txt" },
{ 0100644, "c4e6cca3ec6ae0148ed231f97257df8c311e015f", 0, "gravy.txt" },
{ 0100644, "68af1fc7407fd9addf1701a87eb1c95c7494c598", 0, "oyster.txt" },
{ 0100644, "cd17a91513f3aee9e44114d1ede67932dd41d2fc", 1, "veal.txt" },
{ 0100644, "d604c75019c282144bdbbf3fd3462ba74b240efc", 2, "veal.txt" },
{ 0100644, "37a5054a9f9b4628e3924c5cb8f2147c6e2a3efc", 3, "veal.txt" },
};
opts.file_flags |= GIT_MERGE_FILE_STYLE_DIFF3;
checkout_opts.checkout_strategy |= GIT_CHECKOUT_CONFLICT_STYLE_DIFF3;
cl_git_pass(merge_branches(repo, GIT_REFS_HEADS_DIR "branchH-1", GIT_REFS_HEADS_DIR "branchH-2", &opts, &checkout_opts));
cl_git_pass(git_repository_index(&index, repo));
cl_assert(merge_test_index(index, merge_index_entries, 8));
cl_git_pass(git_futils_readbuffer(&conflicting_buf, "merge-recursive/veal.txt"));
cl_assert_equal_s(CONFLICTING_RECURSIVE_H1_TO_H2_WITH_DIFF3, conflicting_buf.ptr);
git_index_free(index);
git_buf_free(&conflicting_buf);
}
...@@ -63,7 +63,7 @@ void test_merge_workdir_renames__renames(void) ...@@ -63,7 +63,7 @@ void test_merge_workdir_renames__renames(void)
{ 0100644, "b69fe837e4cecfd4c9a40cdca7c138468687df07", 0, "7-both-renamed.txt~rename_conflict_theirs" }, { 0100644, "b69fe837e4cecfd4c9a40cdca7c138468687df07", 0, "7-both-renamed.txt~rename_conflict_theirs" },
}; };
merge_opts.tree_flags |= GIT_MERGE_TREE_FIND_RENAMES; merge_opts.flags |= GIT_MERGE_FIND_RENAMES;
merge_opts.rename_threshold = 50; merge_opts.rename_threshold = 50;
cl_git_pass(merge_branches(repo, GIT_REFS_HEADS_DIR BRANCH_RENAME_OURS, GIT_REFS_HEADS_DIR BRANCH_RENAME_THEIRS, &merge_opts, NULL)); cl_git_pass(merge_branches(repo, GIT_REFS_HEADS_DIR BRANCH_RENAME_OURS, GIT_REFS_HEADS_DIR BRANCH_RENAME_THEIRS, &merge_opts, NULL));
...@@ -99,7 +99,7 @@ void test_merge_workdir_renames__ours(void) ...@@ -99,7 +99,7 @@ void test_merge_workdir_renames__ours(void)
{ 0100644, "b42712cfe99a1a500b2a51fe984e0b8a7702ba11", 0, "7-both-renamed.txt" }, { 0100644, "b42712cfe99a1a500b2a51fe984e0b8a7702ba11", 0, "7-both-renamed.txt" },
}; };
merge_opts.tree_flags |= GIT_MERGE_TREE_FIND_RENAMES; merge_opts.flags |= GIT_MERGE_FIND_RENAMES;
merge_opts.rename_threshold = 50; merge_opts.rename_threshold = 50;
checkout_opts.checkout_strategy = GIT_CHECKOUT_SAFE | GIT_CHECKOUT_USE_OURS; checkout_opts.checkout_strategy = GIT_CHECKOUT_SAFE | GIT_CHECKOUT_USE_OURS;
...@@ -147,7 +147,7 @@ void test_merge_workdir_renames__similar(void) ...@@ -147,7 +147,7 @@ void test_merge_workdir_renames__similar(void)
{ 0100644, "b69fe837e4cecfd4c9a40cdca7c138468687df07", 0, "7-both-renamed.txt~rename_conflict_theirs" }, { 0100644, "b69fe837e4cecfd4c9a40cdca7c138468687df07", 0, "7-both-renamed.txt~rename_conflict_theirs" },
}; };
merge_opts.tree_flags |= GIT_MERGE_TREE_FIND_RENAMES; merge_opts.flags |= GIT_MERGE_FIND_RENAMES;
merge_opts.rename_threshold = 50; merge_opts.rename_threshold = 50;
cl_git_pass(merge_branches(repo, GIT_REFS_HEADS_DIR BRANCH_RENAME_OURS, GIT_REFS_HEADS_DIR BRANCH_RENAME_THEIRS, &merge_opts, NULL)); cl_git_pass(merge_branches(repo, GIT_REFS_HEADS_DIR BRANCH_RENAME_OURS, GIT_REFS_HEADS_DIR BRANCH_RENAME_THEIRS, &merge_opts, NULL));
......
...@@ -4,6 +4,7 @@ ...@@ -4,6 +4,7 @@
#include "buffer.h" #include "buffer.h"
#include "merge.h" #include "merge.h"
#include "../merge_helpers.h" #include "../merge_helpers.h"
#include "../conflict_data.h"
#include "refs.h" #include "refs.h"
#include "fileops.h" #include "fileops.h"
......
[core]
repositoryformatversion = 0
filemode = true
bare = false
logallrefupdates = true
ignorecase = true
precomposeunicode = true
7c7bf85e978f1d18c0566f702d2cb7766b9c8d4f refs/heads/master
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