libgit2/tests-clar/merge/merge_helpers.c
Russell Belfer 114f5a6c41 Reorganize diff and add basic diff driver
This is a significant reorganization of the diff code to break it
into a set of more clearly distinct files and to document the new
organization.  Hopefully this will make the diff code easier to
understand and to extend.

This adds a new `git_diff_driver` object that looks of diff driver
information from the attributes and the config so that things like
function content in diff headers can be provided.  The full driver
spec is not implemented in the commit - this is focused on the
reorganization of the code and putting the driver hooks in place.

This also removes a few #includes from src/repository.h that were
overbroad, but as a result required extra #includes in a variety
of places since including src/repository.h no longer results in
pulling in the whole world.
2013-06-10 10:10:39 -07:00

311 lines
8.0 KiB
C

#include "clar_libgit2.h"
#include "fileops.h"
#include "refs.h"
#include "tree.h"
#include "merge_helpers.h"
#include "merge.h"
#include "git2/merge.h"
#include "git2/sys/index.h"
int merge_trees_from_branches(
git_index **index, git_repository *repo,
const char *ours_name, const char *theirs_name,
git_merge_tree_opts *opts)
{
git_commit *our_commit, *their_commit, *ancestor_commit = NULL;
git_tree *our_tree, *their_tree, *ancestor_tree = NULL;
git_oid our_oid, their_oid, ancestor_oid;
git_buf branch_buf = GIT_BUF_INIT;
int error;
git_buf_printf(&branch_buf, "%s%s", GIT_REFS_HEADS_DIR, ours_name);
cl_git_pass(git_reference_name_to_id(&our_oid, repo, branch_buf.ptr));
cl_git_pass(git_commit_lookup(&our_commit, repo, &our_oid));
git_buf_clear(&branch_buf);
git_buf_printf(&branch_buf, "%s%s", GIT_REFS_HEADS_DIR, theirs_name);
cl_git_pass(git_reference_name_to_id(&their_oid, repo, branch_buf.ptr));
cl_git_pass(git_commit_lookup(&their_commit, repo, &their_oid));
error = git_merge_base(&ancestor_oid, repo, git_commit_id(our_commit), git_commit_id(their_commit));
if (error != GIT_ENOTFOUND) {
cl_git_pass(error);
cl_git_pass(git_commit_lookup(&ancestor_commit, repo, &ancestor_oid));
cl_git_pass(git_commit_tree(&ancestor_tree, ancestor_commit));
}
cl_git_pass(git_commit_tree(&our_tree, our_commit));
cl_git_pass(git_commit_tree(&their_tree, their_commit));
cl_git_pass(git_merge_trees(index, repo, ancestor_tree, our_tree, their_tree, opts));
git_buf_free(&branch_buf);
git_tree_free(our_tree);
git_tree_free(their_tree);
git_tree_free(ancestor_tree);
git_commit_free(our_commit);
git_commit_free(their_commit);
git_commit_free(ancestor_commit);
return 0;
}
void merge__dump_index_entries(git_vector *index_entries)
{
size_t i;
const git_index_entry *index_entry;
printf ("\nINDEX [%d]:\n", (int)index_entries->length);
for (i = 0; i < index_entries->length; i++) {
index_entry = index_entries->contents[i];
printf("%o ", index_entry->mode);
printf("%s ", git_oid_allocfmt(&index_entry->oid));
printf("%d ", git_index_entry_stage(index_entry));
printf("%s ", index_entry->path);
printf("\n");
}
printf("\n");
}
void merge__dump_names(git_index *index)
{
size_t i;
const git_index_name_entry *conflict_name;
for (i = 0; i < git_index_name_entrycount(index); i++) {
conflict_name = git_index_name_get_byindex(index, i);
printf("%s %s %s\n", conflict_name->ancestor, conflict_name->ours, conflict_name->theirs);
}
printf("\n");
}
void merge__dump_reuc(git_index *index)
{
size_t i;
const git_index_reuc_entry *reuc;
printf ("\nREUC:\n");
for (i = 0; i < git_index_reuc_entrycount(index); i++) {
reuc = git_index_reuc_get_byindex(index, i);
printf("%s ", reuc->path);
printf("%o ", reuc->mode[0]);
printf("%s\n", git_oid_allocfmt(&reuc->oid[0]));
printf(" %o ", reuc->mode[1]);
printf(" %s\n", git_oid_allocfmt(&reuc->oid[1]));
printf(" %o ", reuc->mode[2]);
printf(" %s ", git_oid_allocfmt(&reuc->oid[2]));
printf("\n");
}
printf("\n");
}
static int index_entry_eq_merge_index_entry(const struct merge_index_entry *expected, const git_index_entry *actual)
{
git_oid expected_oid;
bool test_oid;
if (strlen(expected->oid_str) != 0) {
cl_git_pass(git_oid_fromstr(&expected_oid, expected->oid_str));
test_oid = 1;
} else
test_oid = 0;
if (actual->mode != expected->mode ||
(test_oid && git_oid_cmp(&actual->oid, &expected_oid) != 0) ||
git_index_entry_stage(actual) != expected->stage)
return 0;
if (actual->mode == 0 && (actual->path != NULL || strlen(expected->path) > 0))
return 0;
if (actual->mode != 0 && (strcmp(actual->path, expected->path) != 0))
return 0;
return 1;
}
static int name_entry_eq(const char *expected, const char *actual)
{
if (strlen(expected) == 0)
return (actual == NULL) ? 1 : 0;
return (strcmp(expected, actual) == 0) ? 1 : 0;
}
static int name_entry_eq_merge_name_entry(const struct merge_name_entry *expected, const git_index_name_entry *actual)
{
if (name_entry_eq(expected->ancestor_path, actual->ancestor) == 0 ||
name_entry_eq(expected->our_path, actual->ours) == 0 ||
name_entry_eq(expected->their_path, actual->theirs) == 0)
return 0;
return 1;
}
static int index_conflict_data_eq_merge_diff(const struct merge_index_conflict_data *expected, git_merge_diff *actual)
{
if (!index_entry_eq_merge_index_entry(&expected->ancestor.entry, &actual->ancestor_entry) ||
!index_entry_eq_merge_index_entry(&expected->ours.entry, &actual->our_entry) ||
!index_entry_eq_merge_index_entry(&expected->theirs.entry, &actual->their_entry))
return 0;
if (expected->ours.status != actual->our_status ||
expected->theirs.status != actual->their_status)
return 0;
return 1;
}
int merge_test_merge_conflicts(git_vector *conflicts, const struct merge_index_conflict_data expected[], size_t expected_len)
{
git_merge_diff *actual;
size_t i;
if (conflicts->length != expected_len)
return 0;
for (i = 0; i < expected_len; i++) {
actual = conflicts->contents[i];
if (!index_conflict_data_eq_merge_diff(&expected[i], actual))
return 0;
}
return 1;
}
int merge_test_index(git_index *index, const struct merge_index_entry expected[], size_t expected_len)
{
size_t i;
const git_index_entry *index_entry;
/*
dump_index_entries(&index->entries);
*/
if (git_index_entrycount(index) != expected_len)
return 0;
for (i = 0; i < expected_len; i++) {
if ((index_entry = git_index_get_byindex(index, i)) == NULL)
return 0;
if (!index_entry_eq_merge_index_entry(&expected[i], index_entry))
return 0;
}
return 1;
}
int merge_test_names(git_index *index, const struct merge_name_entry expected[], size_t expected_len)
{
size_t i;
const git_index_name_entry *name_entry;
/*
dump_names(index);
*/
if (git_index_name_entrycount(index) != expected_len)
return 0;
for (i = 0; i < expected_len; i++) {
if ((name_entry = git_index_name_get_byindex(index, i)) == NULL)
return 0;
if (! name_entry_eq_merge_name_entry(&expected[i], name_entry))
return 0;
}
return 1;
}
int merge_test_reuc(git_index *index, const struct merge_reuc_entry expected[], size_t expected_len)
{
size_t i;
const git_index_reuc_entry *reuc_entry;
git_oid expected_oid;
/*
dump_reuc(index);
*/
if (git_index_reuc_entrycount(index) != expected_len)
return 0;
for (i = 0; i < expected_len; i++) {
if ((reuc_entry = git_index_reuc_get_byindex(index, i)) == NULL)
return 0;
if (strcmp(reuc_entry->path, expected[i].path) != 0 ||
reuc_entry->mode[0] != expected[i].ancestor_mode ||
reuc_entry->mode[1] != expected[i].our_mode ||
reuc_entry->mode[2] != expected[i].their_mode)
return 0;
if (expected[i].ancestor_mode > 0) {
cl_git_pass(git_oid_fromstr(&expected_oid, expected[i].ancestor_oid_str));
if (git_oid_cmp(&reuc_entry->oid[0], &expected_oid) != 0)
return 0;
}
if (expected[i].our_mode > 0) {
cl_git_pass(git_oid_fromstr(&expected_oid, expected[i].our_oid_str));
if (git_oid_cmp(&reuc_entry->oid[1], &expected_oid) != 0)
return 0;
}
if (expected[i].their_mode > 0) {
cl_git_pass(git_oid_fromstr(&expected_oid, expected[i].their_oid_str));
if (git_oid_cmp(&reuc_entry->oid[2], &expected_oid) != 0)
return 0;
}
}
return 1;
}
int dircount(void *payload, git_buf *pathbuf)
{
int *entries = payload;
size_t len = git_buf_len(pathbuf);
if (len < 5 || strcmp(pathbuf->ptr + (git_buf_len(pathbuf) - 5), "/.git") != 0)
(*entries)++;
return 0;
}
int merge_test_workdir(git_repository *repo, const struct merge_index_entry expected[], size_t expected_len)
{
size_t actual_len = 0, i;
git_oid actual_oid, expected_oid;
git_buf wd = GIT_BUF_INIT;
git_buf_puts(&wd, repo->workdir);
git_path_direach(&wd, dircount, &actual_len);
if (actual_len != expected_len)
return 0;
for (i = 0; i < expected_len; i++) {
git_blob_create_fromworkdir(&actual_oid, repo, expected[i].path);
git_oid_fromstr(&expected_oid, expected[i].oid_str);
if (git_oid_cmp(&actual_oid, &expected_oid) != 0)
return 0;
}
git_buf_free(&wd);
return 1;
}