mirror of
https://git.proxmox.com/git/libgit2
synced 2025-05-05 20:33:41 +00:00

This hooks up git_path_direach and git_path_dirload so that they will take a flag indicating if directory entry names should be tested and converted from decomposed unicode to precomposed form. This code will only come into play on the Apple platform and even then, only when certain types of filesystems are used. This involved adding a flag to these functions which involved changing a lot of places in the code. This was an opportunity to do a bit of code cleanup here and there, for example, getting rid of the git_futils_cleanupdir_r function in favor of a simple flag to git_futils_rmdir_r to not remove the top level entry. That ended up adding depth tracking during rmdir_r which led to a safety check for infinite directory recursion. Yay. This hasn't actually been tested on the Mac filesystems where the issue occurs. I still need to get test environment for that.
311 lines
8.0 KiB
C
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, 0, 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;
|
|
}
|