Commit 0462fba5 by Edward Thomson

renames!

parent bec65a5e
......@@ -84,6 +84,12 @@ typedef struct git_index_entry {
char *path;
} git_index_entry;
typedef struct git_index_name_entry {
char *ancestor;
char *ours;
char *theirs;
} git_index_name_entry;
/** Representation of a resolve undo entry in the index. */
typedef struct git_index_reuc_entry {
unsigned int mode[3];
......@@ -478,6 +484,53 @@ GIT_EXTERN(int) git_index_has_conflicts(const git_index *index);
/**@}*/
/** @name Conflict Name entry functions
*
* These functions work on rename conflict entries.
*/
/**@{*/
/**
* Get the count of filename conflict entries currently in the index.
*
* @param index an existing index object
* @return integer of count of current filename conflict entries
*/
GIT_EXTERN(unsigned int) git_index_name_entrycount(git_index *index);
/**
* Get a filename conflict entry from the index.
*
* The returned entry is read-only and should not be modified
* or freed by the caller.
*
* @param index an existing index object
* @param n the position of the entry
* @return a pointer to the filename conflict entry; NULL if out of bounds
*/
GIT_EXTERN(const git_index_name_entry *) git_index_name_get_byindex(
git_index *index, size_t n);
/**
* Record the filenames involved in a rename conflict.
*
* @param index an existing index object
* @param ancestor the path of the file as it existed in the ancestor
* @param ours the path of the file as it existed in our tree
* @param theirs the path of the file as it existed in their tree
*/
GIT_EXTERN(int) git_index_name_add(git_index *index,
const char *ancestor, const char *ours, const char *theirs);
/**
* Remove all filename conflict entries.
*
* @param index an existing index object
* @return 0 or an error code
*/
GIT_EXTERN(void) git_index_name_clear(git_index *index);
/**@}*/
/** @name Resolve Undo (REUC) index entry manipulation.
*
* These functions work on the Resolve Undo index extension and contains
......
......@@ -27,6 +27,8 @@ GIT_BEGIN_DECL
* passed in via the `flags` value in the `git_diff_tree_many_options`.
*/
typedef enum {
/** Detect renames */
GIT_MERGE_TREE_FIND_RENAMES = (1 << 0),
} git_merge_tree_flags;
/**
......@@ -44,6 +46,17 @@ typedef struct {
unsigned int version;
git_merge_tree_flags flags;
/** Similarity to consider a file renamed (default 50) */
unsigned int rename_threshold;
/** Maximum similarity sources to examine (overrides the
* `merge.renameLimit` config) (default 200)
*/
unsigned int target_limit;
/** Pluggable similarity metric; pass NULL to use internal metric */
git_diff_similarity_metric *metric;
/** Flags for automerging content. */
git_merge_automerge_flags automerge_flags;
} git_merge_tree_opts;
......
......@@ -74,5 +74,17 @@ extern int git_diff__from_iterators(
git_iterator *new_iter,
const git_diff_options *opts);
int git_diff_find_similar__hashsig_for_file(
void **out, const git_diff_file *f, const char *path, void *p);
int git_diff_find_similar__hashsig_for_buf(
void **out, const git_diff_file *f, const char *buf, size_t len, void *p);
void git_diff_find_similar__hashsig_free(void *sig, void *payload);
int git_diff_find_similar__calc_similarity(
int *score, void *siga, void *sigb, void *payload);
#endif
......@@ -170,7 +170,7 @@ int git_diff_merge(
return error;
}
static int find_similar__hashsig_for_file(
int git_diff_find_similar__hashsig_for_file(
void **out, const git_diff_file *f, const char *path, void *p)
{
git_hashsig_option_t opt = (git_hashsig_option_t)p;
......@@ -187,7 +187,7 @@ static int find_similar__hashsig_for_file(
return error;
}
static int find_similar__hashsig_for_buf(
int git_diff_find_similar__hashsig_for_buf(
void **out, const git_diff_file *f, const char *buf, size_t len, void *p)
{
git_hashsig_option_t opt = (git_hashsig_option_t)p;
......@@ -204,13 +204,13 @@ static int find_similar__hashsig_for_buf(
return error;
}
static void find_similar__hashsig_free(void *sig, void *payload)
void git_diff_find_similar__hashsig_free(void *sig, void *payload)
{
GIT_UNUSED(payload);
git_hashsig_free(sig);
}
static int find_similar__calc_similarity(
int git_diff_find_similar__calc_similarity(
int *score, void *siga, void *sigb, void *payload)
{
GIT_UNUSED(payload);
......@@ -291,10 +291,10 @@ static int normalize_find_opts(
opts->metric = git__malloc(sizeof(git_diff_similarity_metric));
GITERR_CHECK_ALLOC(opts->metric);
opts->metric->file_signature = find_similar__hashsig_for_file;
opts->metric->buffer_signature = find_similar__hashsig_for_buf;
opts->metric->free_signature = find_similar__hashsig_free;
opts->metric->similarity = find_similar__calc_similarity;
opts->metric->file_signature = git_diff_find_similar__hashsig_for_file;
opts->metric->buffer_signature = git_diff_find_similar__hashsig_for_buf;
opts->metric->free_signature = git_diff_find_similar__hashsig_free;
opts->metric->similarity = git_diff_find_similar__calc_similarity;
if (opts->flags & GIT_DIFF_FIND_IGNORE_WHITESPACE)
opts->metric->payload = (void *)GIT_HASHSIG_IGNORE_WHITESPACE;
......
......@@ -35,6 +35,7 @@ static const unsigned int INDEX_VERSION_NUMBER_EXT = 3;
static const unsigned int INDEX_HEADER_SIG = 0x44495243;
static const char INDEX_EXT_TREECACHE_SIG[] = {'T', 'R', 'E', 'E'};
static const char INDEX_EXT_UNMERGED_SIG[] = {'R', 'E', 'U', 'C'};
static const char INDEX_EXT_CONFLICT_NAME_SIG[] = {'N', 'A', 'M', 'E'};
#define INDEX_OWNER(idx) ((git_repository *)(GIT_REFCOUNT_OWNER(idx)))
......@@ -187,6 +188,46 @@ static int index_icmp(const void *a, const void *b)
return diff;
}
static int conflict_name_cmp(const void *a, const void *b)
{
const git_index_name_entry *name_a = a;
const git_index_name_entry *name_b = b;
if (name_a->ancestor && !name_b->ancestor)
return 1;
if (!name_a->ancestor && name_b->ancestor)
return -1;
if (name_a->ancestor)
return strcmp(name_a->ancestor, name_b->ancestor);
if (!name_a->ours || !name_b->ours)
return 0;
return strcmp(name_a->ours, name_b->ours);
}
static int conflict_name_icmp(const void *a, const void *b)
{
const git_index_name_entry *name_a = a;
const git_index_name_entry *name_b = b;
if (name_a->ancestor && !name_b->ancestor)
return 1;
if (!name_a->ancestor && name_b->ancestor)
return -1;
if (name_a->ancestor)
return strcasecmp(name_a->ancestor, name_b->ancestor);
if (!name_a->ours || !name_b->ours)
return 0;
return strcasecmp(name_a->ours, name_b->ours);
}
static int reuc_srch(const void *key, const void *array_member)
{
const git_index_reuc_entry *reuc = array_member;
......@@ -278,6 +319,7 @@ int git_index_open(git_index **index_out, const char *index_path)
}
if (git_vector_init(&index->entries, 32, index_cmp) < 0 ||
git_vector_init(&index->names, 32, conflict_name_cmp) < 0 ||
git_vector_init(&index->reuc, 32, reuc_cmp) < 0)
return -1;
......@@ -331,6 +373,8 @@ void git_index_clear(git_index *index)
git_index_reuc_clear(index);
git_index_name_clear(index);
git_futils_filestamp_set(&index->stamp, NULL);
git_tree_cache_free(index->tree);
......@@ -1042,6 +1086,72 @@ int git_index_has_conflicts(const git_index *index)
return 0;
}
unsigned int git_index_name_entrycount(git_index *index)
{
assert(index);
return (unsigned int)index->names.length;
}
const git_index_name_entry *git_index_name_get_byindex(
git_index *index, size_t n)
{
assert(index);
git_vector_sort(&index->names);
return git_vector_get(&index->names, n);
}
int git_index_name_add(git_index *index,
const char *ancestor, const char *ours, const char *theirs)
{
git_index_name_entry *conflict_name;
assert ((ancestor && ours) || (ancestor && theirs) || (ours && theirs));
conflict_name = git__calloc(1, sizeof(git_index_name_entry));
GITERR_CHECK_ALLOC(conflict_name);
if (ancestor) {
conflict_name->ancestor = git__strdup(ancestor);
GITERR_CHECK_ALLOC(conflict_name->ancestor);
}
if (ours) {
conflict_name->ours = git__strdup(ours);
GITERR_CHECK_ALLOC(conflict_name->ours);
}
if (theirs) {
conflict_name->theirs = git__strdup(theirs);
GITERR_CHECK_ALLOC(conflict_name->theirs);
}
return git_vector_insert(&index->names, conflict_name);
}
void git_index_name_clear(git_index *index)
{
size_t i;
git_index_name_entry *conflict_name;
assert(index);
git_vector_foreach(&index->names, i, conflict_name) {
if (conflict_name->ancestor)
git__free(conflict_name->ancestor);
if (conflict_name->ours)
git__free(conflict_name->ours);
if (conflict_name->theirs)
git__free(conflict_name->theirs);
git__free(conflict_name);
}
git_vector_clear(&index->names);
}
unsigned int git_index_reuc_entrycount(git_index *index)
{
assert(index);
......@@ -1228,6 +1338,52 @@ static int read_reuc(git_index *index, const char *buffer, size_t size)
return 0;
}
static int read_conflict_names(git_index *index, const char *buffer, size_t size)
{
size_t len;
/* This gets called multiple times, the vector might already be initialized */
if (index->names._alloc_size == 0 &&
git_vector_init(&index->names, 16, conflict_name_cmp) < 0)
return -1;
#define read_conflict_name(ptr) \
len = strlen(buffer) + 1; \
if (size < len) \
return index_error_invalid("reading conflict name entries"); \
\
if (len == 1) \
ptr = NULL; \
else { \
ptr = git__malloc(len); \
GITERR_CHECK_ALLOC(ptr); \
memcpy(ptr, buffer, len); \
} \
\
buffer += len; \
size -= len;
while (size) {
git_index_name_entry *conflict_name = git__calloc(1, sizeof(git_index_name_entry));
GITERR_CHECK_ALLOC(conflict_name);
read_conflict_name(conflict_name->ancestor);
read_conflict_name(conflict_name->ours);
read_conflict_name(conflict_name->theirs);
if (git_vector_insert(&index->names, conflict_name) < 0)
return -1;
}
#undef read_conflict_name
/* entries are guaranteed to be sorted on-disk */
index->names.sorted = 1;
return 0;
}
static size_t read_entry(git_index_entry *dest, const void *buffer, size_t buffer_size)
{
size_t path_length, entry_size;
......@@ -1332,6 +1488,9 @@ static size_t read_extension(git_index *index, const char *buffer, size_t buffer
} else if (memcmp(dest.signature, INDEX_EXT_UNMERGED_SIG, 4) == 0) {
if (read_reuc(index, buffer + 8, dest.extension_size) < 0)
return 0;
} else if (memcmp(dest.signature, INDEX_EXT_CONFLICT_NAME_SIG, 4) == 0) {
if (read_conflict_names(index, buffer + 8, dest.extension_size) < 0)
return 0;
}
/* else, unsupported extension. We cannot parse this, but we can skip
* it by returning `total_size */
......@@ -1545,6 +1704,61 @@ static int write_extension(git_filebuf *file, struct index_extension *header, gi
return error;
}
static int create_name_extension_data(git_buf *name_buf, git_index_name_entry *conflict_name)
{
int error = 0;
if (conflict_name->ancestor == NULL)
error = git_buf_put(name_buf, "\0", 1);
else
error = git_buf_put(name_buf, conflict_name->ancestor, strlen(conflict_name->ancestor) + 1);
if (error != 0)
goto on_error;
if (conflict_name->ours == NULL)
error = git_buf_put(name_buf, "\0", 1);
else
error = git_buf_put(name_buf, conflict_name->ours, strlen(conflict_name->ours) + 1);
if (error != 0)
goto on_error;
if (conflict_name->theirs == NULL)
error = git_buf_put(name_buf, "\0", 1);
else
error = git_buf_put(name_buf, conflict_name->theirs, strlen(conflict_name->theirs) + 1);
on_error:
return error;
}
static int write_name_extension(git_index *index, git_filebuf *file)
{
git_buf name_buf = GIT_BUF_INIT;
git_vector *out = &index->names;
git_index_name_entry *conflict_name;
struct index_extension extension;
size_t i;
int error = 0;
git_vector_foreach(out, i, conflict_name) {
if ((error = create_name_extension_data(&name_buf, conflict_name)) < 0)
goto done;
}
memset(&extension, 0x0, sizeof(struct index_extension));
memcpy(&extension.signature, INDEX_EXT_CONFLICT_NAME_SIG, 4);
extension.extension_size = (uint32_t)name_buf.size;
error = write_extension(file, &extension, &name_buf);
git_buf_free(&name_buf);
done:
return error;
}
static int create_reuc_extension_data(git_buf *reuc_buf, git_index_reuc_entry *reuc)
{
int i;
......@@ -1615,6 +1829,10 @@ static int write_index(git_index *index, git_filebuf *file)
/* TODO: write tree cache extension */
/* write the rename conflict extension */
if (index->names.length > 0 && write_name_extension(index, file) < 0)
return -1;
/* write the reuc extension */
if (index->reuc.length > 0 && write_reuc_extension(index, file) < 0)
return -1;
......
......@@ -33,6 +33,7 @@ struct git_index {
git_tree_cache *tree;
git_vector names;
git_vector reuc;
git_vector_cmp entries_cmp_path;
......
......@@ -23,6 +23,7 @@
#include "merge_file.h"
#include "blob.h"
#include "hashsig.h"
#include "oid.h"
#include "git2/types.h"
#include "git2/repository.h"
......@@ -640,6 +641,440 @@ done:
return error;
}
/* Rename detection and coalescing */
struct merge_diff_similarity {
unsigned char similarity;
size_t other_idx;
};
static int index_entry_similarity_exact(
git_repository *repo,
git_index_entry *a,
size_t a_idx,
git_index_entry *b,
size_t b_idx,
void **cache,
const git_merge_tree_opts *opts)
{
GIT_UNUSED(repo);
GIT_UNUSED(a_idx);
GIT_UNUSED(b_idx);
GIT_UNUSED(cache);
GIT_UNUSED(opts);
if (git_oid__cmp(&a->oid, &b->oid) == 0)
return 100;
return 0;
}
static int index_entry_similarity_calc(
void **out,
git_repository *repo,
git_index_entry *entry,
const git_merge_tree_opts *opts)
{
git_blob *blob;
git_diff_file diff_file = {{{0}}};
int error;
*out = NULL;
if ((error = git_blob_lookup(&blob, repo, &entry->oid)) < 0)
return error;
git_oid_cpy(&diff_file.oid, &entry->oid);
diff_file.path = entry->path;
diff_file.size = entry->file_size;
diff_file.mode = entry->mode;
diff_file.flags = 0;
error = opts->metric->buffer_signature(out, &diff_file,
git_blob_rawcontent(blob), git_blob_rawsize(blob),
opts->metric->payload);
git_blob_free(blob);
return error;
}
static int index_entry_similarity_inexact(
git_repository *repo,
git_index_entry *a,
size_t a_idx,
git_index_entry *b,
size_t b_idx,
void **cache,
const git_merge_tree_opts *opts)
{
int score = 0;
int error = 0;
if (GIT_MODE_TYPE(a->mode) != GIT_MODE_TYPE(b->mode))
return 0;
/* update signature cache if needed */
if (!cache[a_idx] && (error = index_entry_similarity_calc(&cache[a_idx], repo, a, opts)) < 0)
return error;
if (!cache[b_idx] && (error = index_entry_similarity_calc(&cache[b_idx], repo, b, opts)) < 0)
return error;
/* some metrics may not wish to process this file (too big / too small) */
if (!cache[a_idx] || !cache[b_idx])
return 0;
/* compare signatures */
if (opts->metric->similarity(
&score, cache[a_idx], cache[b_idx], opts->metric->payload) < 0)
return -1;
/* clip score */
if (score < 0)
score = 0;
else if (score > 100)
score = 100;
return score;
}
static int merge_diff_mark_similarity(
git_repository *repo,
git_merge_diff_list *diff_list,
struct merge_diff_similarity *similarity_ours,
struct merge_diff_similarity *similarity_theirs,
int (*similarity_fn)(git_repository *, git_index_entry *, size_t, git_index_entry *, size_t, void **, const git_merge_tree_opts *),
void **cache,
const git_merge_tree_opts *opts)
{
size_t i, j;
git_merge_diff *conflict_src, *conflict_tgt;
int similarity;
git_vector_foreach(&diff_list->conflicts, i, conflict_src) {
/* Items can be the source of a rename iff they have an item in the
* ancestor slot and lack an item in the ours or theirs slot. */
if (!GIT_MERGE_INDEX_ENTRY_EXISTS(conflict_src->ancestor_entry) ||
(GIT_MERGE_INDEX_ENTRY_EXISTS(conflict_src->our_entry) &&
GIT_MERGE_INDEX_ENTRY_EXISTS(conflict_src->their_entry)))
continue;
git_vector_foreach(&diff_list->conflicts, j, conflict_tgt) {
size_t our_idx = diff_list->conflicts.length + j;
size_t their_idx = (diff_list->conflicts.length * 2) + j;
if (GIT_MERGE_INDEX_ENTRY_EXISTS(conflict_tgt->ancestor_entry))
continue;
if (GIT_MERGE_INDEX_ENTRY_EXISTS(conflict_tgt->our_entry) &&
!GIT_MERGE_INDEX_ENTRY_EXISTS(conflict_src->our_entry)) {
similarity = similarity_fn(repo, &conflict_src->ancestor_entry, i, &conflict_tgt->our_entry, our_idx, cache, opts);
if (similarity == GIT_EBUFS)
continue;
else if (similarity < 0)
return similarity;
if (similarity > similarity_ours[i].similarity &&
similarity > similarity_ours[j].similarity) {
/* Clear previous best similarity */
if (similarity_ours[i].similarity > 0)
similarity_ours[similarity_ours[i].other_idx].similarity = 0;
if (similarity_ours[j].similarity > 0)
similarity_ours[similarity_ours[j].other_idx].similarity = 0;
similarity_ours[i].similarity = similarity;
similarity_ours[i].other_idx = j;
similarity_ours[j].similarity = similarity;
similarity_ours[j].other_idx = i;
}
}
if (GIT_MERGE_INDEX_ENTRY_EXISTS(conflict_tgt->their_entry) &&
!GIT_MERGE_INDEX_ENTRY_EXISTS(conflict_src->their_entry)) {
similarity = similarity_fn(repo, &conflict_src->ancestor_entry, i, &conflict_tgt->their_entry, their_idx, cache, opts);
if (similarity > similarity_theirs[i].similarity &&
similarity > similarity_theirs[j].similarity) {
/* Clear previous best similarity */
if (similarity_theirs[i].similarity > 0)
similarity_theirs[similarity_theirs[i].other_idx].similarity = 0;
if (similarity_theirs[j].similarity > 0)
similarity_theirs[similarity_theirs[j].other_idx].similarity = 0;
similarity_theirs[i].similarity = similarity;
similarity_theirs[i].other_idx = j;
similarity_theirs[j].similarity = similarity;
similarity_theirs[j].other_idx = i;
}
}
}
}
return 0;
}
/*
* Rename conflicts:
*
* Ancestor Ours Theirs
*
* 0a A A A No rename
* b A A* A No rename (ours was rewritten)
* c A A A* No rename (theirs rewritten)
* 1a A A B[A] Rename or rename/edit
* b A B[A] A (automergeable)
* 2 A B[A] B[A] Both renamed (automergeable)
* 3a A B[A] Rename/delete
* b A B[A] (same)
* 4a A B[A] B Rename/add [B~ours B~theirs]
* b A B B[A] (same)
* 5 A B[A] C[A] Both renamed ("1 -> 2")
* 6 A C[A] Both renamed ("2 -> 1")
* B C[B] [C~ours C~theirs] (automergeable)
*/
static void merge_diff_mark_rename_conflict(
git_merge_diff_list *diff_list,
struct merge_diff_similarity *similarity_ours,
bool ours_renamed,
size_t ours_source_idx,
struct merge_diff_similarity *similarity_theirs,
bool theirs_renamed,
size_t theirs_source_idx,
git_merge_diff *target,
const git_merge_tree_opts *opts)
{
git_merge_diff *ours_source = NULL, *theirs_source = NULL;
if (ours_renamed)
ours_source = diff_list->conflicts.contents[ours_source_idx];
if (theirs_renamed)
theirs_source = diff_list->conflicts.contents[theirs_source_idx];
/* Detect 2->1 conflicts */
if (ours_renamed && theirs_renamed) {
/* Both renamed to the same target name. */
if (ours_source_idx == theirs_source_idx)
ours_source->type = GIT_MERGE_DIFF_BOTH_RENAMED;
else {
ours_source->type = GIT_MERGE_DIFF_BOTH_RENAMED_2_TO_1;
theirs_source->type = GIT_MERGE_DIFF_BOTH_RENAMED_2_TO_1;
}
} else if (ours_renamed) {
/* If our source was also renamed in theirs, this is a 1->2 */
if (similarity_theirs[ours_source_idx].similarity >= opts->rename_threshold)
ours_source->type = GIT_MERGE_DIFF_BOTH_RENAMED_1_TO_2;
else if (GIT_MERGE_INDEX_ENTRY_EXISTS(target->their_entry)) {
ours_source->type = GIT_MERGE_DIFF_RENAMED_ADDED;
target->type = GIT_MERGE_DIFF_RENAMED_ADDED;
}
else if (!GIT_MERGE_INDEX_ENTRY_EXISTS(ours_source->their_entry))
ours_source->type = GIT_MERGE_DIFF_RENAMED_DELETED;
else if (ours_source->type == GIT_MERGE_DIFF_MODIFIED_DELETED)
ours_source->type = GIT_MERGE_DIFF_RENAMED_MODIFIED;
} else if (theirs_renamed) {
/* If their source was also renamed in ours, this is a 1->2 */
if (similarity_ours[theirs_source_idx].similarity >= opts->rename_threshold)
theirs_source->type = GIT_MERGE_DIFF_BOTH_RENAMED_1_TO_2;
else if (GIT_MERGE_INDEX_ENTRY_EXISTS(target->our_entry)) {
theirs_source->type = GIT_MERGE_DIFF_RENAMED_ADDED;
target->type = GIT_MERGE_DIFF_RENAMED_ADDED;
}
else if (!GIT_MERGE_INDEX_ENTRY_EXISTS(theirs_source->our_entry))
theirs_source->type = GIT_MERGE_DIFF_RENAMED_DELETED;
else if (theirs_source->type == GIT_MERGE_DIFF_MODIFIED_DELETED)
theirs_source->type = GIT_MERGE_DIFF_RENAMED_MODIFIED;
}
}
GIT_INLINE(void) merge_diff_coalesce_rename(
git_index_entry *source_entry,
git_delta_t *source_status,
git_index_entry *target_entry,
git_delta_t *target_status)
{
/* Coalesce the rename target into the rename source. */
memcpy(source_entry, target_entry, sizeof(git_index_entry));
*source_status = GIT_DELTA_RENAMED;
memset(target_entry, 0x0, sizeof(git_index_entry));
*target_status = GIT_DELTA_UNMODIFIED;
}
static void merge_diff_list_coalesce_renames(
git_merge_diff_list *diff_list,
struct merge_diff_similarity *similarity_ours,
struct merge_diff_similarity *similarity_theirs,
const git_merge_tree_opts *opts)
{
size_t i;
bool ours_renamed = 0, theirs_renamed = 0;
size_t ours_source_idx = 0, theirs_source_idx = 0;
git_merge_diff *ours_source, *theirs_source, *target;
for (i = 0; i < diff_list->conflicts.length; i++) {
target = diff_list->conflicts.contents[i];
ours_renamed = 0;
theirs_renamed = 0;
if (GIT_MERGE_INDEX_ENTRY_EXISTS(target->our_entry) &&
similarity_ours[i].similarity >= opts->rename_threshold) {
ours_source_idx = similarity_ours[i].other_idx;
ours_source = diff_list->conflicts.contents[ours_source_idx];
merge_diff_coalesce_rename(
&ours_source->our_entry,
&ours_source->our_status,
&target->our_entry,
&target->our_status);
similarity_ours[ours_source_idx].similarity = 0;
similarity_ours[i].similarity = 0;
ours_renamed = 1;
}
/* insufficient to determine direction */
if (GIT_MERGE_INDEX_ENTRY_EXISTS(target->their_entry) &&
similarity_theirs[i].similarity >= opts->rename_threshold) {
theirs_source_idx = similarity_theirs[i].other_idx;
theirs_source = diff_list->conflicts.contents[theirs_source_idx];
merge_diff_coalesce_rename(
&theirs_source->their_entry,
&theirs_source->their_status,
&target->their_entry,
&target->their_status);
similarity_theirs[theirs_source_idx].similarity = 0;
similarity_theirs[i].similarity = 0;
theirs_renamed = 1;
}
merge_diff_mark_rename_conflict(diff_list,
similarity_ours, ours_renamed, ours_source_idx,
similarity_theirs, theirs_renamed, theirs_source_idx,
target, opts);
}
}
static int merge_diff_empty(const git_vector *conflicts, size_t idx)
{
git_merge_diff *conflict = conflicts->contents[idx];
return (!GIT_MERGE_INDEX_ENTRY_EXISTS(conflict->ancestor_entry) &&
!GIT_MERGE_INDEX_ENTRY_EXISTS(conflict->our_entry) &&
!GIT_MERGE_INDEX_ENTRY_EXISTS(conflict->their_entry));
}
static void merge_diff_list_count_candidates(
git_merge_diff_list *diff_list,
size_t *src_count,
size_t *tgt_count)
{
git_merge_diff *entry;
size_t i;
*src_count = 0;
*tgt_count = 0;
git_vector_foreach(&diff_list->conflicts, i, entry) {
if (GIT_MERGE_INDEX_ENTRY_EXISTS(entry->ancestor_entry) &&
(!GIT_MERGE_INDEX_ENTRY_EXISTS(entry->our_entry) ||
!GIT_MERGE_INDEX_ENTRY_EXISTS(entry->their_entry)))
src_count++;
else if (!GIT_MERGE_INDEX_ENTRY_EXISTS(entry->ancestor_entry))
tgt_count++;
}
}
int git_merge_diff_list__find_renames(
git_repository *repo,
git_merge_diff_list *diff_list,
const git_merge_tree_opts *opts)
{
struct merge_diff_similarity *similarity_ours, *similarity_theirs;
void **cache = NULL;
size_t cache_size = 0;
size_t src_count, tgt_count, i;
int error = 0;
assert(diff_list && opts);
if ((opts->flags & GIT_MERGE_TREE_FIND_RENAMES) == 0)
return 0;
similarity_ours = git__calloc(diff_list->conflicts.length,
sizeof(struct merge_diff_similarity));
GITERR_CHECK_ALLOC(similarity_ours);
similarity_theirs = git__calloc(diff_list->conflicts.length,
sizeof(struct merge_diff_similarity));
GITERR_CHECK_ALLOC(similarity_theirs);
/* Calculate similarity between items that were deleted from the ancestor
* and added in the other branch.
*/
if ((error = merge_diff_mark_similarity(repo, diff_list, similarity_ours,
similarity_theirs, index_entry_similarity_exact, NULL, opts)) < 0)
goto done;
if (diff_list->conflicts.length <= opts->target_limit) {
cache_size = diff_list->conflicts.length * 3;
cache = git__calloc(cache_size, sizeof(void *));
GITERR_CHECK_ALLOC(cache);
merge_diff_list_count_candidates(diff_list, &src_count, &tgt_count);
if (src_count > opts->target_limit || tgt_count > opts->target_limit) {
/* TODO: report! */
} else {
if ((error = merge_diff_mark_similarity(
repo, diff_list, similarity_ours, similarity_theirs,
index_entry_similarity_inexact, cache, opts)) < 0)
goto done;
}
}
/* For entries that are appropriately similar, merge the new name's entry
* into the old name.
*/
merge_diff_list_coalesce_renames(diff_list, similarity_ours, similarity_theirs, opts);
/* And remove any entries that were merged and are now empty. */
git_vector_remove_matching(&diff_list->conflicts, merge_diff_empty);
done:
if (cache != NULL) {
for (i = 0; i < cache_size; ++i) {
if (cache[i] != NULL)
opts->metric->free_signature(cache[i], opts->metric->payload);
}
git__free(cache);
}
git__free(similarity_ours);
git__free(similarity_theirs);
return error;
}
/* Directory/file conflict handling */
GIT_INLINE(const char *) merge_diff_path(
......@@ -951,6 +1386,43 @@ static int merge_tree_normalize_opts(
else {
git_merge_tree_opts init = GIT_MERGE_TREE_OPTS_INIT;
memcpy(opts, &init, sizeof(init));
opts->flags = GIT_MERGE_TREE_FIND_RENAMES;
opts->rename_threshold = GIT_MERGE_TREE_RENAME_THRESHOLD;
}
if (!opts->target_limit) {
int32_t limit = 0;
opts->target_limit = GIT_MERGE_TREE_TARGET_LIMIT;
if (git_config_get_int32(&limit, cfg, "merge.renameLimit") < 0) {
giterr_clear();
if (git_config_get_int32(&limit, cfg, "diff.renameLimit") < 0)
giterr_clear();
}
if (limit > 0)
opts->target_limit = limit;
}
/* assign the internal metric with whitespace flag as payload */
if (!opts->metric) {
opts->metric = git__malloc(sizeof(git_diff_similarity_metric));
GITERR_CHECK_ALLOC(opts->metric);
opts->metric->file_signature = git_diff_find_similar__hashsig_for_file;
opts->metric->buffer_signature = git_diff_find_similar__hashsig_for_buf;
opts->metric->free_signature = git_diff_find_similar__hashsig_free;
opts->metric->similarity = git_diff_find_similar__calc_similarity;
if (opts->flags & GIT_DIFF_FIND_IGNORE_WHITESPACE)
opts->metric->payload = (void *)GIT_HASHSIG_IGNORE_WHITESPACE;
else if (opts->flags & GIT_DIFF_FIND_DONT_IGNORE_WHITESPACE)
opts->metric->payload = (void *)GIT_HASHSIG_NORMAL;
else
opts->metric->payload = (void *)GIT_HASHSIG_SMART_WHITESPACE;
}
return 0;
......@@ -1035,6 +1507,12 @@ int index_from_diff_list(git_index **out, git_merge_diff_list *diff_list)
their_path =
GIT_MERGE_INDEX_ENTRY_EXISTS(conflict->their_entry) ?
conflict->their_entry.path : NULL;
if ((our_path && strcmp(ancestor_path, our_path) != 0) ||
(their_path && strcmp(ancestor_path, their_path) != 0)) {
if ((error = git_index_name_add(index, ancestor_path, our_path, their_path)) < 0)
goto on_error;
}
}
/* Add each entry in the resolved conflict to the REUC independently, since
......@@ -1099,7 +1577,8 @@ int git_merge_trees(
diff_list = git_merge_diff_list__alloc(repo);
GITERR_CHECK_ALLOC(diff_list);
if ((error = git_merge_diff_list__find_differences(diff_list, ancestor_tree, our_tree, their_tree)) < 0)
if ((error = git_merge_diff_list__find_differences(diff_list, ancestor_tree, our_tree, their_tree)) < 0 ||
(error = git_merge_diff_list__find_renames(repo, diff_list, &opts)) < 0)
goto done;
memcpy(&changes, &diff_list->conflicts, sizeof(git_vector));
......@@ -1115,6 +1594,9 @@ int git_merge_trees(
git_vector_insert(&diff_list->conflicts, conflict);
}
if (!given_opts || !given_opts->metric)
git__free(opts.metric);
error = index_from_diff_list(out, diff_list);
done:
......@@ -1134,3 +1616,4 @@ void git_merge_diff_list__free(git_merge_diff_list *diff_list)
git_pool_clear(&diff_list->pool);
git__free(diff_list);
}
#include "clar_libgit2.h"
#include "index.h"
#include "git2/repository.h"
#include "../reset/reset_helpers.h"
static git_repository *repo;
static git_index *repo_index;
#define TEST_REPO_PATH "mergedrepo"
#define TEST_INDEX_PATH TEST_REPO_PATH "/.git/index"
// Fixture setup and teardown
void test_index_names__initialize(void)
{
repo = cl_git_sandbox_init("mergedrepo");
git_repository_index(&repo_index, repo);
}
void test_index_names__cleanup(void)
{
git_index_free(repo_index);
repo_index = NULL;
cl_git_sandbox_cleanup();
}
void test_index_names__add(void)
{
const git_index_name_entry *conflict_name;
cl_git_pass(git_index_name_add(repo_index, "ancestor", "ours", "theirs"));
cl_git_pass(git_index_name_add(repo_index, "ancestor2", "ours2", NULL));
cl_git_pass(git_index_name_add(repo_index, "ancestor3", NULL, "theirs3"));
cl_assert(git_index_name_entrycount(repo_index) == 3);
conflict_name = git_index_name_get_byindex(repo_index, 0);
cl_assert(strcmp(conflict_name->ancestor, "ancestor") == 0);
cl_assert(strcmp(conflict_name->ours, "ours") == 0);
cl_assert(strcmp(conflict_name->theirs, "theirs") == 0);
conflict_name = git_index_name_get_byindex(repo_index, 1);
cl_assert(strcmp(conflict_name->ancestor, "ancestor2") == 0);
cl_assert(strcmp(conflict_name->ours, "ours2") == 0);
cl_assert(conflict_name->theirs == NULL);
conflict_name = git_index_name_get_byindex(repo_index, 2);
cl_assert(strcmp(conflict_name->ancestor, "ancestor3") == 0);
cl_assert(conflict_name->ours == NULL);
cl_assert(strcmp(conflict_name->theirs, "theirs3") == 0);
}
void test_index_names__roundtrip(void)
{
const git_index_name_entry *conflict_name;
cl_git_pass(git_index_name_add(repo_index, "ancestor", "ours", "theirs"));
cl_git_pass(git_index_name_add(repo_index, "ancestor2", "ours2", NULL));
cl_git_pass(git_index_name_add(repo_index, "ancestor3", NULL, "theirs3"));
cl_git_pass(git_index_write(repo_index));
git_index_clear(repo_index);
cl_assert(git_index_name_entrycount(repo_index) == 0);
cl_git_pass(git_index_read(repo_index));
cl_assert(git_index_name_entrycount(repo_index) == 3);
conflict_name = git_index_name_get_byindex(repo_index, 0);
cl_assert(strcmp(conflict_name->ancestor, "ancestor") == 0);
cl_assert(strcmp(conflict_name->ours, "ours") == 0);
cl_assert(strcmp(conflict_name->theirs, "theirs") == 0);
conflict_name = git_index_name_get_byindex(repo_index, 1);
cl_assert(strcmp(conflict_name->ancestor, "ancestor2") == 0);
cl_assert(strcmp(conflict_name->ours, "ours2") == 0);
cl_assert(conflict_name->theirs == NULL);
conflict_name = git_index_name_get_byindex(repo_index, 2);
cl_assert(strcmp(conflict_name->ancestor, "ancestor3") == 0);
cl_assert(conflict_name->ours == NULL);
cl_assert(strcmp(conflict_name->theirs, "theirs3") == 0);
}
......@@ -51,6 +51,58 @@ int merge_trees_from_branches(
return 0;
}
static void 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");
}
static void 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");
}
static void 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;
......@@ -84,6 +136,16 @@ static int name_entry_eq(const char *expected, const char *actual)
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((const struct merge_index_entry *)&expected->ancestor, &actual->ancestor_entry) ||
......@@ -139,6 +201,29 @@ int merge_test_index(git_index *index, const struct merge_index_entry expected[]
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;
......
#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"
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_trees_renames__initialize(void)
{
repo = cl_git_sandbox_init(TEST_REPO_PATH);
}
void test_merge_trees_renames__cleanup(void)
{
cl_git_sandbox_cleanup();
}
void test_merge_trees_renames__index(void)
{
git_index *index;
git_merge_tree_opts *opts = NULL;
struct merge_index_entry merge_index_entries[] = {
{ 0100644, "68c6c84b091926c7d90aa6a79b2bc3bb6adccd8e", 0, "0a-no-change.txt" },
{ 0100644, "f0ce2b8e4986084d9b308fb72709e414c23eb5e6", 0, "0b-duplicated-in-ours.txt" },
{ 0100644, "f0ce2b8e4986084d9b308fb72709e414c23eb5e6", 1, "0b-rewritten-in-ours.txt" },
{ 0100644, "e376fbdd06ebf021c92724da9f26f44212734e3e", 2, "0b-rewritten-in-ours.txt" },
{ 0100644, "b2d399ae15224e1d58066e3c8df70ce37de7a656", 3, "0b-rewritten-in-ours.txt" },
{ 0100644, "2f56120107d680129a5d9791b521cb1e73a2ed31", 0, "0c-duplicated-in-theirs.txt" },
{ 0100644, "2f56120107d680129a5d9791b521cb1e73a2ed31", 1, "0c-rewritten-in-theirs.txt" },
{ 0100644, "efc9121fdedaf08ba180b53ebfbcf71bd488ed09", 2, "0c-rewritten-in-theirs.txt" },
{ 0100644, "712ebba6669ea847d9829e4f1059d6c830c8b531", 3, "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", 2, "3a-newname-in-ours-deleted-in-theirs.txt" },
{ 0100644, "18cb316b1cefa0f8a6946f0e201a8e1a6f845ab9", 1, "3a-renamed-in-ours-deleted-in-theirs.txt" },
{ 0100644, "36219b49367146cb2e6a1555b5a9ebd4d0328495", 3, "3b-newname-in-theirs-deleted-in-ours.txt" },
{ 0100644, "36219b49367146cb2e6a1555b5a9ebd4d0328495", 1, "3b-renamed-in-theirs-deleted-in-ours.txt" },
{ 0100644, "227792b52aaa0b238bea00ec7e509b02623f168c", 2, "4a-newname-in-ours-added-in-theirs.txt" },
{ 0100644, "8b5b53cb2aa9ceb1139f5312fcfa3cc3c5a47c9a", 3, "4a-newname-in-ours-added-in-theirs.txt" },
{ 0100644, "227792b52aaa0b238bea00ec7e509b02623f168c", 1, "4a-renamed-in-ours-added-in-theirs.txt" },
{ 0100644, "de872ee3618b894992e9d1e18ba2ebe256a112f9", 2, "4b-newname-in-theirs-added-in-ours.txt" },
{ 0100644, "98d52d07c0b0bbf2b46548f6aa521295c2cb55db", 3, "4b-newname-in-theirs-added-in-ours.txt" },
{ 0100644, "98d52d07c0b0bbf2b46548f6aa521295c2cb55db", 1, "4b-renamed-in-theirs-added-in-ours.txt" },
{ 0100644, "d3719a5ae8e4d92276b5313ce976f6ee5af2b436", 2, "5a-newname-in-ours-added-in-theirs.txt" },
{ 0100644, "98ba4205fcf31f5dd93c916d35fe3f3b3d0e6714", 3, "5a-newname-in-ours-added-in-theirs.txt" },
{ 0100644, "d3719a5ae8e4d92276b5313ce976f6ee5af2b436", 1, "5a-renamed-in-ours-added-in-theirs.txt" },
{ 0100644, "d3719a5ae8e4d92276b5313ce976f6ee5af2b436", 3, "5a-renamed-in-ours-added-in-theirs.txt" },
{ 0100644, "385c8a0f26ddf79e9041e15e17dc352ed2c4cced", 2, "5b-newname-in-theirs-added-in-ours.txt" },
{ 0100644, "63247125386de9ec90a27ad36169307bf8a11a38", 3, "5b-newname-in-theirs-added-in-ours.txt" },
{ 0100644, "63247125386de9ec90a27ad36169307bf8a11a38", 1, "5b-renamed-in-theirs-added-in-ours.txt" },
{ 0100644, "63247125386de9ec90a27ad36169307bf8a11a38", 2, "5b-renamed-in-theirs-added-in-ours.txt" },
{ 0100644, "d8fa77b6833082c1ea36b7828a582d4c43882450", 2, "6-both-renamed-1-to-2-ours.txt" },
{ 0100644, "d8fa77b6833082c1ea36b7828a582d4c43882450", 3, "6-both-renamed-1-to-2-theirs.txt" },
{ 0100644, "d8fa77b6833082c1ea36b7828a582d4c43882450", 1, "6-both-renamed-1-to-2.txt" },
{ 0100644, "b42712cfe99a1a500b2a51fe984e0b8a7702ba11", 1, "7-both-renamed-side-1.txt" },
{ 0100644, "b42712cfe99a1a500b2a51fe984e0b8a7702ba11", 3, "7-both-renamed-side-1.txt" },
{ 0100644, "b69fe837e4cecfd4c9a40cdca7c138468687df07", 1, "7-both-renamed-side-2.txt" },
{ 0100644, "b69fe837e4cecfd4c9a40cdca7c138468687df07", 2, "7-both-renamed-side-2.txt" },
{ 0100644, "b42712cfe99a1a500b2a51fe984e0b8a7702ba11", 2, "7-both-renamed.txt" },
{ 0100644, "b69fe837e4cecfd4c9a40cdca7c138468687df07", 3, "7-both-renamed.txt" },
};
struct merge_name_entry merge_name_entries[] = {
{
"3a-renamed-in-ours-deleted-in-theirs.txt",
"3a-newname-in-ours-deleted-in-theirs.txt",
""
},
{
"3b-renamed-in-theirs-deleted-in-ours.txt",
"",
"3b-newname-in-theirs-deleted-in-ours.txt",
},
{
"4a-renamed-in-ours-added-in-theirs.txt",
"4a-newname-in-ours-added-in-theirs.txt",
"",
},
{
"4b-renamed-in-theirs-added-in-ours.txt",
"",
"4b-newname-in-theirs-added-in-ours.txt",
},
{
"5a-renamed-in-ours-added-in-theirs.txt",
"5a-newname-in-ours-added-in-theirs.txt",
"5a-renamed-in-ours-added-in-theirs.txt",
},
{
"5b-renamed-in-theirs-added-in-ours.txt",
"5b-renamed-in-theirs-added-in-ours.txt",
"5b-newname-in-theirs-added-in-ours.txt",
},
{
"6-both-renamed-1-to-2.txt",
"6-both-renamed-1-to-2-ours.txt",
"6-both-renamed-1-to-2-theirs.txt",
},
{
"7-both-renamed-side-1.txt",
"7-both-renamed.txt",
"7-both-renamed-side-1.txt",
},
{
"7-both-renamed-side-2.txt",
"7-both-renamed-side-2.txt",
"7-both-renamed.txt",
},
};
struct merge_reuc_entry merge_reuc_entries[] = {
{ "1a-newname-in-ours-edited-in-theirs.txt",
0, 0100644, 0,
"",
"c3d02eeef75183df7584d8d13ac03053910c1301",
"" },
{ "1a-newname-in-ours.txt",
0, 0100644, 0,
"",
"d0d4594e16f2e19107e3fa7ea63e7aaaff305ffb",
"" },
{ "1a-renamed-in-ours-edited-in-theirs.txt",
0100644, 0, 0100644,
"c3d02eeef75183df7584d8d13ac03053910c1301",
"",
"0d872f8e871a30208305978ecbf9e66d864f1638" },
{ "1a-renamed-in-ours.txt",
0100644, 0, 0100644,
"d0d4594e16f2e19107e3fa7ea63e7aaaff305ffb",
"",
"d0d4594e16f2e19107e3fa7ea63e7aaaff305ffb" },
{ "1b-newname-in-theirs-edited-in-ours.txt",
0, 0, 0100644,
"",
"",
"241a1005cd9b980732741b74385b891142bcba28" },
{ "1b-newname-in-theirs.txt",
0, 0, 0100644,
"",
"",
"2b5f1f181ee3b58ea751f5dd5d8f9b445520a136" },
{ "1b-renamed-in-theirs-edited-in-ours.txt",
0100644, 0100644, 0,
"241a1005cd9b980732741b74385b891142bcba28",
"ed9523e62e453e50dd9be1606af19399b96e397a",
"" },
{ "1b-renamed-in-theirs.txt",
0100644, 0100644, 0,
"2b5f1f181ee3b58ea751f5dd5d8f9b445520a136",
"2b5f1f181ee3b58ea751f5dd5d8f9b445520a136",
"" },
{ "2-newname-in-both.txt",
0, 0100644, 0100644,
"",
"178940b450f238a56c0d75b7955cb57b38191982",
"178940b450f238a56c0d75b7955cb57b38191982" },
{ "2-renamed-in-both.txt",
0100644, 0, 0,
"178940b450f238a56c0d75b7955cb57b38191982",
"",
"" },
};
cl_git_pass(merge_trees_from_branches(&index, repo,
BRANCH_RENAME_OURS, BRANCH_RENAME_THEIRS,
opts));
cl_assert(merge_test_index(index, merge_index_entries, 41));
cl_assert(merge_test_names(index, merge_name_entries, 9));
cl_assert(merge_test_reuc(index, merge_reuc_entries, 10));
git_index_free(index);
}
void test_merge_trees_renames__no_rename_index(void)
{
git_index *index;
git_merge_tree_opts opts = GIT_MERGE_TREE_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, "f0ce2b8e4986084d9b308fb72709e414c23eb5e6", 1, "0b-rewritten-in-ours.txt" },
{ 0100644, "e376fbdd06ebf021c92724da9f26f44212734e3e", 2, "0b-rewritten-in-ours.txt" },
{ 0100644, "b2d399ae15224e1d58066e3c8df70ce37de7a656", 3, "0b-rewritten-in-ours.txt" },
{ 0100644, "2f56120107d680129a5d9791b521cb1e73a2ed31", 0, "0c-duplicated-in-theirs.txt" },
{ 0100644, "2f56120107d680129a5d9791b521cb1e73a2ed31", 1, "0c-rewritten-in-theirs.txt" },
{ 0100644, "efc9121fdedaf08ba180b53ebfbcf71bd488ed09", 2, "0c-rewritten-in-theirs.txt" },
{ 0100644, "712ebba6669ea847d9829e4f1059d6c830c8b531", 3, "0c-rewritten-in-theirs.txt" },
{ 0100644, "c3d02eeef75183df7584d8d13ac03053910c1301", 0, "1a-newname-in-ours-edited-in-theirs.txt" },
{ 0100644, "d0d4594e16f2e19107e3fa7ea63e7aaaff305ffb", 0, "1a-newname-in-ours.txt" },
{ 0100644, "c3d02eeef75183df7584d8d13ac03053910c1301", 1, "1a-renamed-in-ours-edited-in-theirs.txt" },
{ 0100644, "0d872f8e871a30208305978ecbf9e66d864f1638", 3, "1a-renamed-in-ours-edited-in-theirs.txt" },
{ 0100644, "241a1005cd9b980732741b74385b891142bcba28", 0, "1b-newname-in-theirs-edited-in-ours.txt" },
{ 0100644, "2b5f1f181ee3b58ea751f5dd5d8f9b445520a136", 0, "1b-newname-in-theirs.txt" },
{ 0100644, "241a1005cd9b980732741b74385b891142bcba28", 1, "1b-renamed-in-theirs-edited-in-ours.txt" },
{ 0100644, "ed9523e62e453e50dd9be1606af19399b96e397a", 2, "1b-renamed-in-theirs-edited-in-ours.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", 2, "4a-newname-in-ours-added-in-theirs.txt" },
{ 0100644, "8b5b53cb2aa9ceb1139f5312fcfa3cc3c5a47c9a", 3, "4a-newname-in-ours-added-in-theirs.txt" },
{ 0100644, "de872ee3618b894992e9d1e18ba2ebe256a112f9", 2, "4b-newname-in-theirs-added-in-ours.txt" },
{ 0100644, "98d52d07c0b0bbf2b46548f6aa521295c2cb55db", 3, "4b-newname-in-theirs-added-in-ours.txt" },
{ 0100644, "d3719a5ae8e4d92276b5313ce976f6ee5af2b436", 2, "5a-newname-in-ours-added-in-theirs.txt" },
{ 0100644, "98ba4205fcf31f5dd93c916d35fe3f3b3d0e6714", 3, "5a-newname-in-ours-added-in-theirs.txt" },
{ 0100644, "385c8a0f26ddf79e9041e15e17dc352ed2c4cced", 2, "5b-newname-in-theirs-added-in-ours.txt" },
{ 0100644, "63247125386de9ec90a27ad36169307bf8a11a38", 3, "5b-newname-in-theirs-added-in-ours.txt" },
{ 0100644, "d8fa77b6833082c1ea36b7828a582d4c43882450", 0, "6-both-renamed-1-to-2-ours.txt" },
{ 0100644, "d8fa77b6833082c1ea36b7828a582d4c43882450", 0, "6-both-renamed-1-to-2-theirs.txt" },
{ 0100644, "b42712cfe99a1a500b2a51fe984e0b8a7702ba11", 2, "7-both-renamed.txt" },
{ 0100644, "b69fe837e4cecfd4c9a40cdca7c138468687df07", 3, "7-both-renamed.txt" },
};
cl_git_pass(merge_trees_from_branches(&index, repo,
BRANCH_RENAME_OURS, BRANCH_RENAME_THEIRS,
&opts));
cl_assert(merge_test_index(index, merge_index_entries, 32));
git_index_free(index);
}
......@@ -12,11 +12,17 @@ static git_repository *repo;
#define TREE_OID_ANCESTOR "0d52e3a556e189ba0948ae56780918011c1b167d"
#define TREE_OID_MASTER "1f81433e3161efbf250576c58fede7f6b836f3d3"
#define TREE_OID_BRANCH "eea9286df54245fea72c5b557291470eb825f38f"
#define TREE_OID_RENAMES1 "f5f9dd5886a6ee20272be0aafc790cba43b31931"
#define TREE_OID_RENAMES2 "5fbfbdc04b4eca46f54f4853a3c5a1dce28f5165"
#define TREE_OID_DF_ANCESTOR "b8a3a806d3950e8c0a03a34f234a92eff0e2c68d"
#define TREE_OID_DF_SIDE1 "ee1d6f164893c1866a323f072eeed36b855656be"
#define TREE_OID_DF_SIDE2 "6178885b38fe96e825ac0f492c0a941f288b37f6"
#define TREE_OID_RENAME_CONFLICT_ANCESTOR "476dbb3e207313d1d8aaa120c6ad204bf1295e53"
#define TREE_OID_RENAME_CONFLICT_OURS "c4efe31e9decccc8b2b4d3df9aac2cdfe2995618"
#define TREE_OID_RENAME_CONFLICT_THEIRS "9e7f4359c469f309b6057febf4c6e80742cbed5b"
void test_merge_trees_treediff__initialize(void)
{
repo = cl_git_sandbox_init(TEST_REPO_PATH);
......@@ -46,6 +52,20 @@ static void test_find_differences(
git_tree *ancestor_tree, *ours_tree, *theirs_tree;
struct treediff_cb_data treediff_cb_data = {0};
git_merge_tree_opts opts = GIT_MERGE_TREE_OPTS_INIT;
opts.flags |= GIT_MERGE_TREE_FIND_RENAMES;
opts.target_limit = 1000;
opts.rename_threshold = 50;
opts.metric = git__malloc(sizeof(git_diff_similarity_metric));
cl_assert(opts.metric != NULL);
opts.metric->file_signature = git_diff_find_similar__hashsig_for_file;
opts.metric->buffer_signature = git_diff_find_similar__hashsig_for_buf;
opts.metric->free_signature = git_diff_find_similar__hashsig_free;
opts.metric->similarity = git_diff_find_similar__calc_similarity;
opts.metric->payload = (void *)GIT_HASHSIG_SMART_WHITESPACE;
cl_git_pass(git_oid_fromstr(&ancestor_oid, ancestor_oidstr));
cl_git_pass(git_oid_fromstr(&ours_oid, ours_oidstr));
cl_git_pass(git_oid_fromstr(&theirs_oid, theirs_oidstr));
......@@ -55,6 +75,7 @@ static void test_find_differences(
cl_git_pass(git_tree_lookup(&theirs_tree, repo, &theirs_oid));
cl_git_pass(git_merge_diff_list__find_differences(merge_diff_list, ancestor_tree, ours_tree, theirs_tree));
cl_git_pass(git_merge_diff_list__find_renames(repo, merge_diff_list, &opts));
/*
dump_merge_index(merge_index);
......@@ -72,6 +93,8 @@ static void test_find_differences(
git_tree_free(theirs_tree);
git_merge_diff_list__free(merge_diff_list);
git__free(opts.metric);
}
void test_merge_trees_treediff__simple(void)
......@@ -277,3 +300,255 @@ void test_merge_trees_treediff__df_conflicts(void)
test_find_differences(TREE_OID_DF_ANCESTOR, TREE_OID_DF_SIDE1, TREE_OID_DF_SIDE2, treediff_conflict_data, 20);
}
void test_merge_trees_treediff__strict_renames(void)
{
struct merge_index_conflict_data treediff_conflict_data[] = {
{
{ 0, "", 0, "", GIT_DELTA_UNMODIFIED },
{ 0100644, "233c0919c998ed110a4b6ff36f353aec8b713487", 0, "added-in-master.txt", GIT_DELTA_ADDED },
{ 0, "", 0, "", GIT_DELTA_UNMODIFIED },
GIT_MERGE_DIFF_NONE,
},
{
{ 0100644, "6212c31dab5e482247d7977e4f0dd3601decf13b", 0, "automergeable.txt", GIT_DELTA_UNMODIFIED },
{ 0100644, "ee3fa1b8c00aff7fe02065fdb50864bb0d932ccf", 0, "automergeable.txt", GIT_DELTA_MODIFIED },
{ 0100644, "6212c31dab5e482247d7977e4f0dd3601decf13b", 0, "automergeable.txt", GIT_DELTA_UNMODIFIED },
GIT_MERGE_DIFF_NONE,
},
{
{ 0100644, "ab6c44a2e84492ad4b41bb6bac87353e9d02ac8b", 0, "changed-in-master.txt", GIT_DELTA_UNMODIFIED },
{ 0100644, "11deab00b2d3a6f5a3073988ac050c2d7b6655e2", 0, "changed-in-master.txt", GIT_DELTA_MODIFIED },
{ 0100644, "ab6c44a2e84492ad4b41bb6bac87353e9d02ac8b", 0, "changed-in-master.txt", GIT_DELTA_UNMODIFIED },
GIT_MERGE_DIFF_NONE,
},
{
{ 0100644, "d427e0b2e138501a3d15cc376077a3631e15bd46", 0, "conflicting.txt", GIT_DELTA_UNMODIFIED },
{ 0100644, "4e886e602529caa9ab11d71f86634bd1b6e0de10", 0, "conflicting.txt", GIT_DELTA_MODIFIED },
{ 0100644, "d427e0b2e138501a3d15cc376077a3631e15bd46", 0, "conflicting.txt", GIT_DELTA_UNMODIFIED },
GIT_MERGE_DIFF_NONE,
},
{
{ 0100644, "dfe3f22baa1f6fce5447901c3086bae368de6bdd", 0, "removed-in-branch.txt", GIT_DELTA_UNMODIFIED },
{ 0100644, "dfe3f22baa1f6fce5447901c3086bae368de6bdd", 0, "removed-in-branch.txt", GIT_DELTA_UNMODIFIED },
{ 0100644, "dfe3f22baa1f6fce5447901c3086bae368de6bdd", 0, "renamed-in-branch.txt", GIT_DELTA_RENAMED },
GIT_MERGE_DIFF_NONE,
},
{
{ 0100644, "5c3b68a71fc4fa5d362fd3875e53137c6a5ab7a5", 0, "removed-in-master.txt", GIT_DELTA_UNMODIFIED },
{ 0, "", 0, "", GIT_DELTA_DELETED },
{ 0100644, "5c3b68a71fc4fa5d362fd3875e53137c6a5ab7a5", 0, "removed-in-master.txt", GIT_DELTA_UNMODIFIED },
GIT_MERGE_DIFF_NONE,
},
{
{ 0, "", 0, "", GIT_DELTA_UNMODIFIED },
{ 0, "", 0, "", GIT_DELTA_UNMODIFIED },
{ 0100644, "c8f06f2e3bb2964174677e91f0abead0e43c9e5d", 0, "renamed.txt", GIT_DELTA_ADDED },
GIT_MERGE_DIFF_NONE,
},
{
{ 0100644, "c8f06f2e3bb2964174677e91f0abead0e43c9e5d", 0, "unchanged.txt", GIT_DELTA_UNMODIFIED },
{ 0100644, "c8f06f2e3bb2964174677e91f0abead0e43c9e5d", 0, "unchanged.txt", GIT_DELTA_UNMODIFIED },
{ 0100644, "c8f06f2e3bb2964174677e91f0abead0e43c9e5d", 0, "copied.txt", GIT_DELTA_RENAMED },
GIT_MERGE_DIFF_NONE,
},
};
test_find_differences(TREE_OID_ANCESTOR, TREE_OID_MASTER, TREE_OID_RENAMES1, treediff_conflict_data, 8);
}
void test_merge_trees_treediff__rename_conflicts(void)
{
struct merge_index_conflict_data treediff_conflict_data[] = {
{
{ 0, "", 0, "", GIT_DELTA_UNMODIFIED },
{ 0100644, "f0ce2b8e4986084d9b308fb72709e414c23eb5e6", 0, "0b-duplicated-in-ours.txt", GIT_DELTA_ADDED },
{ 0, "", 0, "", GIT_DELTA_UNMODIFIED },
GIT_MERGE_DIFF_NONE,
},
{
{ 0100644, "f0ce2b8e4986084d9b308fb72709e414c23eb5e6", 0, "0b-rewritten-in-ours.txt", GIT_DELTA_UNMODIFIED },
{ 0100644, "e376fbdd06ebf021c92724da9f26f44212734e3e", 0, "0b-rewritten-in-ours.txt", GIT_DELTA_MODIFIED },
{ 0100644, "b2d399ae15224e1d58066e3c8df70ce37de7a656", 0, "0b-rewritten-in-ours.txt", GIT_DELTA_MODIFIED },
GIT_MERGE_DIFF_BOTH_MODIFIED,
},
{
{ 0, "", 0, "", GIT_DELTA_UNMODIFIED },
{ 0, "", 0, "", GIT_DELTA_UNMODIFIED },
{ 0100644, "2f56120107d680129a5d9791b521cb1e73a2ed31", 0, "0c-duplicated-in-theirs.txt", GIT_DELTA_ADDED },
GIT_MERGE_DIFF_NONE,
},
{
{ 0100644, "2f56120107d680129a5d9791b521cb1e73a2ed31", 0, "0c-rewritten-in-theirs.txt", GIT_DELTA_UNMODIFIED },
{ 0100644, "efc9121fdedaf08ba180b53ebfbcf71bd488ed09", 0, "0c-rewritten-in-theirs.txt", GIT_DELTA_MODIFIED },
{ 0100644, "712ebba6669ea847d9829e4f1059d6c830c8b531", 0, "0c-rewritten-in-theirs.txt", GIT_DELTA_MODIFIED },
GIT_MERGE_DIFF_BOTH_MODIFIED,
},
{
{ 0100644, "c3d02eeef75183df7584d8d13ac03053910c1301", 0, "1a-renamed-in-ours-edited-in-theirs.txt", GIT_DELTA_UNMODIFIED },
{ 0100644, "c3d02eeef75183df7584d8d13ac03053910c1301", 0, "1a-newname-in-ours-edited-in-theirs.txt", GIT_DELTA_RENAMED },
{ 0100644, "0d872f8e871a30208305978ecbf9e66d864f1638", 0, "1a-renamed-in-ours-edited-in-theirs.txt", GIT_DELTA_MODIFIED },
GIT_MERGE_DIFF_RENAMED_MODIFIED,
},
{
{ 0100644, "d0d4594e16f2e19107e3fa7ea63e7aaaff305ffb", 0, "1a-renamed-in-ours.txt", GIT_DELTA_UNMODIFIED },
{ 0100644, "d0d4594e16f2e19107e3fa7ea63e7aaaff305ffb", 0, "1a-newname-in-ours.txt", GIT_DELTA_RENAMED },
{ 0100644, "d0d4594e16f2e19107e3fa7ea63e7aaaff305ffb", 0, "1a-renamed-in-ours.txt", GIT_DELTA_UNMODIFIED },
GIT_MERGE_DIFF_NONE,
},
{
{ 0100644, "241a1005cd9b980732741b74385b891142bcba28", 0, "1b-renamed-in-theirs-edited-in-ours.txt", GIT_DELTA_UNMODIFIED },
{ 0100644, "ed9523e62e453e50dd9be1606af19399b96e397a", 0, "1b-renamed-in-theirs-edited-in-ours.txt", GIT_DELTA_MODIFIED },
{ 0100644, "241a1005cd9b980732741b74385b891142bcba28", 0, "1b-newname-in-theirs-edited-in-ours.txt", GIT_DELTA_RENAMED },
GIT_MERGE_DIFF_RENAMED_MODIFIED,
},
{
{ 0100644, "2b5f1f181ee3b58ea751f5dd5d8f9b445520a136", 0, "1b-renamed-in-theirs.txt", GIT_DELTA_UNMODIFIED },
{ 0100644, "2b5f1f181ee3b58ea751f5dd5d8f9b445520a136", 0, "1b-renamed-in-theirs.txt", GIT_DELTA_UNMODIFIED },
{ 0100644, "2b5f1f181ee3b58ea751f5dd5d8f9b445520a136", 0, "1b-newname-in-theirs.txt", GIT_DELTA_RENAMED },
GIT_MERGE_DIFF_NONE,
},
{
{ 0100644, "178940b450f238a56c0d75b7955cb57b38191982", 0, "2-renamed-in-both.txt", GIT_DELTA_UNMODIFIED },
{ 0100644, "178940b450f238a56c0d75b7955cb57b38191982", 0, "2-newname-in-both.txt", GIT_DELTA_RENAMED },
{ 0100644, "178940b450f238a56c0d75b7955cb57b38191982", 0, "2-newname-in-both.txt", GIT_DELTA_RENAMED },
GIT_MERGE_DIFF_BOTH_RENAMED,
},
{
{ 0100644, "18cb316b1cefa0f8a6946f0e201a8e1a6f845ab9", 0, "3a-renamed-in-ours-deleted-in-theirs.txt", GIT_DELTA_UNMODIFIED },
{ 0100644, "18cb316b1cefa0f8a6946f0e201a8e1a6f845ab9", 0, "3a-newname-in-ours-deleted-in-theirs.txt", GIT_DELTA_RENAMED },
{ 0, "", 0, "", GIT_DELTA_DELETED },
GIT_MERGE_DIFF_RENAMED_DELETED,
},
{
{ 0100644, "36219b49367146cb2e6a1555b5a9ebd4d0328495", 0, "3b-renamed-in-theirs-deleted-in-ours.txt", GIT_DELTA_UNMODIFIED },
{ 0, "", 0, "", GIT_DELTA_DELETED },
{ 0100644, "36219b49367146cb2e6a1555b5a9ebd4d0328495", 0, "3b-newname-in-theirs-deleted-in-ours.txt", GIT_DELTA_RENAMED },
GIT_MERGE_DIFF_RENAMED_DELETED,
},
{
{ 0, "", 0, "", GIT_DELTA_UNMODIFIED },
{ 0, "", 0, "", GIT_DELTA_UNMODIFIED },
{ 0100644, "8b5b53cb2aa9ceb1139f5312fcfa3cc3c5a47c9a", 0, "4a-newname-in-ours-added-in-theirs.txt", GIT_DELTA_ADDED },
GIT_MERGE_DIFF_RENAMED_ADDED,
},
{
{ 0100644, "227792b52aaa0b238bea00ec7e509b02623f168c", 0, "4a-renamed-in-ours-added-in-theirs.txt", GIT_DELTA_UNMODIFIED },
{ 0100644, "227792b52aaa0b238bea00ec7e509b02623f168c", 0, "4a-newname-in-ours-added-in-theirs.txt", GIT_DELTA_RENAMED },
{ 0, "", 0, "", GIT_DELTA_DELETED },
GIT_MERGE_DIFF_RENAMED_ADDED,
},
{
{ 0, "", 0, "", GIT_DELTA_UNMODIFIED },
{ 0100644, "de872ee3618b894992e9d1e18ba2ebe256a112f9", 0, "4b-newname-in-theirs-added-in-ours.txt", GIT_DELTA_ADDED },
{ 0, "", 0, "", GIT_DELTA_UNMODIFIED },
GIT_MERGE_DIFF_RENAMED_ADDED,
},
{
{ 0100644, "98d52d07c0b0bbf2b46548f6aa521295c2cb55db", 0, "4b-renamed-in-theirs-added-in-ours.txt", GIT_DELTA_UNMODIFIED },
{ 0, "", 0, "", GIT_DELTA_DELETED },
{ 0100644, "98d52d07c0b0bbf2b46548f6aa521295c2cb55db", 0, "4b-newname-in-theirs-added-in-ours.txt", GIT_DELTA_RENAMED },
GIT_MERGE_DIFF_RENAMED_ADDED,
},
{
{ 0100644, "d8fa77b6833082c1ea36b7828a582d4c43882450", 0, "5-both-renamed-1-to-2.txt", GIT_DELTA_UNMODIFIED },
{ 0100644, "d8fa77b6833082c1ea36b7828a582d4c43882450", 0, "5-both-renamed-1-to-2-ours.txt", GIT_DELTA_RENAMED },
{ 0100644, "d8fa77b6833082c1ea36b7828a582d4c43882450", 0, "5-both-renamed-1-to-2-theirs.txt", GIT_DELTA_RENAMED },
GIT_MERGE_DIFF_BOTH_RENAMED_1_TO_2,
},
{
{ 0100644, "b42712cfe99a1a500b2a51fe984e0b8a7702ba11", 0, "6-both-renamed-side-1.txt", GIT_DELTA_UNMODIFIED },
{ 0100644, "b42712cfe99a1a500b2a51fe984e0b8a7702ba11", 0, "6-both-renamed.txt", GIT_DELTA_RENAMED },
{ 0100644, "b42712cfe99a1a500b2a51fe984e0b8a7702ba11", 0, "6-both-renamed-side-1.txt", GIT_DELTA_UNMODIFIED },
GIT_MERGE_DIFF_BOTH_RENAMED_2_TO_1,
},
{
{ 0100644, "b69fe837e4cecfd4c9a40cdca7c138468687df07", 0, "6-both-renamed-side-2.txt", GIT_DELTA_UNMODIFIED },
{ 0100644, "b69fe837e4cecfd4c9a40cdca7c138468687df07", 0, "6-both-renamed-side-2.txt", GIT_DELTA_UNMODIFIED },
{ 0100644, "b69fe837e4cecfd4c9a40cdca7c138468687df07", 0, "6-both-renamed.txt", GIT_DELTA_RENAMED },
GIT_MERGE_DIFF_BOTH_RENAMED_2_TO_1,
},
};
test_find_differences(TREE_OID_RENAME_CONFLICT_ANCESTOR,
TREE_OID_RENAME_CONFLICT_OURS, TREE_OID_RENAME_CONFLICT_THEIRS, treediff_conflict_data, 18);
}
void test_merge_trees_treediff__best_renames(void)
{
struct merge_index_conflict_data treediff_conflict_data[] = {
{
{ 0, "", 0, "", GIT_DELTA_UNMODIFIED },
{ 0100644, "233c0919c998ed110a4b6ff36f353aec8b713487", 0, "added-in-master.txt", GIT_DELTA_ADDED },
{ 0, "", 0, "", GIT_DELTA_UNMODIFIED },
GIT_MERGE_DIFF_NONE,
},
{
{ 0100644, "6212c31dab5e482247d7977e4f0dd3601decf13b", 0, "automergeable.txt", GIT_DELTA_UNMODIFIED },
{ 0100644, "ee3fa1b8c00aff7fe02065fdb50864bb0d932ccf", 0, "automergeable.txt", GIT_DELTA_MODIFIED },
{ 0100644, "45299c1ca5e07bba1fd90843056fb559f96b1f5a", 0, "renamed-90.txt", GIT_DELTA_RENAMED },
GIT_MERGE_DIFF_RENAMED_MODIFIED,
},
{
{ 0100644, "ab6c44a2e84492ad4b41bb6bac87353e9d02ac8b", 0, "changed-in-master.txt", GIT_DELTA_UNMODIFIED },
{ 0100644, "11deab00b2d3a6f5a3073988ac050c2d7b6655e2", 0, "changed-in-master.txt", GIT_DELTA_MODIFIED },
{ 0100644, "ab6c44a2e84492ad4b41bb6bac87353e9d02ac8b", 0, "changed-in-master.txt", GIT_DELTA_UNMODIFIED },
GIT_MERGE_DIFF_NONE,
},
{
{ 0100644, "d427e0b2e138501a3d15cc376077a3631e15bd46", 0, "conflicting.txt", GIT_DELTA_UNMODIFIED },
{ 0100644, "4e886e602529caa9ab11d71f86634bd1b6e0de10", 0, "conflicting.txt", GIT_DELTA_MODIFIED },
{ 0100644, "d427e0b2e138501a3d15cc376077a3631e15bd46", 0, "conflicting.txt", GIT_DELTA_UNMODIFIED },
GIT_MERGE_DIFF_NONE,
},
{
{ 0100644, "5c3b68a71fc4fa5d362fd3875e53137c6a5ab7a5", 0, "removed-in-master.txt",GIT_DELTA_UNMODIFIED },
{ 0, "", 0, "", GIT_DELTA_DELETED },
{ 0100644, "5c3b68a71fc4fa5d362fd3875e53137c6a5ab7a5", 0, "removed-in-master.txt", GIT_DELTA_UNMODIFIED },
GIT_MERGE_DIFF_MODIFIED_DELETED,
},
{
{ 0, "", 0, "", GIT_DELTA_UNMODIFIED },
{ 0, "", 0, "", GIT_DELTA_UNMODIFIED },
{ 0100644, "5843febcb23480df0b5edb22a21c59c772bb8e29", 0, "renamed-50.txt", GIT_DELTA_ADDED },
GIT_MERGE_DIFF_NONE,
},
{
{ 0, "", 0, "", GIT_DELTA_UNMODIFIED },
{ 0, "", 0, "", GIT_DELTA_UNMODIFIED },
{ 0100644, "a77a56a49f8f3ae242e02717f18ebbc60c5cc543", 0, "renamed-75.txt", GIT_DELTA_ADDED },
GIT_MERGE_DIFF_NONE,
},
};
test_find_differences(TREE_OID_ANCESTOR, TREE_OID_MASTER, TREE_OID_RENAMES2, treediff_conflict_data, 7);
}
Markdown is supported
0% or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment