mirror of
https://git.proxmox.com/git/libgit2
synced 2025-05-09 16:45:49 +00:00
commit
453e6b3ec0
@ -65,6 +65,29 @@ typedef struct {
|
||||
#define GIT_MERGE_TREE_OPTS_INIT {GIT_MERGE_TREE_OPTS_VERSION}
|
||||
|
||||
|
||||
/**
|
||||
* Option flags for `git_merge`.
|
||||
*
|
||||
* GIT_MERGE_NO_FASTFORWARD - Do not fast-forward.
|
||||
*/
|
||||
typedef enum {
|
||||
GIT_MERGE_NO_FASTFORWARD = 1,
|
||||
GIT_MERGE_FASTFORWARD_ONLY = 2,
|
||||
} git_merge_flags_t;
|
||||
|
||||
typedef struct {
|
||||
unsigned int version;
|
||||
|
||||
git_merge_flags_t merge_flags;
|
||||
git_merge_tree_opts merge_tree_opts;
|
||||
|
||||
git_checkout_opts checkout_opts;
|
||||
} git_merge_opts;
|
||||
|
||||
#define GIT_MERGE_OPTS_VERSION 1
|
||||
#define GIT_MERGE_OPTS_INIT {GIT_MERGE_OPTS_VERSION, 0, GIT_MERGE_TREE_OPTS_INIT, GIT_CHECKOUT_OPTS_INIT}
|
||||
|
||||
|
||||
/**
|
||||
* Find a merge base between two commits
|
||||
*
|
||||
@ -168,6 +191,43 @@ GIT_EXTERN(int) git_merge_trees(
|
||||
const git_tree *their_tree,
|
||||
const git_merge_tree_opts *opts);
|
||||
|
||||
/**
|
||||
* Merges the given commits into HEAD, producing a new commit.
|
||||
*
|
||||
* @param out the results of the merge
|
||||
* @param repo the repository to merge
|
||||
* @param merge_heads the heads to merge into
|
||||
* @param merge_heads_len the number of heads to merge
|
||||
* @param flags merge flags
|
||||
*/
|
||||
GIT_EXTERN(int) git_merge(
|
||||
git_merge_result **out,
|
||||
git_repository *repo,
|
||||
const git_merge_head **their_heads,
|
||||
size_t their_heads_len,
|
||||
const git_merge_opts *opts);
|
||||
|
||||
/**
|
||||
* Returns true if a merge is up-to-date (we were asked to merge the target
|
||||
* into itself.)
|
||||
*/
|
||||
GIT_EXTERN(int) git_merge_result_is_uptodate(git_merge_result *merge_result);
|
||||
|
||||
/**
|
||||
* Returns true if a merge is eligible for fastforward
|
||||
*/
|
||||
GIT_EXTERN(int) git_merge_result_is_fastforward(git_merge_result *merge_result);
|
||||
|
||||
/**
|
||||
* Gets the fast-forward OID if the merge was a fastforward.
|
||||
*
|
||||
* @param out the OID of the fast-forward
|
||||
* @param merge_result the results of the merge
|
||||
*/
|
||||
GIT_EXTERN(int) git_merge_result_fastforward_oid(git_oid *out, git_merge_result *merge_result);
|
||||
|
||||
GIT_EXTERN(void) git_merge_result_free(git_merge_result *merge_result);
|
||||
|
||||
/** @} */
|
||||
GIT_END_DECL
|
||||
#endif
|
||||
|
@ -174,6 +174,9 @@ typedef struct git_reference_iterator git_reference_iterator;
|
||||
/** Merge heads, the input to merge */
|
||||
typedef struct git_merge_head git_merge_head;
|
||||
|
||||
/** Merge result */
|
||||
typedef struct git_merge_result git_merge_result;
|
||||
|
||||
/** Representation of a status collection */
|
||||
typedef struct git_status_list git_status_list;
|
||||
|
||||
|
543
src/merge.c
543
src/merge.c
@ -1615,16 +1615,13 @@ static int write_orig_head(
|
||||
{
|
||||
git_filebuf file = GIT_FILEBUF_INIT;
|
||||
git_buf file_path = GIT_BUF_INIT;
|
||||
char orig_oid_str[GIT_OID_HEXSZ + 1];
|
||||
int error = 0;
|
||||
|
||||
assert(repo && our_head);
|
||||
|
||||
git_oid_tostr(orig_oid_str, GIT_OID_HEXSZ+1, &our_head->oid);
|
||||
|
||||
if ((error = git_buf_joinpath(&file_path, repo->path_repository, GIT_ORIG_HEAD_FILE)) == 0 &&
|
||||
(error = git_filebuf_open(&file, file_path.ptr, GIT_FILEBUF_FORCE, GIT_MERGE_FILE_MODE)) == 0 &&
|
||||
(error = git_filebuf_printf(&file, "%s\n", orig_oid_str)) == 0)
|
||||
(error = git_filebuf_printf(&file, "%s\n", our_head->oid_str)) == 0)
|
||||
error = git_filebuf_commit(&file);
|
||||
|
||||
if (error < 0)
|
||||
@ -1642,7 +1639,6 @@ static int write_merge_head(
|
||||
{
|
||||
git_filebuf file = GIT_FILEBUF_INIT;
|
||||
git_buf file_path = GIT_BUF_INIT;
|
||||
char merge_oid_str[GIT_OID_HEXSZ + 1];
|
||||
size_t i;
|
||||
int error = 0;
|
||||
|
||||
@ -1653,9 +1649,7 @@ static int write_merge_head(
|
||||
goto cleanup;
|
||||
|
||||
for (i = 0; i < heads_len; i++) {
|
||||
git_oid_tostr(merge_oid_str, GIT_OID_HEXSZ+1, &heads[i]->oid);
|
||||
|
||||
if ((error = git_filebuf_printf(&file, "%s\n", merge_oid_str)) < 0)
|
||||
if ((error = git_filebuf_printf(&file, "%s\n", &heads[i]->oid_str)) < 0)
|
||||
goto cleanup;
|
||||
}
|
||||
|
||||
@ -1685,6 +1679,17 @@ static int write_merge_mode(git_repository *repo, unsigned int flags)
|
||||
(error = git_filebuf_open(&file, file_path.ptr, GIT_FILEBUF_FORCE, GIT_MERGE_FILE_MODE)) < 0)
|
||||
goto cleanup;
|
||||
|
||||
/*
|
||||
* no-ff is the only thing allowed here at present. One would
|
||||
* presume they would be space-delimited when there are more, but
|
||||
* this needs to be revisited.
|
||||
*/
|
||||
|
||||
if (flags & GIT_MERGE_NO_FASTFORWARD) {
|
||||
if ((error = git_filebuf_write(&file, "no-ff", 5)) < 0)
|
||||
goto cleanup;
|
||||
}
|
||||
|
||||
error = git_filebuf_commit(&file);
|
||||
|
||||
cleanup:
|
||||
@ -1890,7 +1895,6 @@ static int write_merge_msg(
|
||||
{
|
||||
git_filebuf file = GIT_FILEBUF_INIT;
|
||||
git_buf file_path = GIT_BUF_INIT;
|
||||
char oid_str[GIT_OID_HEXSZ + 1];
|
||||
struct merge_msg_entry *entries;
|
||||
git_vector matching = GIT_VECTOR_INIT;
|
||||
size_t i;
|
||||
@ -1930,10 +1934,9 @@ static int write_merge_msg(
|
||||
if (!msg_entry_is_oid(&entries[i]))
|
||||
break;
|
||||
|
||||
git_oid_fmt(oid_str, &entries[i].merge_head->oid);
|
||||
oid_str[GIT_OID_HEXSZ] = '\0';
|
||||
|
||||
if ((error = git_filebuf_printf(&file, "%scommit '%s'", (i > 0) ? "; " : "", oid_str)) < 0)
|
||||
if ((error = git_filebuf_printf(&file,
|
||||
"%scommit '%s'", (i > 0) ? "; " : "",
|
||||
entries[i].merge_head->oid_str)) < 0)
|
||||
goto cleanup;
|
||||
|
||||
entries[i].written = 1;
|
||||
@ -1980,10 +1983,8 @@ static int write_merge_msg(
|
||||
if (merge_msg_entry_written(&entries[i]))
|
||||
continue;
|
||||
|
||||
git_oid_fmt(oid_str, &entries[i].merge_head->oid);
|
||||
oid_str[GIT_OID_HEXSZ] = '\0';
|
||||
|
||||
if ((error = git_filebuf_printf(&file, "; commit '%s'", oid_str)) < 0)
|
||||
if ((error = git_filebuf_printf(&file, "; commit '%s'",
|
||||
entries[i].merge_head->oid_str)) < 0)
|
||||
goto cleanup;
|
||||
}
|
||||
|
||||
@ -2003,6 +2004,474 @@ cleanup:
|
||||
return error;
|
||||
}
|
||||
|
||||
/* Merge branches */
|
||||
|
||||
static int merge_ancestor_head(
|
||||
git_merge_head **ancestor_head,
|
||||
git_repository *repo,
|
||||
const git_merge_head *our_head,
|
||||
const git_merge_head **their_heads,
|
||||
size_t their_heads_len)
|
||||
{
|
||||
git_oid *oids, ancestor_oid;
|
||||
size_t i;
|
||||
int error = 0;
|
||||
|
||||
assert(repo && our_head && their_heads);
|
||||
|
||||
oids = git__calloc(their_heads_len + 1, sizeof(git_oid));
|
||||
GITERR_CHECK_ALLOC(oids);
|
||||
|
||||
git_oid_cpy(&oids[0], git_commit_id(our_head->commit));
|
||||
|
||||
for (i = 0; i < their_heads_len; i++)
|
||||
git_oid_cpy(&oids[i + 1], &their_heads[i]->oid);
|
||||
|
||||
if ((error = git_merge_base_many(&ancestor_oid, repo, their_heads_len + 1, oids)) < 0)
|
||||
goto on_error;
|
||||
|
||||
error = git_merge_head_from_oid(ancestor_head, repo, &ancestor_oid);
|
||||
|
||||
on_error:
|
||||
git__free(oids);
|
||||
return error;
|
||||
}
|
||||
|
||||
GIT_INLINE(bool) merge_check_uptodate(
|
||||
git_merge_result *result,
|
||||
const git_merge_head *ancestor_head,
|
||||
const git_merge_head *their_head)
|
||||
{
|
||||
if (git_oid_cmp(&ancestor_head->oid, &their_head->oid) == 0) {
|
||||
result->is_uptodate = 1;
|
||||
return true;
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
GIT_INLINE(bool) merge_check_fastforward(
|
||||
git_merge_result *result,
|
||||
const git_merge_head *ancestor_head,
|
||||
const git_merge_head *our_head,
|
||||
const git_merge_head *their_head,
|
||||
unsigned int flags)
|
||||
{
|
||||
if ((flags & GIT_MERGE_NO_FASTFORWARD) == 0 &&
|
||||
git_oid_cmp(&ancestor_head->oid, &our_head->oid) == 0) {
|
||||
result->is_fastforward = 1;
|
||||
git_oid_cpy(&result->fastforward_oid, &their_head->oid);
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
const char *merge_their_label(const char *branchname)
|
||||
{
|
||||
const char *slash;
|
||||
|
||||
if ((slash = strrchr(branchname, '/')) == NULL)
|
||||
return branchname;
|
||||
|
||||
if (*(slash+1) == '\0')
|
||||
return "theirs";
|
||||
|
||||
return slash+1;
|
||||
}
|
||||
|
||||
static int merge_normalize_opts(
|
||||
git_repository *repo,
|
||||
git_merge_opts *opts,
|
||||
const git_merge_opts *given,
|
||||
size_t their_heads_len,
|
||||
const git_merge_head **their_heads)
|
||||
{
|
||||
int error = 0;
|
||||
unsigned int default_checkout_strategy = GIT_CHECKOUT_SAFE_CREATE |
|
||||
GIT_CHECKOUT_ALLOW_CONFLICTS;
|
||||
|
||||
GIT_UNUSED(repo);
|
||||
|
||||
if (given != NULL)
|
||||
memcpy(opts, given, sizeof(git_merge_opts));
|
||||
else {
|
||||
git_merge_opts default_opts = GIT_MERGE_OPTS_INIT;
|
||||
memcpy(opts, &default_opts, sizeof(git_merge_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";
|
||||
|
||||
if (!opts->checkout_opts.their_label) {
|
||||
if (their_heads_len == 1 && their_heads[0]->ref_name)
|
||||
opts->checkout_opts.their_label = merge_their_label(their_heads[0]->ref_name);
|
||||
else if (their_heads_len == 1)
|
||||
opts->checkout_opts.their_label = their_heads[0]->oid_str;
|
||||
else
|
||||
opts->checkout_opts.their_label = "theirs";
|
||||
}
|
||||
|
||||
return error;
|
||||
}
|
||||
|
||||
static int merge_affected_paths(git_vector *paths, git_repository *repo, git_index *index_new)
|
||||
{
|
||||
git_tree *head_tree = NULL;
|
||||
git_iterator *iter_head = NULL, *iter_new = NULL;
|
||||
git_diff *merged_list = NULL;
|
||||
git_diff_options opts = GIT_DIFF_OPTIONS_INIT;
|
||||
git_diff_delta *delta;
|
||||
size_t i;
|
||||
const git_index_entry *e;
|
||||
char *path;
|
||||
int error = 0;
|
||||
|
||||
if ((error = git_repository_head_tree(&head_tree, repo)) < 0 ||
|
||||
(error = git_iterator_for_tree(&iter_head, head_tree, GIT_ITERATOR_DONT_IGNORE_CASE, NULL, NULL)) < 0 ||
|
||||
(error = git_iterator_for_index(&iter_new, index_new, GIT_ITERATOR_DONT_IGNORE_CASE, NULL, NULL)) < 0 ||
|
||||
(error = git_diff__from_iterators(&merged_list, repo, iter_head, iter_new, &opts)) < 0)
|
||||
goto done;
|
||||
|
||||
git_vector_foreach(&merged_list->deltas, i, delta) {
|
||||
path = git__strdup(delta->new_file.path);
|
||||
GITERR_CHECK_ALLOC(path);
|
||||
|
||||
if ((error = git_vector_insert(paths, path)) < 0)
|
||||
goto on_error;
|
||||
}
|
||||
|
||||
for (i = 0; i < git_index_entrycount(index_new); i++) {
|
||||
e = git_index_get_byindex(index_new, i);
|
||||
|
||||
if (git_index_entry_stage(e) != 0 &&
|
||||
(git_vector_last(paths) == NULL ||
|
||||
strcmp(git_vector_last(paths), e->path) != 0)) {
|
||||
|
||||
path = git__strdup(e->path);
|
||||
GITERR_CHECK_ALLOC(path);
|
||||
|
||||
if ((error = git_vector_insert(paths, path)) < 0)
|
||||
goto on_error;
|
||||
}
|
||||
}
|
||||
|
||||
goto done;
|
||||
|
||||
on_error:
|
||||
git_vector_foreach(paths, i, path)
|
||||
git__free(path);
|
||||
|
||||
git_vector_clear(paths);
|
||||
|
||||
done:
|
||||
git_tree_free(head_tree);
|
||||
git_iterator_free(iter_head);
|
||||
git_iterator_free(iter_new);
|
||||
git_diff_free(merged_list);
|
||||
|
||||
return error;
|
||||
}
|
||||
|
||||
static int merge_check_index(size_t *conflicts, git_repository *repo, git_index *index_new, git_vector *merged_paths)
|
||||
{
|
||||
git_tree *head_tree = NULL;
|
||||
git_index *index_repo = NULL;
|
||||
git_iterator *iter_repo = NULL, *iter_new = NULL;
|
||||
git_diff *staged_diff_list = NULL, *index_diff_list = NULL;
|
||||
git_diff_delta *delta;
|
||||
git_diff_options opts = GIT_DIFF_OPTIONS_INIT;
|
||||
git_vector staged_paths = GIT_VECTOR_INIT;
|
||||
size_t i;
|
||||
int error = 0;
|
||||
|
||||
GIT_UNUSED(merged_paths);
|
||||
|
||||
*conflicts = 0;
|
||||
|
||||
/* No staged changes may exist unless the change staged is identical to
|
||||
* the result of the merge. This allows one to apply to merge manually,
|
||||
* then run merge. Any other staged change would be overwritten by
|
||||
* a reset merge.
|
||||
*/
|
||||
if ((error = git_repository_head_tree(&head_tree, repo)) < 0 ||
|
||||
(error = git_repository_index(&index_repo, repo)) < 0 ||
|
||||
(error = git_diff_tree_to_index(&staged_diff_list, repo, head_tree, index_repo, &opts)) < 0)
|
||||
goto done;
|
||||
|
||||
if (staged_diff_list->deltas.length == 0)
|
||||
goto done;
|
||||
|
||||
git_vector_foreach(&staged_diff_list->deltas, i, delta) {
|
||||
if ((error = git_vector_insert(&staged_paths, (char *)delta->new_file.path)) < 0)
|
||||
goto done;
|
||||
}
|
||||
|
||||
opts.pathspec.count = staged_paths.length;
|
||||
opts.pathspec.strings = (char **)staged_paths.contents;
|
||||
|
||||
if ((error = git_iterator_for_index(&iter_repo, index_repo, GIT_ITERATOR_DONT_IGNORE_CASE, NULL, NULL)) < 0 ||
|
||||
(error = git_iterator_for_index(&iter_new, index_new, GIT_ITERATOR_DONT_IGNORE_CASE, NULL, NULL)) < 0 ||
|
||||
(error = git_diff__from_iterators(&index_diff_list, repo, iter_repo, iter_new, &opts)) < 0)
|
||||
goto done;
|
||||
|
||||
*conflicts = index_diff_list->deltas.length;
|
||||
|
||||
done:
|
||||
git_tree_free(head_tree);
|
||||
git_index_free(index_repo);
|
||||
git_iterator_free(iter_repo);
|
||||
git_iterator_free(iter_new);
|
||||
git_diff_free(staged_diff_list);
|
||||
git_diff_free(index_diff_list);
|
||||
git_vector_free(&staged_paths);
|
||||
|
||||
return error;
|
||||
}
|
||||
|
||||
static int merge_check_workdir(size_t *conflicts, git_repository *repo, git_index *index_new, git_vector *merged_paths)
|
||||
{
|
||||
git_tree *head_tree = NULL;
|
||||
git_diff *wd_diff_list = NULL;
|
||||
git_diff_options opts = GIT_DIFF_OPTIONS_INIT;
|
||||
int error = 0;
|
||||
|
||||
*conflicts = 0;
|
||||
|
||||
opts.flags |= GIT_DIFF_INCLUDE_UNTRACKED;
|
||||
|
||||
if ((error = git_repository_head_tree(&head_tree, repo)) < 0)
|
||||
goto done;
|
||||
|
||||
/* Workdir changes may exist iff they do not conflict with changes that
|
||||
* will be applied by the merge (including conflicts). Ensure that there
|
||||
* are no changes in the workdir to these paths.
|
||||
*/
|
||||
opts.pathspec.count = merged_paths->length;
|
||||
opts.pathspec.strings = (char **)merged_paths->contents;
|
||||
|
||||
if ((error = git_diff_tree_to_workdir(&wd_diff_list, repo, head_tree, &opts)) < 0)
|
||||
goto done;
|
||||
|
||||
*conflicts = wd_diff_list->deltas.length;
|
||||
|
||||
done:
|
||||
git_tree_free(head_tree);
|
||||
git_diff_free(wd_diff_list);
|
||||
|
||||
return error;
|
||||
}
|
||||
|
||||
static int merge_indexes(git_repository *repo, git_index *index_new)
|
||||
{
|
||||
git_index *index_repo;
|
||||
unsigned int index_repo_caps;
|
||||
git_vector paths = GIT_VECTOR_INIT;
|
||||
size_t index_conflicts = 0, wd_conflicts = 0, conflicts, i;
|
||||
char *path;
|
||||
const git_index_entry *e;
|
||||
const git_index_name_entry *name;
|
||||
const git_index_reuc_entry *reuc;
|
||||
int error = 0;
|
||||
|
||||
if ((error = git_repository_index(&index_repo, repo)) < 0)
|
||||
goto done;
|
||||
|
||||
/* Set the index to case sensitive to handle the merge */
|
||||
index_repo_caps = git_index_caps(index_repo);
|
||||
|
||||
if ((error = git_index_set_caps(index_repo, (index_repo_caps & ~GIT_INDEXCAP_IGNORE_CASE))) < 0)
|
||||
goto done;
|
||||
|
||||
/* Make sure the index and workdir state do not prevent merging */
|
||||
if ((error = merge_affected_paths(&paths, repo, index_new)) < 0 ||
|
||||
(error = merge_check_index(&index_conflicts, repo, index_new, &paths)) < 0 ||
|
||||
(error = merge_check_workdir(&wd_conflicts, repo, index_new, &paths)) < 0)
|
||||
goto done;
|
||||
|
||||
if ((conflicts = index_conflicts + wd_conflicts) > 0) {
|
||||
giterr_set(GITERR_MERGE, "%d uncommitted change%s would be overwritten by merge",
|
||||
conflicts, (conflicts != 1) ? "s" : "");
|
||||
error = GIT_EMERGECONFLICT;
|
||||
|
||||
goto done;
|
||||
}
|
||||
|
||||
/* Update the new index */
|
||||
git_vector_foreach(&paths, i, path) {
|
||||
if ((e = git_index_get_bypath(index_new, path, 0)) != NULL)
|
||||
error = git_index_add(index_repo, e);
|
||||
else
|
||||
error = git_index_remove(index_repo, path, 0);
|
||||
}
|
||||
|
||||
/* Add conflicts */
|
||||
git_index_conflict_cleanup(index_repo);
|
||||
|
||||
for (i = 0; i < git_index_entrycount(index_new); i++) {
|
||||
e = git_index_get_byindex(index_new, i);
|
||||
|
||||
if (git_index_entry_stage(e) != 0 &&
|
||||
(error = git_index_add(index_repo, e)) < 0)
|
||||
goto done;
|
||||
}
|
||||
|
||||
/* Add name entries */
|
||||
git_index_name_clear(index_repo);
|
||||
|
||||
for (i = 0; i < git_index_name_entrycount(index_new); i++) {
|
||||
name = git_index_name_get_byindex(index_new, i);
|
||||
|
||||
if ((error = git_index_name_add(index_repo,
|
||||
name->ancestor, name->ours, name->theirs)) < 0)
|
||||
goto done;
|
||||
}
|
||||
|
||||
/* Add the reuc */
|
||||
git_index_reuc_clear(index_repo);
|
||||
|
||||
for (i = 0; i < git_index_reuc_entrycount(index_new); i++) {
|
||||
reuc = (git_index_reuc_entry *)git_index_reuc_get_byindex(index_new, i);
|
||||
|
||||
if ((error = git_index_reuc_add(index_repo, reuc->path,
|
||||
reuc->mode[0], &reuc->oid[0],
|
||||
reuc->mode[1], &reuc->oid[1],
|
||||
reuc->mode[2], &reuc->oid[2])) < 0)
|
||||
goto done;
|
||||
}
|
||||
|
||||
done:
|
||||
if (index_repo != NULL)
|
||||
git_index_set_caps(index_repo, index_repo_caps);
|
||||
|
||||
git_index_free(index_repo);
|
||||
|
||||
git_vector_foreach(&paths, i, path)
|
||||
git__free(path);
|
||||
|
||||
git_vector_free(&paths);
|
||||
|
||||
return error;
|
||||
}
|
||||
|
||||
int git_merge(
|
||||
git_merge_result **out,
|
||||
git_repository *repo,
|
||||
const git_merge_head **their_heads,
|
||||
size_t their_heads_len,
|
||||
const git_merge_opts *given_opts)
|
||||
{
|
||||
git_merge_result *result;
|
||||
git_merge_opts opts;
|
||||
git_reference *our_ref = NULL;
|
||||
git_merge_head *ancestor_head = NULL, *our_head = NULL;
|
||||
git_tree *ancestor_tree = NULL, *our_tree = NULL, **their_trees = NULL;
|
||||
git_index *index_new = NULL, *index_repo = NULL;
|
||||
size_t i;
|
||||
int error = 0;
|
||||
|
||||
assert(out && repo && their_heads);
|
||||
|
||||
*out = NULL;
|
||||
|
||||
if (their_heads_len != 1) {
|
||||
giterr_set(GITERR_MERGE, "Can only merge a single branch");
|
||||
return -1;
|
||||
}
|
||||
|
||||
result = git__calloc(1, sizeof(git_merge_result));
|
||||
GITERR_CHECK_ALLOC(result);
|
||||
|
||||
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;
|
||||
|
||||
if ((error = git_reference_lookup(&our_ref, repo, GIT_HEAD_FILE)) < 0 ||
|
||||
(error = git_merge_head_from_ref(&our_head, repo, our_ref)) < 0)
|
||||
goto on_error;
|
||||
|
||||
if ((error = merge_ancestor_head(&ancestor_head, repo, our_head, their_heads, their_heads_len)) < 0 &&
|
||||
error != GIT_ENOTFOUND)
|
||||
goto on_error;
|
||||
|
||||
if (their_heads_len == 1 &&
|
||||
ancestor_head != NULL &&
|
||||
(merge_check_uptodate(result, ancestor_head, their_heads[0]) ||
|
||||
merge_check_fastforward(result, ancestor_head, our_head, their_heads[0], opts.merge_flags))) {
|
||||
*out = result;
|
||||
goto done;
|
||||
}
|
||||
|
||||
/* If FASTFORWARD_ONLY is specified, fail. */
|
||||
if ((opts.merge_flags & GIT_MERGE_FASTFORWARD_ONLY) ==
|
||||
GIT_MERGE_FASTFORWARD_ONLY) {
|
||||
giterr_set(GITERR_MERGE, "Not a fast-forward.");
|
||||
error = GIT_ENONFASTFORWARD;
|
||||
goto on_error;
|
||||
}
|
||||
|
||||
/* Write the merge files to the repository. */
|
||||
if ((error = git_merge__setup(repo, our_head, their_heads, their_heads_len, opts.merge_flags)) < 0)
|
||||
goto on_error;
|
||||
|
||||
if (ancestor_head != NULL &&
|
||||
(error = git_commit_tree(&ancestor_tree, ancestor_head->commit)) < 0)
|
||||
goto on_error;
|
||||
|
||||
if ((error = git_commit_tree(&our_tree, our_head->commit)) < 0)
|
||||
goto on_error;
|
||||
|
||||
for (i = 0; i < their_heads_len; i++) {
|
||||
if ((error = git_commit_tree(&their_trees[i], their_heads[i]->commit)) < 0)
|
||||
goto on_error;
|
||||
}
|
||||
|
||||
/* TODO: recursive, octopus, etc... */
|
||||
|
||||
if ((error = git_merge_trees(&index_new, repo, ancestor_tree, our_tree, their_trees[0], &opts.merge_tree_opts)) < 0 ||
|
||||
(error = merge_indexes(repo, index_new)) < 0 ||
|
||||
(error = git_repository_index(&index_repo, repo)) < 0 ||
|
||||
(error = git_checkout_index(repo, index_repo, &opts.checkout_opts)) < 0)
|
||||
goto on_error;
|
||||
|
||||
result->index = index_new;
|
||||
|
||||
*out = result;
|
||||
goto done;
|
||||
|
||||
on_error:
|
||||
git_repository_merge_cleanup(repo);
|
||||
|
||||
git__free(result);
|
||||
|
||||
done:
|
||||
git_index_free(index_repo);
|
||||
|
||||
git_tree_free(ancestor_tree);
|
||||
git_tree_free(our_tree);
|
||||
|
||||
for (i = 0; i < their_heads_len; i++)
|
||||
git_tree_free(their_trees[i]);
|
||||
|
||||
git__free(their_trees);
|
||||
|
||||
git_merge_head_free(our_head);
|
||||
git_merge_head_free(ancestor_head);
|
||||
|
||||
git_reference_free(our_ref);
|
||||
|
||||
return error;
|
||||
}
|
||||
|
||||
int git_merge__setup(
|
||||
git_repository *repo,
|
||||
const git_merge_head *our_head,
|
||||
@ -2013,7 +2482,7 @@ int git_merge__setup(
|
||||
int error = 0;
|
||||
|
||||
assert (repo && our_head && heads);
|
||||
|
||||
|
||||
if ((error = write_orig_head(repo, our_head)) == 0 &&
|
||||
(error = write_merge_head(repo, heads, heads_len)) == 0 &&
|
||||
(error = write_merge_mode(repo, flags)) == 0) {
|
||||
@ -2056,6 +2525,41 @@ cleanup:
|
||||
return error;
|
||||
}
|
||||
|
||||
/* Merge result data */
|
||||
|
||||
int git_merge_result_is_uptodate(git_merge_result *merge_result)
|
||||
{
|
||||
assert(merge_result);
|
||||
|
||||
return merge_result->is_uptodate;
|
||||
}
|
||||
|
||||
int git_merge_result_is_fastforward(git_merge_result *merge_result)
|
||||
{
|
||||
assert(merge_result);
|
||||
|
||||
return merge_result->is_fastforward;
|
||||
}
|
||||
|
||||
int git_merge_result_fastforward_oid(git_oid *out, git_merge_result *merge_result)
|
||||
{
|
||||
assert(out && merge_result);
|
||||
|
||||
git_oid_cpy(out, &merge_result->fastforward_oid);
|
||||
return 0;
|
||||
}
|
||||
|
||||
void git_merge_result_free(git_merge_result *merge_result)
|
||||
{
|
||||
if (merge_result == NULL)
|
||||
return;
|
||||
|
||||
git_index_free(merge_result->index);
|
||||
merge_result->index = NULL;
|
||||
|
||||
git__free(merge_result);
|
||||
}
|
||||
|
||||
/* Merge heads are the input to merge */
|
||||
|
||||
static int merge_head_init(
|
||||
@ -2087,6 +2591,9 @@ static int merge_head_init(
|
||||
|
||||
git_oid_cpy(&head->oid, oid);
|
||||
|
||||
git_oid_fmt(head->oid_str, oid);
|
||||
head->oid_str[GIT_OID_HEXSZ] = '\0';
|
||||
|
||||
if ((error = git_commit_lookup(&head->commit, repo, &head->oid)) < 0) {
|
||||
git_merge_head_free(head);
|
||||
return error;
|
||||
|
11
src/merge.h
11
src/merge.h
@ -114,9 +114,20 @@ struct git_merge_head {
|
||||
char *remote_url;
|
||||
|
||||
git_oid oid;
|
||||
char oid_str[GIT_OID_HEXSZ+1];
|
||||
git_commit *commit;
|
||||
};
|
||||
|
||||
/** Internal structure for merge results */
|
||||
struct git_merge_result {
|
||||
bool is_uptodate;
|
||||
|
||||
bool is_fastforward;
|
||||
git_oid fastforward_oid;
|
||||
|
||||
git_index *index;
|
||||
};
|
||||
|
||||
int git_merge__bases_many(
|
||||
git_commit_list **out,
|
||||
git_revwalk *walk,
|
||||
|
@ -52,6 +52,29 @@ int merge_trees_from_branches(
|
||||
return 0;
|
||||
}
|
||||
|
||||
int merge_branches(git_merge_result **result, git_repository *repo, const char *ours_branch, const char *theirs_branch, git_merge_opts *opts)
|
||||
{
|
||||
git_reference *head_ref, *theirs_ref;
|
||||
git_merge_head *theirs_head;
|
||||
git_checkout_opts head_checkout_opts = GIT_CHECKOUT_OPTS_INIT;
|
||||
|
||||
head_checkout_opts.checkout_strategy = GIT_CHECKOUT_FORCE;
|
||||
|
||||
cl_git_pass(git_reference_symbolic_create(&head_ref, repo, "HEAD", ours_branch, 1));
|
||||
cl_git_pass(git_checkout_head(repo, &head_checkout_opts));
|
||||
|
||||
cl_git_pass(git_reference_lookup(&theirs_ref, repo, theirs_branch));
|
||||
cl_git_pass(git_merge_head_from_ref(&theirs_head, repo, theirs_ref));
|
||||
|
||||
cl_git_pass(git_merge(result, repo, (const git_merge_head **)&theirs_head, 1, opts));
|
||||
|
||||
git_reference_free(head_ref);
|
||||
git_reference_free(theirs_ref);
|
||||
git_merge_head_free(theirs_head);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
void merge__dump_index_entries(git_vector *index_entries)
|
||||
{
|
||||
size_t i;
|
||||
|
@ -44,6 +44,9 @@ int merge_trees_from_branches(
|
||||
const char *ours_name, const char *theirs_name,
|
||||
git_merge_tree_opts *opts);
|
||||
|
||||
int merge_branches(git_merge_result **result, git_repository *repo,
|
||||
const char *ours_branch, const char *theirs_branch, git_merge_opts *opts);
|
||||
|
||||
int merge_test_diff_list(git_merge_diff_list *diff_list, const struct merge_index_entry expected[], size_t expected_len);
|
||||
|
||||
int merge_test_merge_conflicts(git_vector *conflicts, const struct merge_index_conflict_data expected[], size_t expected_len);
|
||||
|
148
tests-clar/merge/workdir/fastforward.c
Normal file
148
tests-clar/merge/workdir/fastforward.c
Normal file
@ -0,0 +1,148 @@
|
||||
#include "clar_libgit2.h"
|
||||
#include "git2/repository.h"
|
||||
#include "git2/merge.h"
|
||||
#include "git2/sys/index.h"
|
||||
#include "merge.h"
|
||||
#include "../merge_helpers.h"
|
||||
#include "refs.h"
|
||||
|
||||
static git_repository *repo;
|
||||
static git_index *repo_index;
|
||||
|
||||
#define TEST_REPO_PATH "merge-resolve"
|
||||
#define TEST_INDEX_PATH TEST_REPO_PATH "/.git/index"
|
||||
|
||||
#define THEIRS_FASTFORWARD_BRANCH "ff_branch"
|
||||
#define THEIRS_FASTFORWARD_OID "fd89f8cffb663ac89095a0f9764902e93ceaca6a"
|
||||
|
||||
#define THEIRS_NOFASTFORWARD_BRANCH "branch"
|
||||
#define THEIRS_NOFASTFORWARD_OID "7cb63eed597130ba4abb87b3e544b85021905520"
|
||||
|
||||
|
||||
// Fixture setup and teardown
|
||||
void test_merge_workdir_fastforward__initialize(void)
|
||||
{
|
||||
repo = cl_git_sandbox_init(TEST_REPO_PATH);
|
||||
git_repository_index(&repo_index, repo);
|
||||
}
|
||||
|
||||
void test_merge_workdir_fastforward__cleanup(void)
|
||||
{
|
||||
git_index_free(repo_index);
|
||||
cl_git_sandbox_cleanup();
|
||||
}
|
||||
|
||||
static git_merge_result *merge_fastforward_branch(int flags)
|
||||
{
|
||||
git_reference *their_ref;
|
||||
git_merge_head *their_heads[1];
|
||||
git_merge_result *result;
|
||||
git_merge_opts opts = GIT_MERGE_OPTS_INIT;
|
||||
|
||||
opts.merge_flags = flags;
|
||||
|
||||
cl_git_pass(git_reference_lookup(&their_ref, repo, GIT_REFS_HEADS_DIR THEIRS_FASTFORWARD_BRANCH));
|
||||
cl_git_pass(git_merge_head_from_ref(&their_heads[0], repo, their_ref));
|
||||
|
||||
cl_git_pass(git_merge(&result, repo, (const git_merge_head **)their_heads, 1, &opts));
|
||||
|
||||
git_merge_head_free(their_heads[0]);
|
||||
git_reference_free(their_ref);
|
||||
|
||||
return result;
|
||||
}
|
||||
|
||||
void test_merge_workdir_fastforward__fastforward(void)
|
||||
{
|
||||
git_merge_result *result;
|
||||
git_oid expected, ff_oid;
|
||||
|
||||
cl_git_pass(git_oid_fromstr(&expected, THEIRS_FASTFORWARD_OID));
|
||||
|
||||
cl_assert(result = merge_fastforward_branch(0));
|
||||
cl_assert(git_merge_result_is_fastforward(result));
|
||||
cl_git_pass(git_merge_result_fastforward_oid(&ff_oid, result));
|
||||
cl_assert(git_oid_cmp(&ff_oid, &expected) == 0);
|
||||
|
||||
git_merge_result_free(result);
|
||||
}
|
||||
|
||||
void test_merge_workdir_fastforward__fastforward_only(void)
|
||||
{
|
||||
git_merge_result *result;
|
||||
git_merge_opts opts = GIT_MERGE_OPTS_INIT;
|
||||
git_reference *their_ref;
|
||||
git_merge_head *their_head;
|
||||
int error;
|
||||
|
||||
opts.merge_flags = GIT_MERGE_FASTFORWARD_ONLY;
|
||||
|
||||
cl_git_pass(git_reference_lookup(&their_ref, repo, GIT_REFS_HEADS_DIR THEIRS_NOFASTFORWARD_BRANCH));
|
||||
cl_git_pass(git_merge_head_from_ref(&their_head, repo, their_ref));
|
||||
|
||||
cl_git_fail((error = git_merge(&result, repo, (const git_merge_head **)&their_head, 1, &opts)));
|
||||
cl_assert(error == GIT_ENONFASTFORWARD);
|
||||
|
||||
git_merge_head_free(their_head);
|
||||
git_reference_free(their_ref);
|
||||
}
|
||||
|
||||
void test_merge_workdir_fastforward__no_fastforward(void)
|
||||
{
|
||||
git_merge_result *result;
|
||||
|
||||
struct merge_index_entry merge_index_entries[] = {
|
||||
{ 0100644, "233c0919c998ed110a4b6ff36f353aec8b713487", 0, "added-in-master.txt" },
|
||||
{ 0100644, "ee3fa1b8c00aff7fe02065fdb50864bb0d932ccf", 0, "automergeable.txt" },
|
||||
{ 0100644, "ab6c44a2e84492ad4b41bb6bac87353e9d02ac8b", 0, "changed-in-branch.txt" },
|
||||
{ 0100644, "bd9cb4cd0a770cb9adcb5fce212142ef40ea1c35", 0, "changed-in-master.txt" },
|
||||
{ 0100644, "4e886e602529caa9ab11d71f86634bd1b6e0de10", 0, "conflicting.txt" },
|
||||
{ 0100644, "364bbe4ce80c7bd31e6307dce77d46e3e1759fb3", 0, "new-in-ff.txt" },
|
||||
{ 0100644, "dfe3f22baa1f6fce5447901c3086bae368de6bdd", 0, "removed-in-branch.txt" },
|
||||
{ 0100644, "c8f06f2e3bb2964174677e91f0abead0e43c9e5d", 0, "unchanged.txt" },
|
||||
};
|
||||
|
||||
cl_assert(result = merge_fastforward_branch(GIT_MERGE_NO_FASTFORWARD));
|
||||
cl_assert(!git_merge_result_is_fastforward(result));
|
||||
|
||||
cl_assert(merge_test_index(repo_index, merge_index_entries, 8));
|
||||
cl_assert(git_index_reuc_entrycount(repo_index) == 0);
|
||||
|
||||
git_merge_result_free(result);
|
||||
}
|
||||
|
||||
void test_merge_workdir_fastforward__uptodate(void)
|
||||
{
|
||||
git_reference *their_ref;
|
||||
git_merge_head *their_heads[1];
|
||||
git_merge_result *result;
|
||||
|
||||
cl_git_pass(git_reference_lookup(&their_ref, repo, GIT_HEAD_FILE));
|
||||
cl_git_pass(git_merge_head_from_ref(&their_heads[0], repo, their_ref));
|
||||
|
||||
cl_git_pass(git_merge(&result, repo, (const git_merge_head **)their_heads, 1, NULL));
|
||||
|
||||
cl_assert(git_merge_result_is_uptodate(result));
|
||||
|
||||
git_merge_head_free(their_heads[0]);
|
||||
git_reference_free(their_ref);
|
||||
git_merge_result_free(result);
|
||||
}
|
||||
|
||||
void test_merge_workdir_fastforward__uptodate_merging_prev_commit(void)
|
||||
{
|
||||
git_oid their_oid;
|
||||
git_merge_head *their_heads[1];
|
||||
git_merge_result *result;
|
||||
|
||||
cl_git_pass(git_oid_fromstr(&their_oid, "c607fc30883e335def28cd686b51f6cfa02b06ec"));
|
||||
cl_git_pass(git_merge_head_from_oid(&their_heads[0], repo, &their_oid));
|
||||
|
||||
cl_git_pass(git_merge(&result, repo, (const git_merge_head **)their_heads, 1, NULL));
|
||||
|
||||
cl_assert(git_merge_result_is_uptodate(result));
|
||||
|
||||
git_merge_head_free(their_heads[0]);
|
||||
git_merge_result_free(result);
|
||||
}
|
||||
|
156
tests-clar/merge/workdir/renames.c
Normal file
156
tests-clar/merge/workdir/renames.c
Normal file
@ -0,0 +1,156 @@
|
||||
#include "clar_libgit2.h"
|
||||
#include "git2/repository.h"
|
||||
#include "git2/merge.h"
|
||||
#include "buffer.h"
|
||||
#include "merge.h"
|
||||
#include "../merge_helpers.h"
|
||||
#include "fileops.h"
|
||||
#include "refs.h"
|
||||
|
||||
static git_repository *repo;
|
||||
|
||||
#define TEST_REPO_PATH "merge-resolve"
|
||||
|
||||
#define BRANCH_RENAME_OURS "rename_conflict_ours"
|
||||
#define BRANCH_RENAME_THEIRS "rename_conflict_theirs"
|
||||
|
||||
// Fixture setup and teardown
|
||||
void test_merge_workdir_renames__initialize(void)
|
||||
{
|
||||
repo = cl_git_sandbox_init(TEST_REPO_PATH);
|
||||
}
|
||||
|
||||
void test_merge_workdir_renames__cleanup(void)
|
||||
{
|
||||
cl_git_sandbox_cleanup();
|
||||
}
|
||||
|
||||
void test_merge_workdir_renames__renames(void)
|
||||
{
|
||||
git_merge_result *result;
|
||||
git_merge_opts opts = GIT_MERGE_OPTS_INIT;
|
||||
|
||||
struct merge_index_entry merge_index_entries[] = {
|
||||
{ 0100644, "68c6c84b091926c7d90aa6a79b2bc3bb6adccd8e", 0, "0a-no-change.txt" },
|
||||
{ 0100644, "f0ce2b8e4986084d9b308fb72709e414c23eb5e6", 0, "0b-duplicated-in-ours.txt" },
|
||||
{ 0100644, "8aac75de2a34b4d340bf62a6e58197269cb55797", 0, "0b-rewritten-in-ours.txt" },
|
||||
{ 0100644, "2f56120107d680129a5d9791b521cb1e73a2ed31", 0, "0c-duplicated-in-theirs.txt" },
|
||||
{ 0100644, "7edc726325da726751a4195e434e4377b0f67f9a", 0, "0c-rewritten-in-theirs.txt" },
|
||||
{ 0100644, "0d872f8e871a30208305978ecbf9e66d864f1638", 0, "1a-newname-in-ours-edited-in-theirs.txt" },
|
||||
{ 0100644, "d0d4594e16f2e19107e3fa7ea63e7aaaff305ffb", 0, "1a-newname-in-ours.txt" },
|
||||
{ 0100644, "ed9523e62e453e50dd9be1606af19399b96e397a", 0, "1b-newname-in-theirs-edited-in-ours.txt" },
|
||||
{ 0100644, "2b5f1f181ee3b58ea751f5dd5d8f9b445520a136", 0, "1b-newname-in-theirs.txt" },
|
||||
{ 0100644, "178940b450f238a56c0d75b7955cb57b38191982", 0, "2-newname-in-both.txt" },
|
||||
{ 0100644, "18cb316b1cefa0f8a6946f0e201a8e1a6f845ab9", 0, "3a-newname-in-ours-deleted-in-theirs.txt" },
|
||||
{ 0100644, "36219b49367146cb2e6a1555b5a9ebd4d0328495", 0, "3b-newname-in-theirs-deleted-in-ours.txt" },
|
||||
{ 0100644, "227792b52aaa0b238bea00ec7e509b02623f168c", 0, "4a-newname-in-ours-added-in-theirs.txt~HEAD" },
|
||||
{ 0100644, "8b5b53cb2aa9ceb1139f5312fcfa3cc3c5a47c9a", 0, "4a-newname-in-ours-added-in-theirs.txt~rename_conflict_theirs" },
|
||||
{ 0100644, "de872ee3618b894992e9d1e18ba2ebe256a112f9", 0, "4b-newname-in-theirs-added-in-ours.txt~HEAD" },
|
||||
{ 0100644, "98d52d07c0b0bbf2b46548f6aa521295c2cb55db", 0, "4b-newname-in-theirs-added-in-ours.txt~rename_conflict_theirs" },
|
||||
{ 0100644, "d3719a5ae8e4d92276b5313ce976f6ee5af2b436", 0, "5a-newname-in-ours-added-in-theirs.txt~HEAD" },
|
||||
{ 0100644, "98ba4205fcf31f5dd93c916d35fe3f3b3d0e6714", 0, "5a-newname-in-ours-added-in-theirs.txt~rename_conflict_theirs" },
|
||||
{ 0100644, "385c8a0f26ddf79e9041e15e17dc352ed2c4cced", 0, "5b-newname-in-theirs-added-in-ours.txt~HEAD" },
|
||||
{ 0100644, "63247125386de9ec90a27ad36169307bf8a11a38", 0, "5b-newname-in-theirs-added-in-ours.txt~rename_conflict_theirs" },
|
||||
{ 0100644, "d8fa77b6833082c1ea36b7828a582d4c43882450", 0, "6-both-renamed-1-to-2-ours.txt" },
|
||||
{ 0100644, "d8fa77b6833082c1ea36b7828a582d4c43882450", 0, "6-both-renamed-1-to-2-theirs.txt" },
|
||||
{ 0100644, "b42712cfe99a1a500b2a51fe984e0b8a7702ba11", 0, "7-both-renamed.txt~HEAD" },
|
||||
{ 0100644, "b69fe837e4cecfd4c9a40cdca7c138468687df07", 0, "7-both-renamed.txt~rename_conflict_theirs" },
|
||||
};
|
||||
|
||||
opts.merge_tree_opts.flags |= GIT_MERGE_TREE_FIND_RENAMES;
|
||||
opts.merge_tree_opts.rename_threshold = 50;
|
||||
|
||||
cl_git_pass(merge_branches(&result, repo, GIT_REFS_HEADS_DIR BRANCH_RENAME_OURS, GIT_REFS_HEADS_DIR BRANCH_RENAME_THEIRS, &opts));
|
||||
cl_assert(merge_test_workdir(repo, merge_index_entries, 24));
|
||||
|
||||
git_merge_result_free(result);
|
||||
}
|
||||
|
||||
void test_merge_workdir_renames__ours(void)
|
||||
{
|
||||
git_index *index;
|
||||
git_merge_result *result;
|
||||
git_merge_opts opts = GIT_MERGE_OPTS_INIT;
|
||||
|
||||
struct merge_index_entry merge_index_entries[] = {
|
||||
{ 0100644, "68c6c84b091926c7d90aa6a79b2bc3bb6adccd8e", 0, "0a-no-change.txt" },
|
||||
{ 0100644, "f0ce2b8e4986084d9b308fb72709e414c23eb5e6", 0, "0b-duplicated-in-ours.txt" },
|
||||
{ 0100644, "e376fbdd06ebf021c92724da9f26f44212734e3e", 0, "0b-rewritten-in-ours.txt" },
|
||||
{ 0100644, "2f56120107d680129a5d9791b521cb1e73a2ed31", 0, "0c-duplicated-in-theirs.txt" },
|
||||
{ 0100644, "efc9121fdedaf08ba180b53ebfbcf71bd488ed09", 0, "0c-rewritten-in-theirs.txt" },
|
||||
{ 0100644, "0d872f8e871a30208305978ecbf9e66d864f1638", 0, "1a-newname-in-ours-edited-in-theirs.txt" },
|
||||
{ 0100644, "d0d4594e16f2e19107e3fa7ea63e7aaaff305ffb", 0, "1a-newname-in-ours.txt" },
|
||||
{ 0100644, "ed9523e62e453e50dd9be1606af19399b96e397a", 0, "1b-newname-in-theirs-edited-in-ours.txt" },
|
||||
{ 0100644, "2b5f1f181ee3b58ea751f5dd5d8f9b445520a136", 0, "1b-newname-in-theirs.txt" },
|
||||
{ 0100644, "178940b450f238a56c0d75b7955cb57b38191982", 0, "2-newname-in-both.txt" },
|
||||
{ 0100644, "18cb316b1cefa0f8a6946f0e201a8e1a6f845ab9", 0, "3a-newname-in-ours-deleted-in-theirs.txt" },
|
||||
{ 0100644, "36219b49367146cb2e6a1555b5a9ebd4d0328495", 0, "3b-newname-in-theirs-deleted-in-ours.txt" },
|
||||
{ 0100644, "227792b52aaa0b238bea00ec7e509b02623f168c", 0, "4a-newname-in-ours-added-in-theirs.txt" },
|
||||
{ 0100644, "de872ee3618b894992e9d1e18ba2ebe256a112f9", 0, "4b-newname-in-theirs-added-in-ours.txt" },
|
||||
{ 0100644, "d3719a5ae8e4d92276b5313ce976f6ee5af2b436", 0, "5a-newname-in-ours-added-in-theirs.txt" },
|
||||
{ 0100644, "385c8a0f26ddf79e9041e15e17dc352ed2c4cced", 0, "5b-newname-in-theirs-added-in-ours.txt" },
|
||||
{ 0100644, "63247125386de9ec90a27ad36169307bf8a11a38", 0, "5b-renamed-in-theirs-added-in-ours.txt" },
|
||||
{ 0100644, "d8fa77b6833082c1ea36b7828a582d4c43882450", 0, "6-both-renamed-1-to-2-ours.txt" },
|
||||
{ 0100644, "b69fe837e4cecfd4c9a40cdca7c138468687df07", 0, "7-both-renamed-side-2.txt" },
|
||||
{ 0100644, "b42712cfe99a1a500b2a51fe984e0b8a7702ba11", 0, "7-both-renamed.txt" },
|
||||
};
|
||||
|
||||
opts.merge_tree_opts.flags |= GIT_MERGE_TREE_FIND_RENAMES;
|
||||
opts.merge_tree_opts.rename_threshold = 50;
|
||||
opts.checkout_opts.checkout_strategy = GIT_CHECKOUT_SAFE | GIT_CHECKOUT_USE_OURS;
|
||||
|
||||
cl_git_pass(merge_branches(&result, repo, GIT_REFS_HEADS_DIR BRANCH_RENAME_OURS, GIT_REFS_HEADS_DIR BRANCH_RENAME_THEIRS, &opts));
|
||||
cl_git_pass(git_repository_index(&index, repo));
|
||||
cl_git_pass(git_index_write(index));
|
||||
cl_assert(merge_test_workdir(repo, merge_index_entries, 20));
|
||||
|
||||
git_merge_result_free(result);
|
||||
git_index_free(index);
|
||||
}
|
||||
|
||||
void test_merge_workdir_renames__similar(void)
|
||||
{
|
||||
git_merge_result *result;
|
||||
git_merge_opts opts = GIT_MERGE_OPTS_INIT;
|
||||
|
||||
/*
|
||||
* Note: this differs slightly from the core git merge result - there, 4a is
|
||||
* tracked as a rename/delete instead of a rename/add and the theirs side
|
||||
* is not placed in workdir in any form.
|
||||
*/
|
||||
struct merge_index_entry merge_index_entries[] = {
|
||||
{ 0100644, "68c6c84b091926c7d90aa6a79b2bc3bb6adccd8e", 0, "0a-no-change.txt" },
|
||||
{ 0100644, "f0ce2b8e4986084d9b308fb72709e414c23eb5e6", 0, "0b-duplicated-in-ours.txt" },
|
||||
{ 0100644, "8aac75de2a34b4d340bf62a6e58197269cb55797", 0, "0b-rewritten-in-ours.txt" },
|
||||
{ 0100644, "2f56120107d680129a5d9791b521cb1e73a2ed31", 0, "0c-duplicated-in-theirs.txt" },
|
||||
{ 0100644, "7edc726325da726751a4195e434e4377b0f67f9a", 0, "0c-rewritten-in-theirs.txt" },
|
||||
{ 0100644, "0d872f8e871a30208305978ecbf9e66d864f1638", 0, "1a-newname-in-ours-edited-in-theirs.txt" },
|
||||
{ 0100644, "d0d4594e16f2e19107e3fa7ea63e7aaaff305ffb", 0, "1a-newname-in-ours.txt" },
|
||||
{ 0100644, "ed9523e62e453e50dd9be1606af19399b96e397a", 0, "1b-newname-in-theirs-edited-in-ours.txt" },
|
||||
{ 0100644, "2b5f1f181ee3b58ea751f5dd5d8f9b445520a136", 0, "1b-newname-in-theirs.txt" },
|
||||
{ 0100644, "178940b450f238a56c0d75b7955cb57b38191982", 0, "2-newname-in-both.txt" },
|
||||
{ 0100644, "18cb316b1cefa0f8a6946f0e201a8e1a6f845ab9", 0, "3a-newname-in-ours-deleted-in-theirs.txt" },
|
||||
{ 0100644, "36219b49367146cb2e6a1555b5a9ebd4d0328495", 0, "3b-newname-in-theirs-deleted-in-ours.txt" },
|
||||
{ 0100644, "227792b52aaa0b238bea00ec7e509b02623f168c", 0, "4a-newname-in-ours-added-in-theirs.txt~HEAD" },
|
||||
{ 0100644, "8b5b53cb2aa9ceb1139f5312fcfa3cc3c5a47c9a", 0, "4a-newname-in-ours-added-in-theirs.txt~rename_conflict_theirs" },
|
||||
{ 0100644, "de872ee3618b894992e9d1e18ba2ebe256a112f9", 0, "4b-newname-in-theirs-added-in-ours.txt~HEAD" },
|
||||
{ 0100644, "98d52d07c0b0bbf2b46548f6aa521295c2cb55db", 0, "4b-newname-in-theirs-added-in-ours.txt~rename_conflict_theirs" },
|
||||
{ 0100644, "d3719a5ae8e4d92276b5313ce976f6ee5af2b436", 0, "5a-newname-in-ours-added-in-theirs.txt~HEAD" },
|
||||
{ 0100644, "98ba4205fcf31f5dd93c916d35fe3f3b3d0e6714", 0, "5a-newname-in-ours-added-in-theirs.txt~rename_conflict_theirs" },
|
||||
{ 0100644, "385c8a0f26ddf79e9041e15e17dc352ed2c4cced", 0, "5b-newname-in-theirs-added-in-ours.txt~HEAD" },
|
||||
{ 0100644, "63247125386de9ec90a27ad36169307bf8a11a38", 0, "5b-newname-in-theirs-added-in-ours.txt~rename_conflict_theirs" },
|
||||
{ 0100644, "d8fa77b6833082c1ea36b7828a582d4c43882450", 0, "6-both-renamed-1-to-2-ours.txt" },
|
||||
{ 0100644, "d8fa77b6833082c1ea36b7828a582d4c43882450", 0, "6-both-renamed-1-to-2-theirs.txt" },
|
||||
{ 0100644, "b42712cfe99a1a500b2a51fe984e0b8a7702ba11", 0, "7-both-renamed.txt~HEAD" },
|
||||
{ 0100644, "b69fe837e4cecfd4c9a40cdca7c138468687df07", 0, "7-both-renamed.txt~rename_conflict_theirs" },
|
||||
};
|
||||
|
||||
opts.merge_tree_opts.flags |= GIT_MERGE_TREE_FIND_RENAMES;
|
||||
opts.merge_tree_opts.rename_threshold = 50;
|
||||
|
||||
cl_git_pass(merge_branches(&result, repo, GIT_REFS_HEADS_DIR BRANCH_RENAME_OURS, GIT_REFS_HEADS_DIR BRANCH_RENAME_THEIRS, &opts));
|
||||
cl_assert(merge_test_workdir(repo, merge_index_entries, 24));
|
||||
|
||||
git_merge_result_free(result);
|
||||
}
|
||||
|
@ -71,7 +71,7 @@ static void write_file_contents(const char *filename, const char *output)
|
||||
git_buf_free(&file_path_buf);
|
||||
}
|
||||
|
||||
/* git merge --no-ff octo1 */
|
||||
/* git merge octo1 */
|
||||
void test_merge_workdir_setup__one_branch(void)
|
||||
{
|
||||
git_oid our_oid;
|
||||
@ -97,7 +97,33 @@ void test_merge_workdir_setup__one_branch(void)
|
||||
git_merge_head_free(their_heads[0]);
|
||||
}
|
||||
|
||||
/* git merge --no-ff 16f825815cfd20a07a75c71554e82d8eede0b061 */
|
||||
/* git merge --no-ff octo1 */
|
||||
void test_merge_workdir_setup__no_fastforward(void)
|
||||
{
|
||||
git_oid our_oid;
|
||||
git_reference *octo1_ref;
|
||||
git_merge_head *our_head, *their_heads[1];
|
||||
|
||||
cl_git_pass(git_oid_fromstr(&our_oid, ORIG_HEAD));
|
||||
cl_git_pass(git_merge_head_from_oid(&our_head, repo, &our_oid));
|
||||
|
||||
cl_git_pass(git_reference_lookup(&octo1_ref, repo, GIT_REFS_HEADS_DIR OCTO1_BRANCH));
|
||||
cl_git_pass(git_merge_head_from_ref(&their_heads[0], repo, octo1_ref));
|
||||
|
||||
cl_git_pass(git_merge__setup(repo, our_head, (const git_merge_head **)their_heads, 1, GIT_MERGE_NO_FASTFORWARD));
|
||||
|
||||
cl_assert(test_file_contents(GIT_MERGE_HEAD_FILE, OCTO1_OID "\n"));
|
||||
cl_assert(test_file_contents(GIT_ORIG_HEAD_FILE, ORIG_HEAD "\n"));
|
||||
cl_assert(test_file_contents(GIT_MERGE_MODE_FILE, "no-ff"));
|
||||
cl_assert(test_file_contents(GIT_MERGE_MSG_FILE, "Merge branch '" OCTO1_BRANCH "'\n"));
|
||||
|
||||
git_reference_free(octo1_ref);
|
||||
|
||||
git_merge_head_free(our_head);
|
||||
git_merge_head_free(their_heads[0]);
|
||||
}
|
||||
|
||||
/* git merge 16f825815cfd20a07a75c71554e82d8eede0b061 */
|
||||
void test_merge_workdir_setup__one_oid(void)
|
||||
{
|
||||
git_oid our_oid;
|
||||
@ -964,3 +990,65 @@ void test_merge_workdir_setup__head_foreach_octopus(void)
|
||||
|
||||
cl_assert(cb_data.i == cb_data.len);
|
||||
}
|
||||
|
||||
void test_merge_workdir_setup__retained_after_success(void)
|
||||
{
|
||||
git_oid our_oid;
|
||||
git_reference *octo1_ref;
|
||||
git_merge_head *our_head, *their_heads[1];
|
||||
git_merge_result *result;
|
||||
git_merge_opts opts = GIT_MERGE_OPTS_INIT;
|
||||
|
||||
opts.merge_flags |= GIT_MERGE_NO_FASTFORWARD;
|
||||
|
||||
cl_git_pass(git_oid_fromstr(&our_oid, ORIG_HEAD));
|
||||
cl_git_pass(git_merge_head_from_oid(&our_head, repo, &our_oid));
|
||||
|
||||
cl_git_pass(git_reference_lookup(&octo1_ref, repo, GIT_REFS_HEADS_DIR OCTO1_BRANCH));
|
||||
|
||||
cl_git_pass(git_merge_head_from_ref(&their_heads[0], repo, octo1_ref));
|
||||
|
||||
cl_git_pass(git_merge(&result, repo, (const git_merge_head **)&their_heads[0], 1, &opts));
|
||||
|
||||
cl_assert(test_file_contents(GIT_MERGE_HEAD_FILE, OCTO1_OID "\n"));
|
||||
cl_assert(test_file_contents(GIT_ORIG_HEAD_FILE, ORIG_HEAD "\n"));
|
||||
cl_assert(test_file_contents(GIT_MERGE_MODE_FILE, "no-ff"));
|
||||
cl_assert(test_file_contents(GIT_MERGE_MSG_FILE, "Merge branch '" OCTO1_BRANCH "'\n"));
|
||||
|
||||
git_reference_free(octo1_ref);
|
||||
|
||||
git_merge_head_free(our_head);
|
||||
git_merge_head_free(their_heads[0]);
|
||||
}
|
||||
|
||||
void test_merge_workdir_setup__removed_after_failure(void)
|
||||
{
|
||||
git_oid our_oid;
|
||||
git_reference *octo1_ref;
|
||||
git_merge_head *our_head, *their_heads[1];
|
||||
git_merge_result *result;
|
||||
git_merge_opts opts = GIT_MERGE_OPTS_INIT;
|
||||
|
||||
opts.merge_flags |= GIT_MERGE_NO_FASTFORWARD;
|
||||
|
||||
cl_git_pass(git_oid_fromstr(&our_oid, ORIG_HEAD));
|
||||
cl_git_pass(git_merge_head_from_oid(&our_head, repo, &our_oid));
|
||||
|
||||
cl_git_pass(git_reference_lookup(&octo1_ref, repo, GIT_REFS_HEADS_DIR OCTO1_BRANCH));
|
||||
cl_git_pass(git_merge_head_from_ref(&their_heads[0], repo, octo1_ref));
|
||||
|
||||
cl_git_rewritefile("merge-resolve/new-in-octo1.txt",
|
||||
"Conflicting file!\n\nMerge will fail!\n");
|
||||
|
||||
cl_git_fail(git_merge(&result, repo, &their_heads[0], 1, &opts));
|
||||
|
||||
cl_assert(!git_path_exists("merge-resolve/" GIT_MERGE_HEAD_FILE));
|
||||
cl_assert(!git_path_exists("merge-resolve/" GIT_ORIG_HEAD_FILE));
|
||||
cl_assert(!git_path_exists("merge-resolve/" GIT_MERGE_MODE_FILE));
|
||||
cl_assert(!git_path_exists("merge-resolve/" GIT_MERGE_MSG_FILE));
|
||||
|
||||
git_reference_free(octo1_ref);
|
||||
|
||||
git_merge_head_free(our_head);
|
||||
git_merge_head_free(their_heads[0]);
|
||||
}
|
||||
|
491
tests-clar/merge/workdir/simple.c
Normal file
491
tests-clar/merge/workdir/simple.c
Normal file
@ -0,0 +1,491 @@
|
||||
#include "clar_libgit2.h"
|
||||
#include "git2/repository.h"
|
||||
#include "git2/merge.h"
|
||||
#include "buffer.h"
|
||||
#include "merge.h"
|
||||
#include "../merge_helpers.h"
|
||||
#include "refs.h"
|
||||
#include "fileops.h"
|
||||
|
||||
static git_repository *repo;
|
||||
static git_index *repo_index;
|
||||
|
||||
#define TEST_REPO_PATH "merge-resolve"
|
||||
#define TEST_INDEX_PATH TEST_REPO_PATH "/.git/index"
|
||||
|
||||
#define THEIRS_SIMPLE_BRANCH "branch"
|
||||
#define THEIRS_SIMPLE_OID "7cb63eed597130ba4abb87b3e544b85021905520"
|
||||
|
||||
#define THEIRS_UNRELATED_BRANCH "unrelated"
|
||||
#define THEIRS_UNRELATED_OID "55b4e4687e7a0d9ca367016ed930f385d4022e6f"
|
||||
#define THEIRS_UNRELATED_PARENT "d6cf6c7741b3316826af1314042550c97ded1d50"
|
||||
|
||||
#define OURS_DIRECTORY_FILE "df_side1"
|
||||
#define THEIRS_DIRECTORY_FILE "fc90237dc4891fa6c69827fc465632225e391618"
|
||||
|
||||
|
||||
/* Non-conflicting files, index entries are common to every merge operation */
|
||||
#define ADDED_IN_MASTER_INDEX_ENTRY \
|
||||
{ 0100644, "233c0919c998ed110a4b6ff36f353aec8b713487", 0, \
|
||||
"added-in-master.txt" }
|
||||
#define AUTOMERGEABLE_INDEX_ENTRY \
|
||||
{ 0100644, "f2e1550a0c9e53d5811175864a29536642ae3821", 0, \
|
||||
"automergeable.txt" }
|
||||
#define CHANGED_IN_BRANCH_INDEX_ENTRY \
|
||||
{ 0100644, "4eb04c9e79e88f6640d01ff5b25ca2a60764f216", 0, \
|
||||
"changed-in-branch.txt" }
|
||||
#define CHANGED_IN_MASTER_INDEX_ENTRY \
|
||||
{ 0100644, "11deab00b2d3a6f5a3073988ac050c2d7b6655e2", 0, \
|
||||
"changed-in-master.txt" }
|
||||
#define UNCHANGED_INDEX_ENTRY \
|
||||
{ 0100644, "c8f06f2e3bb2964174677e91f0abead0e43c9e5d", 0, \
|
||||
"unchanged.txt" }
|
||||
|
||||
/* Unrelated files */
|
||||
#define UNRELATED_NEW1 \
|
||||
{ 0100644, "ef58fdd8086c243bdc81f99e379acacfd21d32d6", 0, \
|
||||
"new-in-unrelated1.txt" }
|
||||
#define UNRELATED_NEW2 \
|
||||
{ 0100644, "948ba6e701c1edab0c2d394fb7c5538334129793", 0, \
|
||||
"new-in-unrelated2.txt" }
|
||||
|
||||
/* Expected REUC entries */
|
||||
#define AUTOMERGEABLE_REUC_ENTRY \
|
||||
{ "automergeable.txt", 0100644, 0100644, 0100644, \
|
||||
"6212c31dab5e482247d7977e4f0dd3601decf13b", \
|
||||
"ee3fa1b8c00aff7fe02065fdb50864bb0d932ccf", \
|
||||
"058541fc37114bfc1dddf6bd6bffc7fae5c2e6fe" }
|
||||
#define CONFLICTING_REUC_ENTRY \
|
||||
{ "conflicting.txt", 0100644, 0100644, 0100644, \
|
||||
"d427e0b2e138501a3d15cc376077a3631e15bd46", \
|
||||
"4e886e602529caa9ab11d71f86634bd1b6e0de10", \
|
||||
"2bd0a343aeef7a2cf0d158478966a6e587ff3863" }
|
||||
#define REMOVED_IN_BRANCH_REUC_ENTRY \
|
||||
{ "removed-in-branch.txt", 0100644, 0100644, 0, \
|
||||
"dfe3f22baa1f6fce5447901c3086bae368de6bdd", \
|
||||
"dfe3f22baa1f6fce5447901c3086bae368de6bdd", \
|
||||
"" }
|
||||
#define REMOVED_IN_MASTER_REUC_ENTRY \
|
||||
{ "removed-in-master.txt", 0100644, 0, 0100644, \
|
||||
"5c3b68a71fc4fa5d362fd3875e53137c6a5ab7a5", \
|
||||
"", \
|
||||
"5c3b68a71fc4fa5d362fd3875e53137c6a5ab7a5" }
|
||||
|
||||
#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_DIFF3_FILE \
|
||||
"<<<<<<< HEAD\n" \
|
||||
"this file is changed in master and branch\n" \
|
||||
"=======\n" \
|
||||
"this file is changed in branch and master\n" \
|
||||
">>>>>>> 7cb63eed597130ba4abb87b3e544b85021905520\n"
|
||||
|
||||
// Fixture setup and teardown
|
||||
void test_merge_workdir_simple__initialize(void)
|
||||
{
|
||||
repo = cl_git_sandbox_init(TEST_REPO_PATH);
|
||||
git_repository_index(&repo_index, repo);
|
||||
}
|
||||
|
||||
void test_merge_workdir_simple__cleanup(void)
|
||||
{
|
||||
git_index_free(repo_index);
|
||||
cl_git_sandbox_cleanup();
|
||||
}
|
||||
|
||||
static git_merge_result *merge_simple_branch(int automerge_flags, int checkout_strategy)
|
||||
{
|
||||
git_oid their_oids[1];
|
||||
git_merge_head *their_heads[1];
|
||||
git_merge_result *result;
|
||||
git_merge_opts opts = GIT_MERGE_OPTS_INIT;
|
||||
|
||||
cl_git_pass(git_oid_fromstr(&their_oids[0], THEIRS_SIMPLE_OID));
|
||||
cl_git_pass(git_merge_head_from_oid(&their_heads[0], repo, &their_oids[0]));
|
||||
|
||||
opts.merge_tree_opts.automerge_flags = automerge_flags;
|
||||
opts.checkout_opts.checkout_strategy = checkout_strategy;
|
||||
cl_git_pass(git_merge(&result, repo, (const git_merge_head **)their_heads, 1, &opts));
|
||||
|
||||
git_merge_head_free(their_heads[0]);
|
||||
|
||||
return result;
|
||||
}
|
||||
|
||||
static void set_core_autocrlf_to(git_repository *repo, bool value)
|
||||
{
|
||||
git_config *cfg;
|
||||
|
||||
cl_git_pass(git_repository_config(&cfg, repo));
|
||||
cl_git_pass(git_config_set_bool(cfg, "core.autocrlf", value));
|
||||
|
||||
git_config_free(cfg);
|
||||
}
|
||||
|
||||
void test_merge_workdir_simple__automerge(void)
|
||||
{
|
||||
git_index *index;
|
||||
const git_index_entry *entry;
|
||||
git_merge_result *result;
|
||||
git_buf automergeable_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
|
||||
};
|
||||
|
||||
|
||||
set_core_autocrlf_to(repo, false);
|
||||
|
||||
cl_assert(result = merge_simple_branch(0, 0));
|
||||
cl_assert(!git_merge_result_is_fastforward(result));
|
||||
|
||||
cl_git_pass(git_futils_readbuffer(&automergeable_buf,
|
||||
TEST_REPO_PATH "/automergeable.txt"));
|
||||
cl_assert(strcmp(automergeable_buf.ptr, AUTOMERGEABLE_MERGED_FILE) == 0);
|
||||
git_buf_free(&automergeable_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);
|
||||
|
||||
git_repository_index(&index, repo);
|
||||
|
||||
cl_assert((entry = git_index_get_bypath(index, "automergeable.txt", 0)) != NULL);
|
||||
cl_assert(entry->file_size == strlen(AUTOMERGEABLE_MERGED_FILE));
|
||||
|
||||
git_index_free(index);
|
||||
}
|
||||
|
||||
void test_merge_workdir_simple__automerge_crlf(void)
|
||||
{
|
||||
#ifdef GIT_WIN32
|
||||
git_index *index;
|
||||
const git_index_entry *entry;
|
||||
|
||||
git_merge_result *result;
|
||||
git_buf automergeable_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
|
||||
};
|
||||
|
||||
set_core_autocrlf_to(repo, true);
|
||||
|
||||
cl_assert(result = merge_simple_branch(0, 0));
|
||||
cl_assert(!git_merge_result_is_fastforward(result));
|
||||
|
||||
cl_git_pass(git_futils_readbuffer(&automergeable_buf,
|
||||
TEST_REPO_PATH "/automergeable.txt"));
|
||||
cl_assert(strcmp(automergeable_buf.ptr, AUTOMERGEABLE_MERGED_FILE_CRLF) == 0);
|
||||
git_buf_free(&automergeable_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);
|
||||
|
||||
git_repository_index(&index, repo);
|
||||
|
||||
cl_assert((entry = git_index_get_bypath(index, "automergeable.txt", 0)) != NULL);
|
||||
cl_assert(entry->file_size == strlen(AUTOMERGEABLE_MERGED_FILE_CRLF));
|
||||
|
||||
git_index_free(index);
|
||||
#endif /* GIT_WIN32 */
|
||||
}
|
||||
|
||||
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, 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_DIFF3_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__checkout_ours(void)
|
||||
{
|
||||
git_merge_result *result;
|
||||
|
||||
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_SAFE | GIT_CHECKOUT_USE_OURS));
|
||||
cl_assert(!git_merge_result_is_fastforward(result));
|
||||
|
||||
cl_assert(merge_test_index(repo_index, merge_index_entries, 8));
|
||||
cl_assert(merge_test_reuc(repo_index, merge_reuc_entries, 3));
|
||||
|
||||
cl_assert(git_path_exists(TEST_REPO_PATH "/conflicting.txt"));
|
||||
|
||||
git_merge_result_free(result);
|
||||
}
|
||||
|
||||
void test_merge_workdir_simple__favor_ours(void)
|
||||
{
|
||||
git_merge_result *result;
|
||||
|
||||
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, "4e886e602529caa9ab11d71f86634bd1b6e0de10", 0, "conflicting.txt" },
|
||||
UNCHANGED_INDEX_ENTRY,
|
||||
};
|
||||
|
||||
struct merge_reuc_entry merge_reuc_entries[] = {
|
||||
AUTOMERGEABLE_REUC_ENTRY,
|
||||
CONFLICTING_REUC_ENTRY,
|
||||
REMOVED_IN_BRANCH_REUC_ENTRY,
|
||||
REMOVED_IN_MASTER_REUC_ENTRY,
|
||||
};
|
||||
|
||||
cl_assert(result = merge_simple_branch(GIT_MERGE_AUTOMERGE_FAVOR_OURS, 0));
|
||||
cl_assert(!git_merge_result_is_fastforward(result));
|
||||
|
||||
cl_assert(merge_test_index(repo_index, merge_index_entries, 6));
|
||||
cl_assert(merge_test_reuc(repo_index, merge_reuc_entries, 4));
|
||||
|
||||
git_merge_result_free(result);
|
||||
}
|
||||
|
||||
void test_merge_workdir_simple__favor_theirs(void)
|
||||
{
|
||||
git_merge_result *result;
|
||||
|
||||
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, "2bd0a343aeef7a2cf0d158478966a6e587ff3863", 0, "conflicting.txt" },
|
||||
UNCHANGED_INDEX_ENTRY,
|
||||
};
|
||||
|
||||
struct merge_reuc_entry merge_reuc_entries[] = {
|
||||
AUTOMERGEABLE_REUC_ENTRY,
|
||||
CONFLICTING_REUC_ENTRY,
|
||||
REMOVED_IN_BRANCH_REUC_ENTRY,
|
||||
REMOVED_IN_MASTER_REUC_ENTRY,
|
||||
};
|
||||
|
||||
cl_assert(result = merge_simple_branch(GIT_MERGE_AUTOMERGE_FAVOR_THEIRS, 0));
|
||||
cl_assert(!git_merge_result_is_fastforward(result));
|
||||
|
||||
cl_assert(merge_test_index(repo_index, merge_index_entries, 6));
|
||||
cl_assert(merge_test_reuc(repo_index, merge_reuc_entries, 4));
|
||||
|
||||
git_merge_result_free(result);
|
||||
}
|
||||
|
||||
void test_merge_workdir_simple__directory_file(void)
|
||||
{
|
||||
git_reference *head;
|
||||
git_oid their_oids[1], head_commit_id;
|
||||
git_merge_head *their_heads[1];
|
||||
git_merge_result *result;
|
||||
git_merge_opts opts = GIT_MERGE_OPTS_INIT;
|
||||
git_commit *head_commit;
|
||||
|
||||
struct merge_index_entry merge_index_entries[] = {
|
||||
{ 0100644, "49130a28ef567af9a6a6104c38773fedfa5f9742", 2, "dir-10" },
|
||||
{ 0100644, "6c06dcd163587c2cc18be44857e0b71116382aeb", 3, "dir-10" },
|
||||
{ 0100644, "43aafd43bea779ec74317dc361f45ae3f532a505", 0, "dir-6" },
|
||||
{ 0100644, "a031a28ae70e33a641ce4b8a8f6317f1ab79dee4", 3, "dir-7" },
|
||||
{ 0100644, "5012fd565b1393bdfda1805d4ec38ce6619e1fd1", 1, "dir-7/file.txt" },
|
||||
{ 0100644, "a5563304ddf6caba25cb50323a2ea6f7dbfcadca", 2, "dir-7/file.txt" },
|
||||
{ 0100644, "e9ad6ec3e38364a3d07feda7c4197d4d845c53b5", 0, "dir-8" },
|
||||
{ 0100644, "3ef4d30382ca33fdeba9fda895a99e0891ba37aa", 2, "dir-9" },
|
||||
{ 0100644, "fc4c636d6515e9e261f9260dbcf3cc6eca97ea08", 1, "dir-9/file.txt" },
|
||||
{ 0100644, "76ab0e2868197ec158ddd6c78d8a0d2fd73d38f9", 3, "dir-9/file.txt" },
|
||||
{ 0100644, "5c2411f8075f48a6b2fdb85ebc0d371747c4df15", 0, "file-1/new" },
|
||||
{ 0100644, "a39a620dae5bc8b4e771cd4d251b7d080401a21e", 1, "file-2" },
|
||||
{ 0100644, "d963979c237d08b6ba39062ee7bf64c7d34a27f8", 2, "file-2" },
|
||||
{ 0100644, "5c341ead2ba6f2af98ce5ec3fe84f6b6d2899c0d", 0, "file-2/new" },
|
||||
{ 0100644, "9efe7723802d4305142eee177e018fee1572c4f4", 0, "file-3/new" },
|
||||
{ 0100644, "bacac9b3493509aa15e1730e1545fc0919d1dae0", 1, "file-4" },
|
||||
{ 0100644, "7663fce0130db092936b137cabd693ec234eb060", 3, "file-4" },
|
||||
{ 0100644, "e49f917b448d1340b31d76e54ba388268fd4c922", 0, "file-4/new" },
|
||||
{ 0100644, "cab2cf23998b40f1af2d9d9a756dc9e285a8df4b", 2, "file-5/new" },
|
||||
{ 0100644, "f5504f36e6f4eb797a56fc5bac6c6c7f32969bf2", 3, "file-5/new" },
|
||||
};
|
||||
|
||||
cl_git_pass(git_reference_symbolic_create(&head, repo, GIT_HEAD_FILE, GIT_REFS_HEADS_DIR OURS_DIRECTORY_FILE, 1));
|
||||
cl_git_pass(git_reference_name_to_id(&head_commit_id, repo, GIT_HEAD_FILE));
|
||||
cl_git_pass(git_commit_lookup(&head_commit, repo, &head_commit_id));
|
||||
cl_git_pass(git_reset(repo, (git_object *)head_commit, GIT_RESET_HARD));
|
||||
|
||||
cl_git_pass(git_oid_fromstr(&their_oids[0], THEIRS_DIRECTORY_FILE));
|
||||
cl_git_pass(git_merge_head_from_oid(&their_heads[0], repo, &their_oids[0]));
|
||||
|
||||
opts.merge_tree_opts.automerge_flags = 0;
|
||||
cl_git_pass(git_merge(&result, repo, (const git_merge_head **)their_heads, 1, &opts));
|
||||
|
||||
cl_assert(merge_test_index(repo_index, merge_index_entries, 20));
|
||||
|
||||
git_reference_free(head);
|
||||
git_commit_free(head_commit);
|
||||
git_merge_head_free(their_heads[0]);
|
||||
git_merge_result_free(result);
|
||||
}
|
||||
|
||||
void test_merge_workdir_simple__unrelated(void)
|
||||
{
|
||||
git_oid their_oids[1];
|
||||
git_merge_head *their_heads[1];
|
||||
git_merge_result *result;
|
||||
git_merge_opts opts = GIT_MERGE_OPTS_INIT;
|
||||
|
||||
struct merge_index_entry merge_index_entries[] = {
|
||||
{ 0100644, "233c0919c998ed110a4b6ff36f353aec8b713487", 0, "added-in-master.txt" },
|
||||
{ 0100644, "ee3fa1b8c00aff7fe02065fdb50864bb0d932ccf", 0, "automergeable.txt" },
|
||||
{ 0100644, "ab6c44a2e84492ad4b41bb6bac87353e9d02ac8b", 0, "changed-in-branch.txt" },
|
||||
{ 0100644, "11deab00b2d3a6f5a3073988ac050c2d7b6655e2", 0, "changed-in-master.txt" },
|
||||
{ 0100644, "4e886e602529caa9ab11d71f86634bd1b6e0de10", 0, "conflicting.txt" },
|
||||
{ 0100644, "ef58fdd8086c243bdc81f99e379acacfd21d32d6", 0, "new-in-unrelated1.txt" },
|
||||
{ 0100644, "948ba6e701c1edab0c2d394fb7c5538334129793", 0, "new-in-unrelated2.txt" },
|
||||
{ 0100644, "dfe3f22baa1f6fce5447901c3086bae368de6bdd", 0, "removed-in-branch.txt" },
|
||||
{ 0100644, "c8f06f2e3bb2964174677e91f0abead0e43c9e5d", 0, "unchanged.txt" },
|
||||
};
|
||||
|
||||
cl_git_pass(git_oid_fromstr(&their_oids[0], THEIRS_UNRELATED_PARENT));
|
||||
cl_git_pass(git_merge_head_from_oid(&their_heads[0], repo, &their_oids[0]));
|
||||
|
||||
opts.merge_tree_opts.automerge_flags = 0;
|
||||
cl_git_pass(git_merge(&result, repo, (const git_merge_head **)their_heads, 1, &opts));
|
||||
|
||||
cl_assert(merge_test_index(repo_index, merge_index_entries, 9));
|
||||
|
||||
git_merge_head_free(their_heads[0]);
|
||||
git_merge_result_free(result);
|
||||
}
|
||||
|
||||
void test_merge_workdir_simple__unrelated_with_conflicts(void)
|
||||
{
|
||||
git_oid their_oids[1];
|
||||
git_merge_head *their_heads[1];
|
||||
git_merge_result *result;
|
||||
git_merge_opts opts = GIT_MERGE_OPTS_INIT;
|
||||
|
||||
struct merge_index_entry merge_index_entries[] = {
|
||||
{ 0100644, "233c0919c998ed110a4b6ff36f353aec8b713487", 0, "added-in-master.txt" },
|
||||
{ 0100644, "ee3fa1b8c00aff7fe02065fdb50864bb0d932ccf", 2, "automergeable.txt" },
|
||||
{ 0100644, "d07ec190c306ec690bac349e87d01c4358e49bb2", 3, "automergeable.txt" },
|
||||
{ 0100644, "ab6c44a2e84492ad4b41bb6bac87353e9d02ac8b", 0, "changed-in-branch.txt" },
|
||||
{ 0100644, "11deab00b2d3a6f5a3073988ac050c2d7b6655e2", 0, "changed-in-master.txt" },
|
||||
{ 0100644, "4e886e602529caa9ab11d71f86634bd1b6e0de10", 2, "conflicting.txt" },
|
||||
{ 0100644, "4b253da36a0ae8bfce63aeabd8c5b58429925594", 3, "conflicting.txt" },
|
||||
{ 0100644, "ef58fdd8086c243bdc81f99e379acacfd21d32d6", 0, "new-in-unrelated1.txt" },
|
||||
{ 0100644, "948ba6e701c1edab0c2d394fb7c5538334129793", 0, "new-in-unrelated2.txt" },
|
||||
{ 0100644, "dfe3f22baa1f6fce5447901c3086bae368de6bdd", 0, "removed-in-branch.txt" },
|
||||
{ 0100644, "c8f06f2e3bb2964174677e91f0abead0e43c9e5d", 0, "unchanged.txt" },
|
||||
};
|
||||
|
||||
cl_git_pass(git_oid_fromstr(&their_oids[0], THEIRS_UNRELATED_OID));
|
||||
cl_git_pass(git_merge_head_from_oid(&their_heads[0], repo, &their_oids[0]));
|
||||
|
||||
opts.merge_tree_opts.automerge_flags = 0;
|
||||
cl_git_pass(git_merge(&result, repo, (const git_merge_head **)their_heads, 1, &opts));
|
||||
|
||||
cl_assert(merge_test_index(repo_index, merge_index_entries, 11));
|
||||
|
||||
git_merge_head_free(their_heads[0]);
|
||||
git_merge_result_free(result);
|
||||
}
|
||||
|
341
tests-clar/merge/workdir/trivial.c
Normal file
341
tests-clar/merge/workdir/trivial.c
Normal file
@ -0,0 +1,341 @@
|
||||
#include "clar_libgit2.h"
|
||||
#include "git2/repository.h"
|
||||
#include "git2/merge.h"
|
||||
#include "git2/sys/index.h"
|
||||
#include "merge.h"
|
||||
#include "../merge_helpers.h"
|
||||
#include "refs.h"
|
||||
#include "fileops.h"
|
||||
|
||||
static git_repository *repo;
|
||||
static git_index *repo_index;
|
||||
|
||||
#define TEST_REPO_PATH "merge-resolve"
|
||||
#define TEST_INDEX_PATH TEST_REPO_PATH "/.git/index"
|
||||
|
||||
|
||||
// Fixture setup and teardown
|
||||
void test_merge_workdir_trivial__initialize(void)
|
||||
{
|
||||
repo = cl_git_sandbox_init(TEST_REPO_PATH);
|
||||
git_repository_index(&repo_index, repo);
|
||||
}
|
||||
|
||||
void test_merge_workdir_trivial__cleanup(void)
|
||||
{
|
||||
git_index_free(repo_index);
|
||||
cl_git_sandbox_cleanup();
|
||||
}
|
||||
|
||||
|
||||
static int merge_trivial(const char *ours, const char *theirs, bool automerge)
|
||||
{
|
||||
git_buf branch_buf = GIT_BUF_INIT;
|
||||
git_checkout_opts checkout_opts = GIT_CHECKOUT_OPTS_INIT;
|
||||
git_reference *our_ref, *their_ref;
|
||||
git_merge_head *their_heads[1];
|
||||
git_merge_opts opts = GIT_MERGE_OPTS_INIT;
|
||||
git_merge_result *result;
|
||||
|
||||
checkout_opts.checkout_strategy = GIT_CHECKOUT_FORCE;
|
||||
|
||||
opts.merge_tree_opts.automerge_flags |= automerge ? 0 : GIT_MERGE_AUTOMERGE_NONE;
|
||||
|
||||
git_buf_printf(&branch_buf, "%s%s", GIT_REFS_HEADS_DIR, ours);
|
||||
cl_git_pass(git_reference_symbolic_create(&our_ref, repo, "HEAD", branch_buf.ptr, 1));
|
||||
|
||||
cl_git_pass(git_checkout_head(repo, &checkout_opts));
|
||||
|
||||
git_buf_clear(&branch_buf);
|
||||
git_buf_printf(&branch_buf, "%s%s", GIT_REFS_HEADS_DIR, theirs);
|
||||
cl_git_pass(git_reference_lookup(&their_ref, repo, branch_buf.ptr));
|
||||
cl_git_pass(git_merge_head_from_ref(&their_heads[0], repo, their_ref));
|
||||
|
||||
cl_git_pass(git_merge(&result, repo, (const git_merge_head **)their_heads, 1, &opts));
|
||||
|
||||
git_buf_free(&branch_buf);
|
||||
git_reference_free(our_ref);
|
||||
git_reference_free(their_ref);
|
||||
git_merge_head_free(their_heads[0]);
|
||||
git_merge_result_free(result);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int merge_trivial_conflict_entrycount(void)
|
||||
{
|
||||
const git_index_entry *entry;
|
||||
size_t count = 0;
|
||||
size_t i;
|
||||
|
||||
for (i = 0; i < git_index_entrycount(repo_index); i++) {
|
||||
cl_assert(entry = git_index_get_byindex(repo_index, i));
|
||||
|
||||
if (git_index_entry_stage(entry) > 0)
|
||||
count++;
|
||||
}
|
||||
|
||||
return count;
|
||||
}
|
||||
|
||||
/* 2ALT: ancest:(empty)+, head:*empty*, remote:remote = result:remote */
|
||||
void test_merge_workdir_trivial__2alt(void)
|
||||
{
|
||||
const git_index_entry *entry;
|
||||
|
||||
cl_git_pass(merge_trivial("trivial-2alt", "trivial-2alt-branch", 0));
|
||||
|
||||
cl_assert(entry = git_index_get_bypath(repo_index, "new-in-branch.txt", 0));
|
||||
cl_assert(git_index_reuc_entrycount(repo_index) == 0);
|
||||
cl_assert(merge_trivial_conflict_entrycount() == 0);
|
||||
}
|
||||
|
||||
/* 3ALT: ancest:(empty)+, head:head, remote:*empty* = result:head */
|
||||
void test_merge_workdir_trivial__3alt(void)
|
||||
{
|
||||
const git_index_entry *entry;
|
||||
|
||||
cl_git_pass(merge_trivial("trivial-3alt", "trivial-3alt-branch", 0));
|
||||
|
||||
cl_assert(entry = git_index_get_bypath(repo_index, "new-in-3alt.txt", 0));
|
||||
cl_assert(git_index_reuc_entrycount(repo_index) == 0);
|
||||
cl_assert(merge_trivial_conflict_entrycount() == 0);
|
||||
}
|
||||
|
||||
/* 4: ancest:(empty)^, head:head, remote:remote = result:no merge */
|
||||
void test_merge_workdir_trivial__4(void)
|
||||
{
|
||||
const git_index_entry *entry;
|
||||
|
||||
cl_git_pass(merge_trivial("trivial-4", "trivial-4-branch", 0));
|
||||
|
||||
cl_assert((entry = git_index_get_bypath(repo_index, "new-and-different.txt", 0)) == NULL);
|
||||
cl_assert(git_index_reuc_entrycount(repo_index) == 0);
|
||||
|
||||
cl_assert(merge_trivial_conflict_entrycount() == 2);
|
||||
cl_assert(entry = git_index_get_bypath(repo_index, "new-and-different.txt", 2));
|
||||
cl_assert(entry = git_index_get_bypath(repo_index, "new-and-different.txt", 3));
|
||||
}
|
||||
|
||||
/* 5ALT: ancest:*, head:head, remote:head = result:head */
|
||||
void test_merge_workdir_trivial__5alt_1(void)
|
||||
{
|
||||
const git_index_entry *entry;
|
||||
|
||||
cl_git_pass(merge_trivial("trivial-5alt-1", "trivial-5alt-1-branch", 0));
|
||||
|
||||
cl_assert(entry = git_index_get_bypath(repo_index, "new-and-same.txt", 0));
|
||||
cl_assert(git_index_reuc_entrycount(repo_index) == 0);
|
||||
cl_assert(merge_trivial_conflict_entrycount() == 0);
|
||||
}
|
||||
|
||||
/* 5ALT: ancest:*, head:head, remote:head = result:head */
|
||||
void test_merge_workdir_trivial__5alt_2(void)
|
||||
{
|
||||
const git_index_entry *entry;
|
||||
|
||||
cl_git_pass(merge_trivial("trivial-5alt-2", "trivial-5alt-2-branch", 0));
|
||||
|
||||
cl_assert(entry = git_index_get_bypath(repo_index, "modified-to-same.txt", 0));
|
||||
cl_assert(git_index_reuc_entrycount(repo_index) == 0);
|
||||
cl_assert(merge_trivial_conflict_entrycount() == 0);
|
||||
}
|
||||
|
||||
/* 6: ancest:ancest+, head:(empty), remote:(empty) = result:no merge */
|
||||
void test_merge_workdir_trivial__6(void)
|
||||
{
|
||||
const git_index_entry *entry;
|
||||
|
||||
cl_git_pass(merge_trivial("trivial-6", "trivial-6-branch", 0));
|
||||
|
||||
cl_assert((entry = git_index_get_bypath(repo_index, "removed-in-both.txt", 0)) == NULL);
|
||||
cl_assert(git_index_reuc_entrycount(repo_index) == 0);
|
||||
|
||||
cl_assert(merge_trivial_conflict_entrycount() == 1);
|
||||
cl_assert(entry = git_index_get_bypath(repo_index, "removed-in-both.txt", 1));
|
||||
}
|
||||
|
||||
/* 6: ancest:ancest+, head:(empty), remote:(empty) = result:no merge */
|
||||
void test_merge_workdir_trivial__6_automerge(void)
|
||||
{
|
||||
const git_index_entry *entry;
|
||||
const git_index_reuc_entry *reuc;
|
||||
|
||||
cl_git_pass(merge_trivial("trivial-6", "trivial-6-branch", 1));
|
||||
|
||||
cl_assert((entry = git_index_get_bypath(repo_index, "removed-in-both.txt", 0)) == NULL);
|
||||
cl_assert(git_index_reuc_entrycount(repo_index) == 1);
|
||||
cl_assert(reuc = git_index_reuc_get_bypath(repo_index, "removed-in-both.txt"));
|
||||
|
||||
cl_assert(merge_trivial_conflict_entrycount() == 0);
|
||||
}
|
||||
|
||||
/* 8: ancest:ancest^, head:(empty), remote:ancest = result:no merge */
|
||||
void test_merge_workdir_trivial__8(void)
|
||||
{
|
||||
const git_index_entry *entry;
|
||||
|
||||
cl_git_pass(merge_trivial("trivial-8", "trivial-8-branch", 0));
|
||||
|
||||
cl_assert((entry = git_index_get_bypath(repo_index, "removed-in-8.txt", 0)) == NULL);
|
||||
cl_assert(git_index_reuc_entrycount(repo_index) == 0);
|
||||
|
||||
cl_assert(merge_trivial_conflict_entrycount() == 2);
|
||||
cl_assert(entry = git_index_get_bypath(repo_index, "removed-in-8.txt", 1));
|
||||
cl_assert(entry = git_index_get_bypath(repo_index, "removed-in-8.txt", 3));
|
||||
}
|
||||
|
||||
/* 8: ancest:ancest^, head:(empty), remote:ancest = result:no merge */
|
||||
void test_merge_workdir_trivial__8_automerge(void)
|
||||
{
|
||||
const git_index_entry *entry;
|
||||
const git_index_reuc_entry *reuc;
|
||||
|
||||
cl_git_pass(merge_trivial("trivial-8", "trivial-8-branch", 1));
|
||||
|
||||
cl_assert((entry = git_index_get_bypath(repo_index, "removed-in-8.txt", 0)) == NULL);
|
||||
|
||||
cl_assert(git_index_reuc_entrycount(repo_index) == 1);
|
||||
cl_assert(reuc = git_index_reuc_get_bypath(repo_index, "removed-in-8.txt"));
|
||||
|
||||
cl_assert(merge_trivial_conflict_entrycount() == 0);
|
||||
}
|
||||
|
||||
/* 7: ancest:ancest+, head:(empty), remote:remote = result:no merge */
|
||||
void test_merge_workdir_trivial__7(void)
|
||||
{
|
||||
const git_index_entry *entry;
|
||||
|
||||
cl_git_pass(merge_trivial("trivial-7", "trivial-7-branch", 0));
|
||||
|
||||
cl_assert((entry = git_index_get_bypath(repo_index, "removed-in-7.txt", 0)) == NULL);
|
||||
cl_assert(git_index_reuc_entrycount(repo_index) == 0);
|
||||
|
||||
cl_assert(merge_trivial_conflict_entrycount() == 2);
|
||||
cl_assert(entry = git_index_get_bypath(repo_index, "removed-in-7.txt", 1));
|
||||
cl_assert(entry = git_index_get_bypath(repo_index, "removed-in-7.txt", 3));
|
||||
}
|
||||
|
||||
/* 7: ancest:ancest+, head:(empty), remote:remote = result:no merge */
|
||||
void test_merge_workdir_trivial__7_automerge(void)
|
||||
{
|
||||
const git_index_entry *entry;
|
||||
|
||||
cl_git_pass(merge_trivial("trivial-7", "trivial-7-branch", 0));
|
||||
|
||||
cl_assert((entry = git_index_get_bypath(repo_index, "removed-in-7.txt", 0)) == NULL);
|
||||
cl_assert(git_index_reuc_entrycount(repo_index) == 0);
|
||||
|
||||
cl_assert(merge_trivial_conflict_entrycount() == 2);
|
||||
cl_assert(entry = git_index_get_bypath(repo_index, "removed-in-7.txt", 1));
|
||||
cl_assert(entry = git_index_get_bypath(repo_index, "removed-in-7.txt", 3));
|
||||
}
|
||||
|
||||
/* 10: ancest:ancest^, head:ancest, remote:(empty) = result:no merge */
|
||||
void test_merge_workdir_trivial__10(void)
|
||||
{
|
||||
const git_index_entry *entry;
|
||||
|
||||
cl_git_pass(merge_trivial("trivial-10", "trivial-10-branch", 0));
|
||||
|
||||
cl_assert((entry = git_index_get_bypath(repo_index, "removed-in-10-branch.txt", 0)) == NULL);
|
||||
cl_assert(git_index_reuc_entrycount(repo_index) == 0);
|
||||
|
||||
cl_assert(merge_trivial_conflict_entrycount() == 2);
|
||||
cl_assert(entry = git_index_get_bypath(repo_index, "removed-in-10-branch.txt", 1));
|
||||
cl_assert(entry = git_index_get_bypath(repo_index, "removed-in-10-branch.txt", 2));
|
||||
}
|
||||
|
||||
/* 10: ancest:ancest^, head:ancest, remote:(empty) = result:no merge */
|
||||
void test_merge_workdir_trivial__10_automerge(void)
|
||||
{
|
||||
const git_index_entry *entry;
|
||||
const git_index_reuc_entry *reuc;
|
||||
|
||||
cl_git_pass(merge_trivial("trivial-10", "trivial-10-branch", 1));
|
||||
|
||||
cl_assert((entry = git_index_get_bypath(repo_index, "removed-in-10-branch.txt", 0)) == NULL);
|
||||
|
||||
cl_assert(git_index_reuc_entrycount(repo_index) == 1);
|
||||
cl_assert(reuc = git_index_reuc_get_bypath(repo_index, "removed-in-10-branch.txt"));
|
||||
|
||||
cl_assert(merge_trivial_conflict_entrycount() == 0);
|
||||
}
|
||||
|
||||
/* 9: ancest:ancest+, head:head, remote:(empty) = result:no merge */
|
||||
void test_merge_workdir_trivial__9(void)
|
||||
{
|
||||
const git_index_entry *entry;
|
||||
|
||||
cl_git_pass(merge_trivial("trivial-9", "trivial-9-branch", 0));
|
||||
|
||||
cl_assert((entry = git_index_get_bypath(repo_index, "removed-in-9-branch.txt", 0)) == NULL);
|
||||
cl_assert(git_index_reuc_entrycount(repo_index) == 0);
|
||||
|
||||
cl_assert(merge_trivial_conflict_entrycount() == 2);
|
||||
cl_assert(entry = git_index_get_bypath(repo_index, "removed-in-9-branch.txt", 1));
|
||||
cl_assert(entry = git_index_get_bypath(repo_index, "removed-in-9-branch.txt", 2));
|
||||
}
|
||||
|
||||
/* 9: ancest:ancest+, head:head, remote:(empty) = result:no merge */
|
||||
void test_merge_workdir_trivial__9_automerge(void)
|
||||
{
|
||||
const git_index_entry *entry;
|
||||
|
||||
cl_git_pass(merge_trivial("trivial-9", "trivial-9-branch", 1));
|
||||
|
||||
cl_assert((entry = git_index_get_bypath(repo_index, "removed-in-9-branch.txt", 0)) == NULL);
|
||||
cl_assert(git_index_reuc_entrycount(repo_index) == 0);
|
||||
|
||||
cl_assert(merge_trivial_conflict_entrycount() == 2);
|
||||
cl_assert(entry = git_index_get_bypath(repo_index, "removed-in-9-branch.txt", 1));
|
||||
cl_assert(entry = git_index_get_bypath(repo_index, "removed-in-9-branch.txt", 2));
|
||||
}
|
||||
|
||||
/* 13: ancest:ancest+, head:head, remote:ancest = result:head */
|
||||
void test_merge_workdir_trivial__13(void)
|
||||
{
|
||||
const git_index_entry *entry;
|
||||
git_oid expected_oid;
|
||||
|
||||
cl_git_pass(merge_trivial("trivial-13", "trivial-13-branch", 0));
|
||||
|
||||
cl_assert(entry = git_index_get_bypath(repo_index, "modified-in-13.txt", 0));
|
||||
cl_git_pass(git_oid_fromstr(&expected_oid, "1cff9ec6a47a537380dedfdd17c9e76d74259a2b"));
|
||||
cl_assert(git_oid_cmp(&entry->oid, &expected_oid) == 0);
|
||||
|
||||
cl_assert(git_index_reuc_entrycount(repo_index) == 0);
|
||||
cl_assert(merge_trivial_conflict_entrycount() == 0);
|
||||
}
|
||||
|
||||
/* 14: ancest:ancest+, head:ancest, remote:remote = result:remote */
|
||||
void test_merge_workdir_trivial__14(void)
|
||||
{
|
||||
const git_index_entry *entry;
|
||||
git_oid expected_oid;
|
||||
|
||||
cl_git_pass(merge_trivial("trivial-14", "trivial-14-branch", 0));
|
||||
|
||||
cl_assert(entry = git_index_get_bypath(repo_index, "modified-in-14-branch.txt", 0));
|
||||
cl_git_pass(git_oid_fromstr(&expected_oid, "26153a3ff3649b6c2bb652d3f06878c6e0a172f9"));
|
||||
cl_assert(git_oid_cmp(&entry->oid, &expected_oid) == 0);
|
||||
|
||||
cl_assert(git_index_reuc_entrycount(repo_index) == 0);
|
||||
cl_assert(merge_trivial_conflict_entrycount() == 0);
|
||||
}
|
||||
|
||||
/* 11: ancest:ancest+, head:head, remote:remote = result:no merge */
|
||||
void test_merge_workdir_trivial__11(void)
|
||||
{
|
||||
const git_index_entry *entry;
|
||||
|
||||
cl_git_pass(merge_trivial("trivial-11", "trivial-11-branch", 0));
|
||||
|
||||
cl_assert((entry = git_index_get_bypath(repo_index, "modified-in-both.txt", 0)) == NULL);
|
||||
cl_assert(git_index_reuc_entrycount(repo_index) == 0);
|
||||
|
||||
cl_assert(merge_trivial_conflict_entrycount() == 3);
|
||||
cl_assert(entry = git_index_get_bypath(repo_index, "modified-in-both.txt", 1));
|
||||
cl_assert(entry = git_index_get_bypath(repo_index, "modified-in-both.txt", 2));
|
||||
cl_assert(entry = git_index_get_bypath(repo_index, "modified-in-both.txt", 3));
|
||||
}
|
Loading…
Reference in New Issue
Block a user