Commit 758e50c5 by Vicent Martí

Merge pull request #1389 from ethomson/merge_trees

Merge trees
parents cfcdbc10 75d1c8c6
Anc / Our / Thr represent the ancestor / ours / theirs side of a merge
from branch "branch" into HEAD. Workdir represents the expected files in
the working directory. Index represents the expected files in the index,
with stage markers.
Anc Our Thr Workdir Index
1 D D
D/F D/F D/F [0]
2 D D+ D~HEAD (mod/del) D/F [0]
D/F D/F D [1]
D [2]
3 D D D/F D/F [0]
D/F
4 D D+ D~branch (mod/del) D/F [0]
D/F D/F D [1]
D [3]
5 D D/F (add/add) D/F [2]
D/F D/F [3]
D/F
6 D/F D/F D D [0]
D
7 D/F D/F+ D/F (mod/del) D/F [1]
D D~branch (fil/dir) D/F [2]
D [3]
8 D/F D/F D D [0]
D
9 D/F D/F+ D/F (mod/del) D/F [1]
D D~HEAD (fil/dir) D [2]
D/F [3]
10 D/F D/F (fil/dir) D/F [0]
D D~HEAD D [2]
D
......@@ -84,13 +84,6 @@ typedef struct git_index_entry {
char *path;
} git_index_entry;
/** Representation of a resolve undo entry in the index. */
typedef struct git_index_reuc_entry {
unsigned int mode[3];
git_oid oid[3];
char *path;
} git_index_reuc_entry;
/** Capabilities of system that affect index actions. */
enum {
GIT_INDEXCAP_IGNORE_CASE = 1,
......@@ -478,102 +471,6 @@ GIT_EXTERN(int) git_index_has_conflicts(const git_index *index);
/**@}*/
/** @name Resolve Undo (REUC) index entry manipulation.
*
* These functions work on the Resolve Undo index extension and contains
* data about the original files that led to a merge conflict.
*/
/**@{*/
/**
* Get the count of resolve undo entries currently in the index.
*
* @param index an existing index object
* @return integer of count of current resolve undo entries
*/
GIT_EXTERN(unsigned int) git_index_reuc_entrycount(git_index *index);
/**
* Finds the resolve undo entry that points to the given path in the Git
* index.
*
* @param at_pos the address to which the position of the reuc entry is written (optional)
* @param index an existing index object
* @param path path to search
* @return 0 if found, < 0 otherwise (GIT_ENOTFOUND)
*/
GIT_EXTERN(int) git_index_reuc_find(size_t *at_pos, git_index *index, const char *path);
/**
* Get a resolve undo 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 path path to search
* @return the resolve undo entry; NULL if not found
*/
GIT_EXTERN(const git_index_reuc_entry *) git_index_reuc_get_bypath(git_index *index, const char *path);
/**
* Get a resolve undo 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 resolve undo entry; NULL if out of bounds
*/
GIT_EXTERN(const git_index_reuc_entry *) git_index_reuc_get_byindex(git_index *index, size_t n);
/**
* Adds a resolve undo entry for a file based on the given parameters.
*
* The resolve undo entry contains the OIDs of files that were involved
* in a merge conflict after the conflict has been resolved. This allows
* conflicts to be re-resolved later.
*
* If there exists a resolve undo entry for the given path in the index,
* it will be removed.
*
* This method will fail in bare index instances.
*
* @param index an existing index object
* @param path filename to add
* @param ancestor_mode mode of the ancestor file
* @param ancestor_id oid of the ancestor file
* @param our_mode mode of our file
* @param our_id oid of our file
* @param their_mode mode of their file
* @param their_id oid of their file
* @return 0 or an error code
*/
GIT_EXTERN(int) git_index_reuc_add(git_index *index, const char *path,
int ancestor_mode, git_oid *ancestor_id,
int our_mode, git_oid *our_id,
int their_mode, git_oid *their_id);
/**
* Remove an resolve undo entry from the index
*
* @param index an existing index object
* @param n position of the resolve undo entry to remove
* @return 0 or an error code
*/
GIT_EXTERN(int) git_index_reuc_remove(git_index *index, size_t n);
/**
* Remove all resolve undo entries from the index
*
* @param index an existing index object
* @return 0 or an error code
*/
GIT_EXTERN(void) git_index_reuc_clear(git_index *index);
/**@}*/
/** @} */
GIT_END_DECL
#endif
......@@ -7,20 +7,65 @@
#ifndef INCLUDE_git_merge_h__
#define INCLUDE_git_merge_h__
#include "common.h"
#include "types.h"
#include "oid.h"
#include "git2/common.h"
#include "git2/types.h"
#include "git2/oid.h"
#include "git2/checkout.h"
#include "git2/index.h"
/**
* @file git2/merge.h
* @brief Git merge-base routines
* @defgroup git_revwalk Git merge-base routines
* @brief Git merge routines
* @defgroup git_merge Git merge routines
* @ingroup Git
* @{
*/
GIT_BEGIN_DECL
/**
* Flags for tree_many diff options. A combination of these flags can be
* 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;
/**
* Automerge options for `git_merge_trees_opts`.
*/
typedef enum {
GIT_MERGE_AUTOMERGE_NORMAL = 0,
GIT_MERGE_AUTOMERGE_NONE = 1,
GIT_MERGE_AUTOMERGE_FAVOR_OURS = 2,
GIT_MERGE_AUTOMERGE_FAVOR_THEIRS = 3,
} git_merge_automerge_flags;
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;
#define GIT_MERGE_TREE_OPTS_VERSION 1
#define GIT_MERGE_TREE_OPTS_INIT {GIT_MERGE_TREE_OPTS_VERSION}
/**
* Find a merge base between two commits
*
* @param out the OID of a merge base between 'one' and 'two'
......@@ -50,6 +95,28 @@ GIT_EXTERN(int) git_merge_base_many(
const git_oid input_array[],
size_t length);
/**
* Merge two trees, producing a `git_index` that reflects the result of
* the merge.
*
* The returned index must be freed explicitly with `git_index_free`.
*
* @param out pointer to store the index result in
* @param repo repository that contains the given trees
* @param ancestor_tree the common ancestor between the trees (or null if none)
* @param our_tree the tree that reflects the destination tree
* @param their_tree the tree to merge in to `our_tree`
* @param opts the merge tree options (or null for defaults)
* @return zero on success, -1 on failure.
*/
GIT_EXTERN(int) git_merge_trees(
git_index **out,
git_repository *repo,
const git_tree *ancestor_tree,
const git_tree *our_tree,
const git_tree *their_tree,
const git_merge_tree_opts *opts);
/** @} */
GIT_END_DECL
#endif
/*
* Copyright (C) the libgit2 contributors. All rights reserved.
*
* This file is part of libgit2, distributed under the GNU GPL v2 with
* a Linking Exception. For full terms see the included COPYING file.
*/
#ifndef INCLUDE_sys_git_index_h__
#define INCLUDE_sys_git_index_h__
/**
* @file git2/sys/index.h
* @brief Low-level Git index manipulation routines
* @defgroup git_backend Git custom backend APIs
* @ingroup Git
* @{
*/
GIT_BEGIN_DECL
/** Representation of a rename conflict entry in the index. */
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];
git_oid oid[3];
char *path;
} git_index_reuc_entry;
/** @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
* data about the original files that led to a merge conflict.
*/
/**@{*/
/**
* Get the count of resolve undo entries currently in the index.
*
* @param index an existing index object
* @return integer of count of current resolve undo entries
*/
GIT_EXTERN(unsigned int) git_index_reuc_entrycount(git_index *index);
/**
* Finds the resolve undo entry that points to the given path in the Git
* index.
*
* @param at_pos the address to which the position of the reuc entry is written (optional)
* @param index an existing index object
* @param path path to search
* @return 0 if found, < 0 otherwise (GIT_ENOTFOUND)
*/
GIT_EXTERN(int) git_index_reuc_find(size_t *at_pos, git_index *index, const char *path);
/**
* Get a resolve undo 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 path path to search
* @return the resolve undo entry; NULL if not found
*/
GIT_EXTERN(const git_index_reuc_entry *) git_index_reuc_get_bypath(git_index *index, const char *path);
/**
* Get a resolve undo 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 resolve undo entry; NULL if out of bounds
*/
GIT_EXTERN(const git_index_reuc_entry *) git_index_reuc_get_byindex(git_index *index, size_t n);
/**
* Adds a resolve undo entry for a file based on the given parameters.
*
* The resolve undo entry contains the OIDs of files that were involved
* in a merge conflict after the conflict has been resolved. This allows
* conflicts to be re-resolved later.
*
* If there exists a resolve undo entry for the given path in the index,
* it will be removed.
*
* This method will fail in bare index instances.
*
* @param index an existing index object
* @param path filename to add
* @param ancestor_mode mode of the ancestor file
* @param ancestor_id oid of the ancestor file
* @param our_mode mode of our file
* @param our_id oid of our file
* @param their_mode mode of their file
* @param their_id oid of their file
* @return 0 or an error code
*/
GIT_EXTERN(int) git_index_reuc_add(git_index *index, const char *path,
int ancestor_mode, const git_oid *ancestor_id,
int our_mode, const git_oid *our_id,
int their_mode, const git_oid *their_id);
/**
* Remove an resolve undo entry from the index
*
* @param index an existing index object
* @param n position of the resolve undo entry to remove
* @return 0 or an error code
*/
GIT_EXTERN(int) git_index_reuc_remove(git_index *index, size_t n);
/**
* Remove all resolve undo entries from the index
*
* @param index an existing index object
* @return 0 or an error code
*/
GIT_EXTERN(void) git_index_reuc_clear(git_index *index);
/**@}*/
/** @} */
GIT_END_DECL
#endif
......@@ -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,12 +187,12 @@ 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;
int error = 0;
GIT_UNUSED(f);
error = git_hashsig_create((git_hashsig **)out, buf, len, opt);
......@@ -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;
......
......@@ -19,6 +19,7 @@
#include "git2/oid.h"
#include "git2/blob.h"
#include "git2/config.h"
#include "git2/sys/index.h"
#define entry_size(type,len) ((offsetof(type, path) + (len) + 8) & ~7)
#define short_entry_size(len) entry_size(struct entry_short, len)
......@@ -35,6 +36,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 +189,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 +320,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 +374,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);
......@@ -585,8 +630,9 @@ static int index_entry_init(git_index_entry **entry_out, git_index *index, const
static int index_entry_reuc_init(git_index_reuc_entry **reuc_out,
const char *path,
int ancestor_mode, git_oid *ancestor_oid,
int our_mode, git_oid *our_oid, int their_mode, git_oid *their_oid)
int ancestor_mode, const git_oid *ancestor_oid,
int our_mode, const git_oid *our_oid,
int their_mode, const git_oid *their_oid)
{
git_index_reuc_entry *reuc = NULL;
......@@ -692,7 +738,7 @@ static int index_conflict_to_reuc(git_index *index, const char *path)
{
git_index_entry *conflict_entries[3];
int ancestor_mode, our_mode, their_mode;
git_oid *ancestor_oid, *our_oid, *their_oid;
git_oid const *ancestor_oid, *our_oid, *their_oid;
int ret;
if ((ret = git_index_conflict_get(&conflict_entries[0],
......@@ -946,7 +992,6 @@ int git_index_conflict_get(git_index_entry **ancestor_out,
return GIT_ENOTFOUND;
for (posmax = git_index_entrycount(index); pos < posmax; ++pos) {
conflict_entry = git_vector_get(&index->entries, pos);
if (index->entries_cmp_path(conflict_entry->path, path) != 0)
......@@ -1042,13 +1087,82 @@ 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);
return (unsigned int)index->reuc.length;
}
static int index_reuc_insert(git_index *index, git_index_reuc_entry *reuc, int replace)
static int index_reuc_insert(
git_index *index,
git_index_reuc_entry *reuc,
int replace)
{
git_index_reuc_entry **existing = NULL;
size_t position;
......@@ -1070,9 +1184,9 @@ static int index_reuc_insert(git_index *index, git_index_reuc_entry *reuc, int r
}
int git_index_reuc_add(git_index *index, const char *path,
int ancestor_mode, git_oid *ancestor_oid,
int our_mode, git_oid *our_oid,
int their_mode, git_oid *their_oid)
int ancestor_mode, const git_oid *ancestor_oid,
int our_mode, const git_oid *our_oid,
int their_mode, const git_oid *their_oid)
{
git_index_reuc_entry *reuc = NULL;
int error = 0;
......@@ -1225,6 +1339,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;
......@@ -1329,6 +1489,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 */
......@@ -1542,6 +1705,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;
......@@ -1612,6 +1830,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;
......
......@@ -5,15 +5,54 @@
* a Linking Exception. For full terms see the included COPYING file.
*/
#include "common.h"
#include "posix.h"
#include "buffer.h"
#include "repository.h"
#include "revwalk.h"
#include "buffer.h"
#include "commit_list.h"
#include "merge.h"
#include "path.h"
#include "refs.h"
#include "object.h"
#include "iterator.h"
#include "refs.h"
#include "diff.h"
#include "checkout.h"
#include "tree.h"
#include "merge_file.h"
#include "blob.h"
#include "hashsig.h"
#include "oid.h"
#include "git2/types.h"
#include "git2/repository.h"
#include "git2/object.h"
#include "git2/commit.h"
#include "git2/merge.h"
#include "git2/refs.h"
#include "git2/reset.h"
#include "commit_list.h"
#include "git2/checkout.h"
#include "git2/signature.h"
#include "git2/config.h"
#include "git2/tree.h"
#include "git2/sys/index.h"
#define GIT_MERGE_INDEX_ENTRY_EXISTS(X) ((X).mode != 0)
typedef enum {
TREE_IDX_ANCESTOR = 0,
TREE_IDX_OURS = 1,
TREE_IDX_THEIRS = 2
} merge_tree_index_t;
/* Tracks D/F conflicts */
struct merge_diff_df_data {
const char *df_path;
const char *prev_path;
git_merge_diff *prev_conflict;
};
int git_repository_merge_cleanup(git_repository *repo)
{
......@@ -48,6 +87,8 @@ cleanup:
return error;
}
/* Merge base computation */
int git_merge_base_many(git_oid *out, git_repository *repo, const git_oid input_array[], size_t length)
{
git_revwalk *walk;
......@@ -177,7 +218,7 @@ int git_merge__bases_many(git_commit_list **out, git_revwalk *walk, git_commit_l
return -1;
if (git_commit_list_parse(walk, one) < 0)
return -1;
return -1;
one->flags |= PARENT1;
if (git_pqueue_insert(&list, one) < 0)
......@@ -294,3 +335,1286 @@ cleanup:
return error;
}
GIT_INLINE(int) index_entry_cmp(const git_index_entry *a, const git_index_entry *b)
{
int value = 0;
if (a->path == NULL)
return (b->path == NULL) ? 0 : 1;
if ((value = a->mode - b->mode) == 0 &&
(value = git_oid__cmp(&a->oid, &b->oid)) == 0)
value = strcmp(a->path, b->path);
return value;
}
/* Conflict resolution */
static int merge_conflict_resolve_trivial(
int *resolved,
git_merge_diff_list *diff_list,
const git_merge_diff *conflict)
{
int ancestor_empty, ours_empty, theirs_empty;
int ours_changed, theirs_changed, ours_theirs_differ;
git_index_entry const *result = NULL;
int error = 0;
assert(resolved && diff_list && conflict);
*resolved = 0;
if (conflict->type == GIT_MERGE_DIFF_DIRECTORY_FILE ||
conflict->type == GIT_MERGE_DIFF_RENAMED_ADDED)
return 0;
if (conflict->our_status == GIT_DELTA_RENAMED ||
conflict->their_status == GIT_DELTA_RENAMED)
return 0;
ancestor_empty = !GIT_MERGE_INDEX_ENTRY_EXISTS(conflict->ancestor_entry);
ours_empty = !GIT_MERGE_INDEX_ENTRY_EXISTS(conflict->our_entry);
theirs_empty = !GIT_MERGE_INDEX_ENTRY_EXISTS(conflict->their_entry);
ours_changed = (conflict->our_status != GIT_DELTA_UNMODIFIED);
theirs_changed = (conflict->their_status != GIT_DELTA_UNMODIFIED);
ours_theirs_differ = ours_changed && theirs_changed &&
index_entry_cmp(&conflict->our_entry, &conflict->their_entry);
/*
* Note: with only one ancestor, some cases are not distinct:
*
* 16: ancest:anc1/anc2, head:anc1, remote:anc2 = result:no merge
* 3: ancest:(empty)^, head:head, remote:(empty) = result:no merge
* 2: ancest:(empty)^, head:(empty), remote:remote = result:no merge
*
* Note that the two cases that take D/F conflicts into account
* specifically do not need to be explicitly tested, as D/F conflicts
* would fail the *empty* test:
*
* 3ALT: ancest:(empty)+, head:head, remote:*empty* = result:head
* 2ALT: ancest:(empty)+, head:*empty*, remote:remote = result:remote
*
* Note that many of these cases need not be explicitly tested, as
* they simply degrade to "all different" cases (eg, 11):
*
* 4: ancest:(empty)^, head:head, remote:remote = result:no merge
* 7: ancest:ancest+, head:(empty), remote:remote = result:no merge
* 9: ancest:ancest+, head:head, remote:(empty) = result:no merge
* 11: ancest:ancest+, head:head, remote:remote = result:no merge
*/
/* 5ALT: ancest:*, head:head, remote:head = result:head */
if (ours_changed && !ours_empty && !ours_theirs_differ)
result = &conflict->our_entry;
/* 6: ancest:ancest+, head:(empty), remote:(empty) = result:no merge */
else if (ours_changed && ours_empty && theirs_empty)
*resolved = 0;
/* 8: ancest:ancest^, head:(empty), remote:ancest = result:no merge */
else if (ours_empty && !theirs_changed)
*resolved = 0;
/* 10: ancest:ancest^, head:ancest, remote:(empty) = result:no merge */
else if (!ours_changed && theirs_empty)
*resolved = 0;
/* 13: ancest:ancest+, head:head, remote:ancest = result:head */
else if (ours_changed && !theirs_changed)
result = &conflict->our_entry;
/* 14: ancest:ancest+, head:ancest, remote:remote = result:remote */
else if (!ours_changed && theirs_changed)
result = &conflict->their_entry;
else
*resolved = 0;
if (result != NULL &&
GIT_MERGE_INDEX_ENTRY_EXISTS(*result) &&
(error = git_vector_insert(&diff_list->staged, (void *)result)) >= 0)
*resolved = 1;
/* Note: trivial resolution does not update the REUC. */
return error;
}
static int merge_conflict_resolve_one_removed(
int *resolved,
git_merge_diff_list *diff_list,
const git_merge_diff *conflict)
{
int ours_empty, theirs_empty;
int ours_changed, theirs_changed;
int error = 0;
assert(resolved && diff_list && conflict);
*resolved = 0;
if (conflict->type == GIT_MERGE_DIFF_DIRECTORY_FILE ||
conflict->type == GIT_MERGE_DIFF_RENAMED_ADDED)
return 0;
ours_empty = !GIT_MERGE_INDEX_ENTRY_EXISTS(conflict->our_entry);
theirs_empty = !GIT_MERGE_INDEX_ENTRY_EXISTS(conflict->their_entry);
ours_changed = (conflict->our_status != GIT_DELTA_UNMODIFIED);
theirs_changed = (conflict->their_status != GIT_DELTA_UNMODIFIED);
/* Removed in both */
if (ours_changed && ours_empty && theirs_empty)
*resolved = 1;
/* Removed in ours */
else if (ours_empty && !theirs_changed)
*resolved = 1;
/* Removed in theirs */
else if (!ours_changed && theirs_empty)
*resolved = 1;
if (*resolved)
git_vector_insert(&diff_list->resolved, (git_merge_diff *)conflict);
return error;
}
static int merge_conflict_resolve_one_renamed(
int *resolved,
git_merge_diff_list *diff_list,
const git_merge_diff *conflict)
{
int ours_renamed, theirs_renamed;
int ours_changed, theirs_changed;
git_index_entry *merged;
int error = 0;
assert(resolved && diff_list && conflict);
*resolved = 0;
if (!GIT_MERGE_INDEX_ENTRY_EXISTS(conflict->our_entry) ||
!GIT_MERGE_INDEX_ENTRY_EXISTS(conflict->their_entry))
return 0;
ours_renamed = (conflict->our_status == GIT_DELTA_RENAMED);
theirs_renamed = (conflict->their_status == GIT_DELTA_RENAMED);
if (!ours_renamed && !theirs_renamed)
return 0;
/* Reject one file in a 2->1 conflict */
if (conflict->type == GIT_MERGE_DIFF_BOTH_RENAMED_2_TO_1 ||
conflict->type == GIT_MERGE_DIFF_BOTH_RENAMED_1_TO_2 ||
conflict->type == GIT_MERGE_DIFF_RENAMED_ADDED)
return 0;
ours_changed = (git_oid__cmp(&conflict->ancestor_entry.oid, &conflict->our_entry.oid) != 0);
theirs_changed = (git_oid__cmp(&conflict->ancestor_entry.oid, &conflict->their_entry.oid) != 0);
/* if both are modified (and not to a common target) require a merge */
if (ours_changed && theirs_changed &&
git_oid__cmp(&conflict->our_entry.oid, &conflict->their_entry.oid) != 0)
return 0;
if ((merged = git_pool_malloc(&diff_list->pool, sizeof(git_index_entry))) == NULL)
return -1;
if (ours_changed)
memcpy(merged, &conflict->our_entry, sizeof(git_index_entry));
else
memcpy(merged, &conflict->their_entry, sizeof(git_index_entry));
if (ours_renamed)
merged->path = conflict->our_entry.path;
else
merged->path = conflict->their_entry.path;
*resolved = 1;
git_vector_insert(&diff_list->staged, merged);
git_vector_insert(&diff_list->resolved, (git_merge_diff *)conflict);
return error;
}
static int merge_conflict_resolve_automerge(
int *resolved,
git_merge_diff_list *diff_list,
const git_merge_diff *conflict,
unsigned int automerge_flags)
{
git_merge_file_input ancestor = GIT_MERGE_FILE_INPUT_INIT,
ours = GIT_MERGE_FILE_INPUT_INIT,
theirs = GIT_MERGE_FILE_INPUT_INIT;
git_merge_file_result result = GIT_MERGE_FILE_RESULT_INIT;
git_index_entry *index_entry;
git_odb *odb = NULL;
git_oid automerge_oid;
int error = 0;
assert(resolved && diff_list && conflict);
*resolved = 0;
if (automerge_flags == GIT_MERGE_AUTOMERGE_NONE)
return 0;
/* Reject D/F conflicts */
if (conflict->type == GIT_MERGE_DIFF_DIRECTORY_FILE)
return 0;
/* Reject link/file conflicts. */
if ((S_ISLNK(conflict->ancestor_entry.mode) ^ S_ISLNK(conflict->our_entry.mode)) ||
(S_ISLNK(conflict->ancestor_entry.mode) ^ S_ISLNK(conflict->their_entry.mode)))
return 0;
/* Reject name conflicts */
if (conflict->type == GIT_MERGE_DIFF_BOTH_RENAMED_2_TO_1 ||
conflict->type == GIT_MERGE_DIFF_RENAMED_ADDED)
return 0;
if ((conflict->our_status & GIT_DELTA_RENAMED) == GIT_DELTA_RENAMED &&
(conflict->their_status & GIT_DELTA_RENAMED) == GIT_DELTA_RENAMED &&
strcmp(conflict->ancestor_entry.path, conflict->their_entry.path) != 0)
return 0;
if ((error = git_repository_odb(&odb, diff_list->repo)) < 0 ||
(error = git_merge_file_input_from_index_entry(&ancestor, diff_list->repo, &conflict->ancestor_entry)) < 0 ||
(error = git_merge_file_input_from_index_entry(&ours, diff_list->repo, &conflict->our_entry)) < 0 ||
(error = git_merge_file_input_from_index_entry(&theirs, diff_list->repo, &conflict->their_entry)) < 0 ||
(error = git_merge_files(&result, &ancestor, &ours, &theirs, automerge_flags)) < 0 ||
!result.automergeable ||
(error = git_odb_write(&automerge_oid, odb, result.data, result.len, GIT_OBJ_BLOB)) < 0)
goto done;
if ((index_entry = git_pool_malloc(&diff_list->pool, sizeof(git_index_entry))) == NULL)
GITERR_CHECK_ALLOC(index_entry);
index_entry->path = git_pool_strdup(&diff_list->pool, result.path);
GITERR_CHECK_ALLOC(index_entry->path);
index_entry->file_size = result.len;
index_entry->mode = result.mode;
git_oid_cpy(&index_entry->oid, &automerge_oid);
git_vector_insert(&diff_list->staged, index_entry);
git_vector_insert(&diff_list->resolved, (git_merge_diff *)conflict);
*resolved = 1;
done:
git_merge_file_input_free(&ancestor);
git_merge_file_input_free(&ours);
git_merge_file_input_free(&theirs);
git_merge_file_result_free(&result);
git_odb_free(odb);
return error;
}
static int merge_conflict_resolve(
int *out,
git_merge_diff_list *diff_list,
const git_merge_diff *conflict,
unsigned int automerge_flags)
{
int resolved = 0;
int error = 0;
*out = 0;
if ((error = merge_conflict_resolve_trivial(&resolved, diff_list, conflict)) < 0)
goto done;
if (automerge_flags != GIT_MERGE_AUTOMERGE_NONE) {
if (!resolved && (error = merge_conflict_resolve_one_removed(&resolved, diff_list, conflict)) < 0)
goto done;
if (!resolved && (error = merge_conflict_resolve_one_renamed(&resolved, diff_list, conflict)) < 0)
goto done;
if (!resolved && (error = merge_conflict_resolve_automerge(&resolved, diff_list, conflict, automerge_flags)) < 0)
goto done;
}
*out = resolved;
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(
const git_merge_diff *conflict)
{
if (GIT_MERGE_INDEX_ENTRY_EXISTS(conflict->ancestor_entry))
return conflict->ancestor_entry.path;
else if (GIT_MERGE_INDEX_ENTRY_EXISTS(conflict->our_entry))
return conflict->our_entry.path;
else if (GIT_MERGE_INDEX_ENTRY_EXISTS(conflict->their_entry))
return conflict->their_entry.path;
return NULL;
}
GIT_INLINE(bool) merge_diff_any_side_added_or_modified(
const git_merge_diff *conflict)
{
if (conflict->our_status == GIT_DELTA_ADDED ||
conflict->our_status == GIT_DELTA_MODIFIED ||
conflict->their_status == GIT_DELTA_ADDED ||
conflict->their_status == GIT_DELTA_MODIFIED)
return true;
return false;
}
GIT_INLINE(bool) path_is_prefixed(const char *parent, const char *child)
{
size_t child_len = strlen(child);
size_t parent_len = strlen(parent);
if (child_len < parent_len ||
strncmp(parent, child, parent_len) != 0)
return 0;
return (child[parent_len] == '/');
}
GIT_INLINE(int) merge_diff_detect_df_conflict(
struct merge_diff_df_data *df_data,
git_merge_diff *conflict)
{
const char *cur_path = merge_diff_path(conflict);
/* Determine if this is a D/F conflict or the child of one */
if (df_data->df_path &&
path_is_prefixed(df_data->df_path, cur_path))
conflict->type = GIT_MERGE_DIFF_DF_CHILD;
else if(df_data->df_path)
df_data->df_path = NULL;
else if (df_data->prev_path &&
merge_diff_any_side_added_or_modified(df_data->prev_conflict) &&
merge_diff_any_side_added_or_modified(conflict) &&
path_is_prefixed(df_data->prev_path, cur_path)) {
conflict->type = GIT_MERGE_DIFF_DF_CHILD;
df_data->prev_conflict->type = GIT_MERGE_DIFF_DIRECTORY_FILE;
df_data->df_path = df_data->prev_path;
}
df_data->prev_path = cur_path;
df_data->prev_conflict = conflict;
return 0;
}
/* Conflict handling */
GIT_INLINE(int) merge_diff_detect_type(
git_merge_diff *conflict)
{
if (conflict->our_status == GIT_DELTA_ADDED &&
conflict->their_status == GIT_DELTA_ADDED)
conflict->type = GIT_MERGE_DIFF_BOTH_ADDED;
else if (conflict->our_status == GIT_DELTA_MODIFIED &&
conflict->their_status == GIT_DELTA_MODIFIED)
conflict->type = GIT_MERGE_DIFF_BOTH_MODIFIED;
else if (conflict->our_status == GIT_DELTA_DELETED &&
conflict->their_status == GIT_DELTA_DELETED)
conflict->type = GIT_MERGE_DIFF_BOTH_DELETED;
else if (conflict->our_status == GIT_DELTA_MODIFIED &&
conflict->their_status == GIT_DELTA_DELETED)
conflict->type = GIT_MERGE_DIFF_MODIFIED_DELETED;
else if (conflict->our_status == GIT_DELTA_DELETED &&
conflict->their_status == GIT_DELTA_MODIFIED)
conflict->type = GIT_MERGE_DIFF_MODIFIED_DELETED;
else
conflict->type = GIT_MERGE_DIFF_NONE;
return 0;
}
GIT_INLINE(int) index_entry_dup(
git_index_entry *out,
git_pool *pool,
const git_index_entry *src)
{
if (src != NULL) {
memcpy(out, src, sizeof(git_index_entry));
if ((out->path = git_pool_strdup(pool, src->path)) == NULL)
return -1;
}
return 0;
}
GIT_INLINE(int) merge_delta_type_from_index_entries(
const git_index_entry *ancestor,
const git_index_entry *other)
{
if (ancestor == NULL && other == NULL)
return GIT_DELTA_UNMODIFIED;
else if (ancestor == NULL && other != NULL)
return GIT_DELTA_ADDED;
else if (ancestor != NULL && other == NULL)
return GIT_DELTA_DELETED;
else if (S_ISDIR(ancestor->mode) ^ S_ISDIR(other->mode))
return GIT_DELTA_TYPECHANGE;
else if(S_ISLNK(ancestor->mode) ^ S_ISLNK(other->mode))
return GIT_DELTA_TYPECHANGE;
else if (git_oid__cmp(&ancestor->oid, &other->oid) ||
ancestor->mode != other->mode)
return GIT_DELTA_MODIFIED;
return GIT_DELTA_UNMODIFIED;
}
static git_merge_diff *merge_diff_from_index_entries(
git_merge_diff_list *diff_list,
const git_index_entry **entries)
{
git_merge_diff *conflict;
git_pool *pool = &diff_list->pool;
if ((conflict = git_pool_malloc(pool, sizeof(git_merge_diff))) == NULL)
return NULL;
if (index_entry_dup(&conflict->ancestor_entry, pool, entries[TREE_IDX_ANCESTOR]) < 0 ||
index_entry_dup(&conflict->our_entry, pool, entries[TREE_IDX_OURS]) < 0 ||
index_entry_dup(&conflict->their_entry, pool, entries[TREE_IDX_THEIRS]) < 0)
return NULL;
conflict->our_status = merge_delta_type_from_index_entries(
entries[TREE_IDX_ANCESTOR], entries[TREE_IDX_OURS]);
conflict->their_status = merge_delta_type_from_index_entries(
entries[TREE_IDX_ANCESTOR], entries[TREE_IDX_THEIRS]);
return conflict;
}
/* Merge trees */
static int merge_index_insert_conflict(
git_merge_diff_list *diff_list,
struct merge_diff_df_data *merge_df_data,
const git_index_entry *tree_items[3])
{
git_merge_diff *conflict;
if ((conflict = merge_diff_from_index_entries(diff_list, tree_items)) == NULL ||
merge_diff_detect_type(conflict) < 0 ||
merge_diff_detect_df_conflict(merge_df_data, conflict) < 0 ||
git_vector_insert(&diff_list->conflicts, conflict) < 0)
return -1;
return 0;
}
static int merge_index_insert_unmodified(
git_merge_diff_list *diff_list,
const git_index_entry *tree_items[3])
{
int error = 0;
git_index_entry *entry;
entry = git_pool_malloc(&diff_list->pool, sizeof(git_index_entry));
GITERR_CHECK_ALLOC(entry);
if ((error = index_entry_dup(entry, &diff_list->pool, tree_items[0])) >= 0)
error = git_vector_insert(&diff_list->staged, entry);
return error;
}
int git_merge_diff_list__find_differences(
git_merge_diff_list *diff_list,
const git_tree *ancestor_tree,
const git_tree *our_tree,
const git_tree *their_tree)
{
git_iterator *iterators[3] = {0};
git_index_entry *items[3] = {0}, *best_cur_item, *cur_items[3];
git_vector_cmp entry_compare = git_index_entry__cmp;
struct merge_diff_df_data df_data = {0};
int cur_item_modified;
size_t i;
int error = 0;
assert(diff_list && our_tree && their_tree);
if ((error = git_iterator_for_tree(&iterators[TREE_IDX_ANCESTOR], (git_tree *)ancestor_tree, GIT_ITERATOR_DONT_IGNORE_CASE, NULL, NULL)) < 0 ||
(error = git_iterator_for_tree(&iterators[TREE_IDX_OURS], (git_tree *)our_tree, GIT_ITERATOR_DONT_IGNORE_CASE, NULL, NULL)) < 0 ||
(error = git_iterator_for_tree(&iterators[TREE_IDX_THEIRS], (git_tree *)their_tree, GIT_ITERATOR_DONT_IGNORE_CASE, NULL, NULL)) < 0)
goto done;
/* Set up the iterators */
for (i = 0; i < 3; i++) {
if ((error = git_iterator_current(&items[i], iterators[i])) < 0)
goto done;
}
while (true) {
memset(cur_items, 0x0, sizeof(git_index_entry *) * 3);
best_cur_item = NULL;
cur_item_modified = 0;
/* Find the next path(s) to consume from each iterator */
for (i = 0; i < 3; i++) {
if (items[i] == NULL) {
cur_item_modified = 1;
continue;
}
if (best_cur_item == NULL) {
best_cur_item = items[i];
cur_items[i] = items[i];
} else {
int path_diff = entry_compare(items[i], best_cur_item);
if (path_diff < 0) {
/*
* Found an item that sorts before our current item, make
* our current item this one.
*/
memset(cur_items, 0x0, sizeof(git_index_entry *) * 3);
cur_item_modified = 1;
best_cur_item = items[i];
cur_items[i] = items[i];
} else if (path_diff > 0) {
/* No entry for the current item, this is modified */
cur_item_modified = 1;
} else if (path_diff == 0) {
cur_items[i] = items[i];
if (!cur_item_modified)
cur_item_modified = index_entry_cmp(best_cur_item, items[i]);
}
}
}
if (best_cur_item == NULL)
break;
if (cur_item_modified)
error = merge_index_insert_conflict(diff_list, &df_data, cur_items);
else
error = merge_index_insert_unmodified(diff_list, cur_items);
/* Advance each iterator that participated */
for (i = 0; i < 3; i++) {
if (cur_items[i] != NULL &&
(error = git_iterator_advance(&items[i], iterators[i])) < 0)
goto done;
}
}
done:
for (i = 0; i < 3; i++)
git_iterator_free(iterators[i]);
return error;
}
git_merge_diff_list *git_merge_diff_list__alloc(git_repository *repo)
{
git_merge_diff_list *diff_list = git__calloc(1, sizeof(git_merge_diff_list));
if (diff_list == NULL)
return NULL;
diff_list->repo = repo;
if (git_vector_init(&diff_list->staged, 0, NULL) < 0 ||
git_vector_init(&diff_list->conflicts, 0, NULL) < 0 ||
git_vector_init(&diff_list->resolved, 0, NULL) < 0 ||
git_pool_init(&diff_list->pool, 1, 0) < 0)
return NULL;
return diff_list;
}
static int merge_tree_normalize_opts(
git_repository *repo,
git_merge_tree_opts *opts,
const git_merge_tree_opts *given)
{
git_config *cfg = NULL;
int error = 0;
assert(repo && opts);
if ((error = git_repository_config__weakptr(&cfg, repo)) < 0)
return error;
if (given != NULL)
memcpy(opts, given, sizeof(git_merge_tree_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;
}
static int merge_index_insert_reuc(
git_index *index,
size_t idx,
const git_index_entry *entry)
{
const git_index_reuc_entry *reuc;
int mode[3] = { 0, 0, 0 };
git_oid const *oid[3] = { NULL, NULL, NULL };
size_t i;
if (!GIT_MERGE_INDEX_ENTRY_EXISTS(*entry))
return 0;
if ((reuc = git_index_reuc_get_bypath(index, entry->path)) != NULL) {
for (i = 0; i < 3; i++) {
mode[i] = reuc->mode[i];
oid[i] = &reuc->oid[i];
}
}
mode[idx] = entry->mode;
oid[idx] = &entry->oid;
return git_index_reuc_add(index, entry->path,
mode[0], oid[0], mode[1], oid[1], mode[2], oid[2]);
}
int index_from_diff_list(git_index **out, git_merge_diff_list *diff_list)
{
git_index *index;
size_t i;
git_index_entry *entry;
git_merge_diff *conflict;
int error = 0;
*out = NULL;
if ((error = git_index_new(&index)) < 0)
return error;
git_vector_foreach(&diff_list->staged, i, entry) {
if ((error = git_index_add(index, entry)) < 0)
goto on_error;
}
git_vector_foreach(&diff_list->conflicts, i, conflict) {
const git_index_entry *ancestor =
GIT_MERGE_INDEX_ENTRY_EXISTS(conflict->ancestor_entry) ?
&conflict->ancestor_entry : NULL;
const git_index_entry *ours =
GIT_MERGE_INDEX_ENTRY_EXISTS(conflict->our_entry) ?
&conflict->our_entry : NULL;
const git_index_entry *theirs =
GIT_MERGE_INDEX_ENTRY_EXISTS(conflict->their_entry) ?
&conflict->their_entry : NULL;
if ((error = git_index_conflict_add(index, ancestor, ours, theirs)) < 0)
goto on_error;
}
/* Add each rename entry to the rename portion of the index. */
git_vector_foreach(&diff_list->conflicts, i, conflict) {
const char *ancestor_path, *our_path, *their_path;
if (!GIT_MERGE_INDEX_ENTRY_EXISTS(conflict->ancestor_entry))
continue;
ancestor_path = conflict->ancestor_entry.path;
our_path =
GIT_MERGE_INDEX_ENTRY_EXISTS(conflict->our_entry) ?
conflict->our_entry.path : NULL;
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
* the paths may differ due to renames. */
git_vector_foreach(&diff_list->resolved, i, conflict) {
const git_index_entry *ancestor =
GIT_MERGE_INDEX_ENTRY_EXISTS(conflict->ancestor_entry) ?
&conflict->ancestor_entry : NULL;
const git_index_entry *ours =
GIT_MERGE_INDEX_ENTRY_EXISTS(conflict->our_entry) ?
&conflict->our_entry : NULL;
const git_index_entry *theirs =
GIT_MERGE_INDEX_ENTRY_EXISTS(conflict->their_entry) ?
&conflict->their_entry : NULL;
if (ancestor != NULL &&
(error = merge_index_insert_reuc(index, TREE_IDX_ANCESTOR, ancestor)) < 0)
goto on_error;
if (ours != NULL &&
(error = merge_index_insert_reuc(index, TREE_IDX_OURS, ours)) < 0)
goto on_error;
if (theirs != NULL &&
(error = merge_index_insert_reuc(index, TREE_IDX_THEIRS, theirs)) < 0)
goto on_error;
}
*out = index;
return 0;
on_error:
git_index_free(index);
return error;
}
int git_merge_trees(
git_index **out,
git_repository *repo,
const git_tree *ancestor_tree,
const git_tree *our_tree,
const git_tree *their_tree,
const git_merge_tree_opts *given_opts)
{
git_merge_diff_list *diff_list;
git_merge_tree_opts opts;
git_merge_diff *conflict;
git_vector changes;
size_t i;
int error = 0;
assert(out && repo && our_tree && their_tree);
*out = NULL;
if ((error = merge_tree_normalize_opts(repo, &opts, given_opts)) < 0)
return error;
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 ||
(error = git_merge_diff_list__find_renames(repo, diff_list, &opts)) < 0)
goto done;
memcpy(&changes, &diff_list->conflicts, sizeof(git_vector));
git_vector_clear(&diff_list->conflicts);
git_vector_foreach(&changes, i, conflict) {
int resolved = 0;
if ((error = merge_conflict_resolve(&resolved, diff_list, conflict, opts.automerge_flags)) < 0)
goto done;
if (!resolved)
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:
git_merge_diff_list__free(diff_list);
return error;
}
void git_merge_diff_list__free(git_merge_diff_list *diff_list)
{
if (!diff_list)
return;
git_vector_free(&diff_list->staged);
git_vector_free(&diff_list->conflicts);
git_vector_free(&diff_list->resolved);
git_pool_clear(&diff_list->pool);
git__free(diff_list);
}
......@@ -7,16 +7,121 @@
#ifndef INCLUDE_merge_h__
#define INCLUDE_merge_h__
#include "git2/types.h"
#include "git2/merge.h"
#include "commit_list.h"
#include "vector.h"
#include "commit_list.h"
#include "pool.h"
#include "git2/merge.h"
#include "git2/types.h"
#define GIT_MERGE_MSG_FILE "MERGE_MSG"
#define GIT_MERGE_MODE_FILE "MERGE_MODE"
#define MERGE_CONFIG_FILE_MODE 0666
#define GIT_MERGE_TREE_RENAME_THRESHOLD 50
#define GIT_MERGE_TREE_TARGET_LIMIT 1000
/** Types of changes when files are merged from branch to branch. */
typedef enum {
/* No conflict - a change only occurs in one branch. */
GIT_MERGE_DIFF_NONE = 0,
/* Occurs when a file is modified in both branches. */
GIT_MERGE_DIFF_BOTH_MODIFIED = (1 << 0),
/* Occurs when a file is added in both branches. */
GIT_MERGE_DIFF_BOTH_ADDED = (1 << 1),
/* Occurs when a file is deleted in both branches. */
GIT_MERGE_DIFF_BOTH_DELETED = (1 << 2),
/* Occurs when a file is modified in one branch and deleted in the other. */
GIT_MERGE_DIFF_MODIFIED_DELETED = (1 << 3),
/* Occurs when a file is renamed in one branch and modified in the other. */
GIT_MERGE_DIFF_RENAMED_MODIFIED = (1 << 4),
/* Occurs when a file is renamed in one branch and deleted in the other. */
GIT_MERGE_DIFF_RENAMED_DELETED = (1 << 5),
/* Occurs when a file is renamed in one branch and a file with the same
* name is added in the other. Eg, A->B and new file B. Core git calls
* this a "rename/delete". */
GIT_MERGE_DIFF_RENAMED_ADDED = (1 << 6),
/* Occurs when both a file is renamed to the same name in the ours and
* theirs branches. Eg, A->B and A->B in both. Automergeable. */
GIT_MERGE_DIFF_BOTH_RENAMED = (1 << 7),
/* Occurs when a file is renamed to different names in the ours and theirs
* branches. Eg, A->B and A->C. */
GIT_MERGE_DIFF_BOTH_RENAMED_1_TO_2 = (1 << 8),
/* Occurs when two files are renamed to the same name in the ours and
* theirs branches. Eg, A->C and B->C. */
GIT_MERGE_DIFF_BOTH_RENAMED_2_TO_1 = (1 << 9),
/* Occurs when an item at a path in one branch is a directory, and an
* item at the same path in a different branch is a file. */
GIT_MERGE_DIFF_DIRECTORY_FILE = (1 << 10),
/* The child of a folder that is in a directory/file conflict. */
GIT_MERGE_DIFF_DF_CHILD = (1 << 11),
} git_merge_diff_type_t;
typedef struct {
git_repository *repo;
git_pool pool;
/* Vector of git_index_entry that represent the merged items that
* have been staged, either because only one side changed, or because
* the two changes were non-conflicting and mergeable. These items
* will be written as staged entries in the main index.
*/
git_vector staged;
/* Vector of git_merge_diff entries that represent the conflicts that
* have not been automerged. These items will be written to high-stage
* entries in the main index.
*/
git_vector conflicts;
/* Vector of git_merge_diff that have been automerged. These items
* will be written to the REUC when the index is produced.
*/
git_vector resolved;
} git_merge_diff_list;
/**
* Description of changes to one file across three trees.
*/
typedef struct {
git_merge_diff_type_t type;
git_index_entry ancestor_entry;
git_index_entry our_entry;
git_delta_t our_status;
git_index_entry their_entry;
git_delta_t their_status;
} git_merge_diff;
int git_merge__bases_many(
git_commit_list **out,
git_revwalk *walk,
git_commit_list_node *one,
git_vector *twos);
git_merge_diff_list *git_merge_diff_list__alloc(git_repository *repo);
int git_merge_diff_list__find_differences(git_merge_diff_list *merge_diff_list,
const git_tree *ancestor_tree,
const git_tree *ours_tree,
const git_tree *theirs_tree);
int git_merge_diff_list__find_renames(git_repository *repo, git_merge_diff_list *merge_diff_list, const git_merge_tree_opts *opts);
int git_merge__bases_many(git_commit_list **out, git_revwalk *walk, git_commit_list_node *one, git_vector *twos);
void git_merge_diff_list__free(git_merge_diff_list *diff_list);
#endif
/*
* Copyright (C) the libgit2 contributors. All rights reserved.
*
* This file is part of libgit2, distributed under the GNU GPL v2 with
* a Linking Exception. For full terms see the included COPYING file.
*/
#include "common.h"
#include "repository.h"
#include "merge_file.h"
#include "git2/repository.h"
#include "git2/object.h"
#include "git2/index.h"
#include "xdiff/xdiff.h"
#define GIT_MERGE_FILE_SIDE_EXISTS(X) ((X)->mode != 0)
GIT_INLINE(const char *) merge_file_best_path(
const git_merge_file_input *ancestor,
const git_merge_file_input *ours,
const git_merge_file_input *theirs)
{
if (!GIT_MERGE_FILE_SIDE_EXISTS(ancestor)) {
if (strcmp(ours->path, theirs->path) == 0)
return ours->path;
return NULL;
}
if (strcmp(ancestor->path, ours->path) == 0)
return theirs->path;
else if(strcmp(ancestor->path, theirs->path) == 0)
return ours->path;
return NULL;
}
GIT_INLINE(int) merge_file_best_mode(
const git_merge_file_input *ancestor,
const git_merge_file_input *ours,
const git_merge_file_input *theirs)
{
/*
* If ancestor didn't exist and either ours or theirs is executable,
* assume executable. Otherwise, if any mode changed from the ancestor,
* use that one.
*/
if (GIT_MERGE_FILE_SIDE_EXISTS(ancestor)) {
if (ours->mode == GIT_FILEMODE_BLOB_EXECUTABLE ||
theirs->mode == GIT_FILEMODE_BLOB_EXECUTABLE)
return GIT_FILEMODE_BLOB_EXECUTABLE;
return GIT_FILEMODE_BLOB;
}
if (ancestor->mode == ours->mode)
return theirs->mode;
else if(ancestor->mode == theirs->mode)
return ours->mode;
return 0;
}
int git_merge_file_input_from_index_entry(
git_merge_file_input *input,
git_repository *repo,
const git_index_entry *entry)
{
git_odb *odb = NULL;
int error = 0;
assert(input && repo && entry);
if (entry->mode == 0)
return 0;
if ((error = git_repository_odb(&odb, repo)) < 0 ||
(error = git_odb_read(&input->odb_object, odb, &entry->oid)) < 0)
goto done;
input->mode = entry->mode;
input->path = git__strdup(entry->path);
input->mmfile.size = git_odb_object_size(input->odb_object);
input->mmfile.ptr = (char *)git_odb_object_data(input->odb_object);
if (input->label == NULL)
input->label = entry->path;
done:
git_odb_free(odb);
return error;
}
int git_merge_file_input_from_diff_file(
git_merge_file_input *input,
git_repository *repo,
const git_diff_file *file)
{
git_odb *odb = NULL;
int error = 0;
assert(input && repo && file);
if (file->mode == 0)
return 0;
if ((error = git_repository_odb(&odb, repo)) < 0 ||
(error = git_odb_read(&input->odb_object, odb, &file->oid)) < 0)
goto done;
input->mode = file->mode;
input->path = git__strdup(file->path);
input->mmfile.size = git_odb_object_size(input->odb_object);
input->mmfile.ptr = (char *)git_odb_object_data(input->odb_object);
if (input->label == NULL)
input->label = file->path;
done:
git_odb_free(odb);
return error;
}
int git_merge_files(
git_merge_file_result *out,
git_merge_file_input *ancestor,
git_merge_file_input *ours,
git_merge_file_input *theirs,
git_merge_automerge_flags flags)
{
xmparam_t xmparam;
mmbuffer_t mmbuffer;
int xdl_result;
int error = 0;
assert(out && ancestor && ours && theirs);
memset(out, 0x0, sizeof(git_merge_file_result));
if (!GIT_MERGE_FILE_SIDE_EXISTS(ours) || !GIT_MERGE_FILE_SIDE_EXISTS(theirs))
return 0;
memset(&xmparam, 0x0, sizeof(xmparam_t));
xmparam.ancestor = ancestor->label;
xmparam.file1 = ours->label;
xmparam.file2 = theirs->label;
out->path = merge_file_best_path(ancestor, ours, theirs);
out->mode = merge_file_best_mode(ancestor, ours, theirs);
if (flags == GIT_MERGE_AUTOMERGE_FAVOR_OURS)
xmparam.favor = XDL_MERGE_FAVOR_OURS;
if (flags == GIT_MERGE_AUTOMERGE_FAVOR_THEIRS)
xmparam.favor = XDL_MERGE_FAVOR_THEIRS;
if ((xdl_result = xdl_merge(&ancestor->mmfile, &ours->mmfile,
&theirs->mmfile, &xmparam, &mmbuffer)) < 0) {
giterr_set(GITERR_MERGE, "Failed to merge files.");
error = -1;
goto done;
}
out->automergeable = (xdl_result == 0);
out->data = (unsigned char *)mmbuffer.ptr;
out->len = mmbuffer.size;
done:
return error;
}
/*
* Copyright (C) the libgit2 contributors. All rights reserved.
*
* This file is part of libgit2, distributed under the GNU GPL v2 with
* a Linking Exception. For full terms see the included COPYING file.
*/
#ifndef INCLUDE_filediff_h__
#define INCLUDE_filediff_h__
#include "xdiff/xdiff.h"
#include "git2/merge.h"
typedef struct {
const char *label;
char *path;
unsigned int mode;
mmfile_t mmfile;
git_odb_object *odb_object;
} git_merge_file_input;
#define GIT_MERGE_FILE_INPUT_INIT {0}
typedef struct {
bool automergeable;
const char *path;
int mode;
unsigned char *data;
size_t len;
} git_merge_file_result;
#define GIT_MERGE_FILE_RESULT_INIT {0}
int git_merge_file_input_from_index_entry(
git_merge_file_input *input,
git_repository *repo,
const git_index_entry *entry);
int git_merge_file_input_from_diff_file(
git_merge_file_input *input,
git_repository *repo,
const git_diff_file *file);
int git_merge_files(
git_merge_file_result *out,
git_merge_file_input *ancestor,
git_merge_file_input *ours,
git_merge_file_input *theirs,
git_merge_automerge_flags flags);
GIT_INLINE(void) git_merge_file_input_free(git_merge_file_input *input)
{
assert(input);
git__free(input->path);
git_odb_object_free(input->odb_object);
}
GIT_INLINE(void) git_merge_file_result_free(git_merge_file_result *filediff)
{
if (filediff == NULL)
return;
/* xdiff uses malloc() not git_malloc, so we use free(), not git_free() */
if (filediff->data != NULL)
free(filediff->data);
}
#endif
#include "clar_libgit2.h"
#include "index.h"
#include "git2/sys/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);
}
#include "clar_libgit2.h"
#include "index.h"
#include "git2/sys/index.h"
#include "git2/repository.h"
#include "../reset/reset_helpers.h"
......
#include "clar_libgit2.h"
#include "buffer.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;
}
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;
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((const struct merge_index_entry *)&expected->ancestor, &actual->ancestor_entry) ||
!index_entry_eq_merge_index_entry((const struct merge_index_entry *)&expected->ours, &actual->our_entry) ||
!index_entry_eq_merge_index_entry((const struct merge_index_entry *)&expected->theirs, &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;
}
#ifndef INCLUDE_cl_merge_helpers_h__
#define INCLUDE_cl_merge_helpers_h__
#include "merge.h"
#include "git2/merge.h"
struct merge_index_entry {
uint16_t mode;
char oid_str[41];
int stage;
char path[128];
};
struct merge_name_entry {
char ancestor_path[128];
char our_path[128];
char their_path[128];
};
struct merge_index_with_status {
uint16_t mode;
char oid_str[41];
int stage;
char path[128];
unsigned int status;
};
struct merge_reuc_entry {
char path[128];
unsigned int ancestor_mode;
unsigned int our_mode;
unsigned int their_mode;
char ancestor_oid_str[41];
char our_oid_str[41];
char their_oid_str[41];
};
struct merge_index_conflict_data {
struct merge_index_with_status ancestor;
struct merge_index_with_status ours;
struct merge_index_with_status theirs;
git_merge_diff_type_t change_type;
};
int merge_trees_from_branches(
git_index **index, git_repository *repo,
const char *ours_name, const char *theirs_name,
git_merge_tree_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);
int merge_test_index(git_index *index, const struct merge_index_entry expected[], size_t expected_len);
int merge_test_names(git_index *index, const struct merge_name_entry expected[], size_t expected_len);
int merge_test_reuc(git_index *index, const struct merge_reuc_entry expected[], size_t expected_len);
int merge_test_workdir(git_repository *repo, const struct merge_index_entry expected[], size_t expected_len);
#endif
#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 TEST_INDEX_PATH TEST_REPO_PATH "/.git/index"
#define THEIRS_AUTOMERGE_BRANCH "branch"
#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 "df_side2"
/* 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" }
/* 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"
// Fixture setup and teardown
void test_merge_trees_automerge__initialize(void)
{
repo = cl_git_sandbox_init(TEST_REPO_PATH);
}
void test_merge_trees_automerge__cleanup(void)
{
cl_git_sandbox_cleanup();
}
void test_merge_trees_automerge__automerge(void)
{
git_index *index;
const git_index_entry *entry;
git_merge_tree_opts opts = GIT_MERGE_TREE_OPTS_INIT;
git_blob *blob;
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_git_pass(merge_trees_from_branches(&index, repo, "master", THEIRS_AUTOMERGE_BRANCH, &opts));
cl_assert(merge_test_index(index, merge_index_entries, 8));
cl_assert(merge_test_reuc(index, merge_reuc_entries, 3));
cl_assert((entry = git_index_get_bypath(index, "automergeable.txt", 0)) != NULL);
cl_assert(entry->file_size == strlen(AUTOMERGEABLE_MERGED_FILE));
cl_git_pass(git_object_lookup((git_object **)&blob, repo, &entry->oid, GIT_OBJ_BLOB));
cl_assert(memcmp(git_blob_rawcontent(blob), AUTOMERGEABLE_MERGED_FILE, entry->file_size) == 0);
git_index_free(index);
git_blob_free(blob);
}
void test_merge_trees_automerge__favor_ours(void)
{
git_index *index;
git_merge_tree_opts opts = GIT_MERGE_TREE_OPTS_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, "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,
};
opts.automerge_flags = GIT_MERGE_AUTOMERGE_FAVOR_OURS;
cl_git_pass(merge_trees_from_branches(&index, repo, "master", THEIRS_AUTOMERGE_BRANCH, &opts));
cl_assert(merge_test_index(index, merge_index_entries, 6));
cl_assert(merge_test_reuc(index, merge_reuc_entries, 4));
git_index_free(index);
}
void test_merge_trees_automerge__favor_theirs(void)
{
git_index *index;
git_merge_tree_opts opts = GIT_MERGE_TREE_OPTS_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, "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,
};
opts.automerge_flags = GIT_MERGE_AUTOMERGE_FAVOR_THEIRS;
cl_git_pass(merge_trees_from_branches(&index, repo, "master", THEIRS_AUTOMERGE_BRANCH, &opts));
cl_assert(merge_test_index(index, merge_index_entries, 6));
cl_assert(merge_test_reuc(index, merge_reuc_entries, 4));
git_index_free(index);
}
void test_merge_trees_automerge__unrelated(void)
{
git_index *index;
git_merge_tree_opts opts = GIT_MERGE_TREE_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(merge_trees_from_branches(&index, repo, "master", THEIRS_UNRELATED_BRANCH, &opts));
cl_assert(merge_test_index(index, merge_index_entries, 11));
git_index_free(index);
}
#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 DF_SIDE1_BRANCH "df_side1"
#define DF_SIDE2_BRANCH "df_side2"
// Fixture setup and teardown
void test_merge_trees_modeconflict__initialize(void)
{
repo = cl_git_sandbox_init(TEST_REPO_PATH);
}
void test_merge_trees_modeconflict__cleanup(void)
{
cl_git_sandbox_cleanup();
}
void test_merge_trees_modeconflict__df_conflict(void)
{
git_index *index;
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(merge_trees_from_branches(&index, repo, DF_SIDE1_BRANCH, DF_SIDE2_BRANCH, NULL));
cl_assert(merge_test_index(index, merge_index_entries, 20));
git_index_free(index);
}
#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);
}
#include "clar_libgit2.h"
#include "git2/tree.h"
#include "merge.h"
#include "../merge_helpers.h"
#include "diff.h"
#include "hashsig.h"
static git_repository *repo;
#define TEST_REPO_PATH "merge-resolve"
#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);
}
void test_merge_trees_treediff__cleanup(void)
{
cl_git_sandbox_cleanup();
}
struct treediff_cb_data {
struct merge_index_conflict_data *conflict_data;
size_t conflict_data_len;
size_t idx;
};
static void test_find_differences(
const char *ancestor_oidstr,
const char *ours_oidstr,
const char *theirs_oidstr,
struct merge_index_conflict_data *treediff_conflict_data,
size_t treediff_conflict_data_len)
{
git_merge_diff_list *merge_diff_list = git_merge_diff_list__alloc(repo);
git_oid ancestor_oid, ours_oid, theirs_oid;
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));
cl_git_pass(git_tree_lookup(&ancestor_tree, repo, &ancestor_oid));
cl_git_pass(git_tree_lookup(&ours_tree, repo, &ours_oid));
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);
*/
cl_assert(treediff_conflict_data_len == merge_diff_list->conflicts.length);
treediff_cb_data.conflict_data = treediff_conflict_data;
treediff_cb_data.conflict_data_len = treediff_conflict_data_len;
cl_assert(merge_test_merge_conflicts(&merge_diff_list->conflicts, treediff_conflict_data, treediff_conflict_data_len));
git_tree_free(ancestor_tree);
git_tree_free(ours_tree);
git_tree_free(theirs_tree);
git_merge_diff_list__free(merge_diff_list);
git__free(opts.metric);
}
void test_merge_trees_treediff__simple(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, "058541fc37114bfc1dddf6bd6bffc7fae5c2e6fe", 0, "automergeable.txt", GIT_DELTA_MODIFIED },
GIT_MERGE_DIFF_BOTH_MODIFIED
},
{
{ 0100644, "ab6c44a2e84492ad4b41bb6bac87353e9d02ac8b", 0, "changed-in-branch.txt", GIT_DELTA_UNMODIFIED },
{ 0100644, "ab6c44a2e84492ad4b41bb6bac87353e9d02ac8b", 0, "changed-in-branch.txt", GIT_DELTA_UNMODIFIED },
{ 0100644, "4eb04c9e79e88f6640d01ff5b25ca2a60764f216", 0, "changed-in-branch.txt", GIT_DELTA_MODIFIED },
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, "2bd0a343aeef7a2cf0d158478966a6e587ff3863", 0, "conflicting.txt", GIT_DELTA_MODIFIED },
GIT_MERGE_DIFF_BOTH_MODIFIED
},
{
{ 0100644, "dfe3f22baa1f6fce5447901c3086bae368de6bdd", 0, "removed-in-branch.txt", GIT_DELTA_UNMODIFIED },
{ 0100644, "dfe3f22baa1f6fce5447901c3086bae368de6bdd", 0, "removed-in-branch.txt", GIT_DELTA_UNMODIFIED },
{ 0, "", 0, "", GIT_DELTA_DELETED },
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
},
};
test_find_differences(TREE_OID_ANCESTOR, TREE_OID_MASTER, TREE_OID_BRANCH, treediff_conflict_data, 7);
}
void test_merge_trees_treediff__df_conflicts(void)
{
struct merge_index_conflict_data treediff_conflict_data[] = {
{
{ 0, "", 0, "", GIT_DELTA_UNMODIFIED },
{ 0100644, "49130a28ef567af9a6a6104c38773fedfa5f9742", 0, "dir-10", GIT_DELTA_ADDED },
{ 0100644, "6c06dcd163587c2cc18be44857e0b71116382aeb", 0, "dir-10", GIT_DELTA_ADDED },
GIT_MERGE_DIFF_BOTH_ADDED,
},
{
{ 0100644, "242591eb280ee9eeb2ce63524b9a8b9bc4cb515d", 0, "dir-10/file.txt", GIT_DELTA_UNMODIFIED },
{ 0, "", 0, "", GIT_DELTA_DELETED },
{ 0, "", 0, "", GIT_DELTA_DELETED },
GIT_MERGE_DIFF_BOTH_DELETED,
},
{
{ 0, "", 0, "", GIT_DELTA_UNMODIFIED },
{ 0, "", 0, "", GIT_DELTA_UNMODIFIED },
{ 0100644, "43aafd43bea779ec74317dc361f45ae3f532a505", 0, "dir-6", GIT_DELTA_ADDED },
GIT_MERGE_DIFF_NONE,
},
{
{ 0100644, "cf8c5cc8a85a1ff5a4ba51e0bc7cf5665669924d", 0, "dir-6/file.txt", GIT_DELTA_UNMODIFIED },
{ 0100644, "cf8c5cc8a85a1ff5a4ba51e0bc7cf5665669924d", 0, "dir-6/file.txt", GIT_DELTA_UNMODIFIED },
{ 0, "", 0, "", GIT_DELTA_DELETED },
GIT_MERGE_DIFF_NONE,
},
{
{ 0, "", 0, "", GIT_DELTA_UNMODIFIED },
{ 0, "", 0, "", GIT_DELTA_UNMODIFIED },
{ 0100644, "a031a28ae70e33a641ce4b8a8f6317f1ab79dee4", 0, "dir-7", GIT_DELTA_ADDED },
GIT_MERGE_DIFF_DIRECTORY_FILE,
},
{
{ 0100644, "5012fd565b1393bdfda1805d4ec38ce6619e1fd1", 0, "dir-7/file.txt", GIT_DELTA_UNMODIFIED },
{ 0100644, "a5563304ddf6caba25cb50323a2ea6f7dbfcadca", 0, "dir-7/file.txt", GIT_DELTA_MODIFIED },
{ 0, "", 0, "", GIT_DELTA_DELETED },
GIT_MERGE_DIFF_DF_CHILD,
},
{
{ 0, "", 0, "", GIT_DELTA_UNMODIFIED },
{ 0100644, "e9ad6ec3e38364a3d07feda7c4197d4d845c53b5", 0, "dir-8", GIT_DELTA_ADDED },
{ 0, "", 0, "", GIT_DELTA_UNMODIFIED },
GIT_MERGE_DIFF_NONE,
},
{
{ 0100644, "f20c9063fa0bda9a397c96947a7b687305c49753", 0, "dir-8/file.txt", GIT_DELTA_UNMODIFIED },
{ 0, "", 0, "", GIT_DELTA_DELETED },
{ 0100644, "f20c9063fa0bda9a397c96947a7b687305c49753", 0, "dir-8/file.txt", GIT_DELTA_UNMODIFIED },
GIT_MERGE_DIFF_NONE,
},
{
{ 0, "", 0, "", GIT_DELTA_UNMODIFIED },
{ 0100644, "3ef4d30382ca33fdeba9fda895a99e0891ba37aa", 0, "dir-9", GIT_DELTA_ADDED },
{ 0, "", 0, "", GIT_DELTA_UNMODIFIED },
GIT_MERGE_DIFF_DIRECTORY_FILE,
},
{
{ 0100644, "fc4c636d6515e9e261f9260dbcf3cc6eca97ea08", 0, "dir-9/file.txt", GIT_DELTA_UNMODIFIED },
{ 0, "", 0, "", GIT_DELTA_DELETED },
{ 0100644, "76ab0e2868197ec158ddd6c78d8a0d2fd73d38f9", 0, "dir-9/file.txt", GIT_DELTA_MODIFIED },
GIT_MERGE_DIFF_DF_CHILD,
},
{
{ 0100644, "1e4ff029aee68d0d69ef9eb6efa6cbf1ec732f99", 0, "file-1", GIT_DELTA_UNMODIFIED },
{ 0100644, "1e4ff029aee68d0d69ef9eb6efa6cbf1ec732f99", 0, "file-1", GIT_DELTA_UNMODIFIED },
{ 0, "", 0, "", GIT_DELTA_DELETED },
GIT_MERGE_DIFF_NONE,
},
{
{ 0, "", 0, "", GIT_DELTA_UNMODIFIED },
{ 0, "", 0, "", GIT_DELTA_UNMODIFIED },
{ 0100644, "5c2411f8075f48a6b2fdb85ebc0d371747c4df15", 0, "file-1/new", GIT_DELTA_ADDED },
GIT_MERGE_DIFF_NONE,
},
{
{ 0100644, "a39a620dae5bc8b4e771cd4d251b7d080401a21e", 0, "file-2", GIT_DELTA_UNMODIFIED },
{ 0100644, "d963979c237d08b6ba39062ee7bf64c7d34a27f8", 0, "file-2", GIT_DELTA_MODIFIED },
{ 0, "", 0, "", GIT_DELTA_DELETED },
GIT_MERGE_DIFF_DIRECTORY_FILE,
},
{
{ 0, "", 0, "", GIT_DELTA_UNMODIFIED },
{ 0, "", 0, "", GIT_DELTA_UNMODIFIED },
{ 0100644, "5c341ead2ba6f2af98ce5ec3fe84f6b6d2899c0d", 0, "file-2/new", GIT_DELTA_ADDED },
GIT_MERGE_DIFF_DF_CHILD,
},
{
{ 0100644, "032ebc5ab85d9553bb187d3cd40875ff23a63ed0", 0, "file-3", GIT_DELTA_UNMODIFIED },
{ 0, "", 0, "", GIT_DELTA_DELETED },
{ 0100644, "032ebc5ab85d9553bb187d3cd40875ff23a63ed0", 0, "file-3", GIT_DELTA_UNMODIFIED },
GIT_MERGE_DIFF_NONE,
},
{
{ 0, "", 0, "", GIT_DELTA_UNMODIFIED },
{ 0100644, "9efe7723802d4305142eee177e018fee1572c4f4", 0, "file-3/new", GIT_DELTA_ADDED },
{ 0, "", 0, "", GIT_DELTA_UNMODIFIED },
GIT_MERGE_DIFF_NONE,
},
{
{ 0100644, "bacac9b3493509aa15e1730e1545fc0919d1dae0", 0, "file-4", GIT_DELTA_UNMODIFIED },
{ 0, "", 0, "", GIT_DELTA_DELETED },
{ 0100644, "7663fce0130db092936b137cabd693ec234eb060", 0, "file-4", GIT_DELTA_MODIFIED },
GIT_MERGE_DIFF_DIRECTORY_FILE,
},
{
{ 0, "", 0, "", GIT_DELTA_UNMODIFIED },
{ 0100644, "e49f917b448d1340b31d76e54ba388268fd4c922", 0, "file-4/new", GIT_DELTA_ADDED },
{ 0, "", 0, "", GIT_DELTA_UNMODIFIED },
GIT_MERGE_DIFF_DF_CHILD,
},
{
{ 0100644, "ac4045f965119e6998f4340ed0f411decfb3ec05", 0, "file-5", GIT_DELTA_UNMODIFIED },
{ 0, "", 0, "", GIT_DELTA_DELETED },
{ 0, "", 0, "", GIT_DELTA_DELETED },
GIT_MERGE_DIFF_BOTH_DELETED,
},
{
{ 0, "", 0, "", GIT_DELTA_UNMODIFIED },
{ 0100644, "cab2cf23998b40f1af2d9d9a756dc9e285a8df4b", 0, "file-5/new", GIT_DELTA_ADDED },
{ 0100644, "f5504f36e6f4eb797a56fc5bac6c6c7f32969bf2", 0, "file-5/new", GIT_DELTA_ADDED },
GIT_MERGE_DIFF_BOTH_ADDED,
},
};
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);
}
#include "clar_libgit2.h"
#include "git2/repository.h"
#include "git2/merge.h"
#include "merge.h"
#include "../merge_helpers.h"
#include "refs.h"
#include "fileops.h"
#include "git2/sys/index.h"
static git_repository *repo;
#define TEST_REPO_PATH "merge-resolve"
#define TEST_INDEX_PATH TEST_REPO_PATH "/.git/index"
// Fixture setup and teardown
void test_merge_trees_trivial__initialize(void)
{
repo = cl_git_sandbox_init(TEST_REPO_PATH);
}
void test_merge_trees_trivial__cleanup(void)
{
cl_git_sandbox_cleanup();
}
static int merge_trivial(git_index **index, const char *ours, const char *theirs, bool automerge)
{
git_commit *our_commit, *their_commit, *ancestor_commit;
git_tree *our_tree, *their_tree, *ancestor_tree;
git_oid our_oid, their_oid, ancestor_oid;
git_buf branch_buf = GIT_BUF_INIT;
git_merge_tree_opts opts = GIT_MERGE_TREE_OPTS_INIT;
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_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);
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));
cl_git_pass(git_merge_base(&ancestor_oid, repo, git_commit_id(our_commit), git_commit_id(their_commit)));
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;
}
static int merge_trivial_conflict_entrycount(git_index *index)
{
const git_index_entry *entry;
size_t count = 0;
size_t i;
for (i = 0; i < git_index_entrycount(index); i++) {
cl_assert(entry = git_index_get_byindex(index, i));
if (git_index_entry_stage(entry) > 0)
count++;
}
return count;
}
/* 2ALT: ancest:(empty)+, head:*empty*, remote:remote = result:remote */
void test_merge_trees_trivial__2alt(void)
{
git_index *result;
const git_index_entry *entry;
cl_git_pass(merge_trivial(&result, "trivial-2alt", "trivial-2alt-branch", 0));
cl_assert(entry = git_index_get_bypath(result, "new-in-branch.txt", 0));
cl_assert(git_index_reuc_entrycount(result) == 0);
cl_assert(merge_trivial_conflict_entrycount(result) == 0);
git_index_free(result);
}
/* 3ALT: ancest:(empty)+, head:head, remote:*empty* = result:head */
void test_merge_trees_trivial__3alt(void)
{
git_index *result;
const git_index_entry *entry;
cl_git_pass(merge_trivial(&result, "trivial-3alt", "trivial-3alt-branch", 0));
cl_assert(entry = git_index_get_bypath(result, "new-in-3alt.txt", 0));
cl_assert(git_index_reuc_entrycount(result) == 0);
cl_assert(merge_trivial_conflict_entrycount(result) == 0);
git_index_free(result);
}
/* 4: ancest:(empty)^, head:head, remote:remote = result:no merge */
void test_merge_trees_trivial__4(void)
{
git_index *result;
const git_index_entry *entry;
cl_git_pass(merge_trivial(&result, "trivial-4", "trivial-4-branch", 0));
cl_assert((entry = git_index_get_bypath(result, "new-and-different.txt", 0)) == NULL);
cl_assert(git_index_reuc_entrycount(result) == 0);
cl_assert(merge_trivial_conflict_entrycount(result) == 2);
cl_assert(entry = git_index_get_bypath(result, "new-and-different.txt", 2));
cl_assert(entry = git_index_get_bypath(result, "new-and-different.txt", 3));
git_index_free(result);
}
/* 5ALT: ancest:*, head:head, remote:head = result:head */
void test_merge_trees_trivial__5alt_1(void)
{
git_index *result;
const git_index_entry *entry;
cl_git_pass(merge_trivial(&result, "trivial-5alt-1", "trivial-5alt-1-branch", 0));
cl_assert(entry = git_index_get_bypath(result, "new-and-same.txt", 0));
cl_assert(git_index_reuc_entrycount(result) == 0);
cl_assert(merge_trivial_conflict_entrycount(result) == 0);
git_index_free(result);
}
/* 5ALT: ancest:*, head:head, remote:head = result:head */
void test_merge_trees_trivial__5alt_2(void)
{
git_index *result;
const git_index_entry *entry;
cl_git_pass(merge_trivial(&result, "trivial-5alt-2", "trivial-5alt-2-branch", 0));
cl_assert(entry = git_index_get_bypath(result, "modified-to-same.txt", 0));
cl_assert(git_index_reuc_entrycount(result) == 0);
cl_assert(merge_trivial_conflict_entrycount(result) == 0);
git_index_free(result);
}
/* 6: ancest:ancest+, head:(empty), remote:(empty) = result:no merge */
void test_merge_trees_trivial__6(void)
{
git_index *result;
const git_index_entry *entry;
cl_git_pass(merge_trivial(&result, "trivial-6", "trivial-6-branch", 0));
cl_assert((entry = git_index_get_bypath(result, "removed-in-both.txt", 0)) == NULL);
cl_assert(git_index_reuc_entrycount(result) == 0);
cl_assert(merge_trivial_conflict_entrycount(result) == 1);
cl_assert(entry = git_index_get_bypath(result, "removed-in-both.txt", 1));
git_index_free(result);
}
/* 6: ancest:ancest+, head:(empty), remote:(empty) = result:no merge */
void test_merge_trees_trivial__6_automerge(void)
{
git_index *result;
const git_index_entry *entry;
const git_index_reuc_entry *reuc;
cl_git_pass(merge_trivial(&result, "trivial-6", "trivial-6-branch", 1));
cl_assert((entry = git_index_get_bypath(result, "removed-in-both.txt", 0)) == NULL);
cl_assert(git_index_reuc_entrycount(result) == 1);
cl_assert(reuc = git_index_reuc_get_bypath(result, "removed-in-both.txt"));
cl_assert(merge_trivial_conflict_entrycount(result) == 0);
git_index_free(result);
}
/* 8: ancest:ancest^, head:(empty), remote:ancest = result:no merge */
void test_merge_trees_trivial__8(void)
{
git_index *result;
const git_index_entry *entry;
cl_git_pass(merge_trivial(&result, "trivial-8", "trivial-8-branch", 0));
cl_assert((entry = git_index_get_bypath(result, "removed-in-8.txt", 0)) == NULL);
cl_assert(git_index_reuc_entrycount(result) == 0);
cl_assert(merge_trivial_conflict_entrycount(result) == 2);
cl_assert(entry = git_index_get_bypath(result, "removed-in-8.txt", 1));
cl_assert(entry = git_index_get_bypath(result, "removed-in-8.txt", 3));
git_index_free(result);
}
/* 8: ancest:ancest^, head:(empty), remote:ancest = result:no merge */
void test_merge_trees_trivial__8_automerge(void)
{
git_index *result;
const git_index_entry *entry;
const git_index_reuc_entry *reuc;
cl_git_pass(merge_trivial(&result, "trivial-8", "trivial-8-branch", 1));
cl_assert((entry = git_index_get_bypath(result, "removed-in-8.txt", 0)) == NULL);
cl_assert(git_index_reuc_entrycount(result) == 1);
cl_assert(reuc = git_index_reuc_get_bypath(result, "removed-in-8.txt"));
cl_assert(merge_trivial_conflict_entrycount(result) == 0);
git_index_free(result);
}
/* 7: ancest:ancest+, head:(empty), remote:remote = result:no merge */
void test_merge_trees_trivial__7(void)
{
git_index *result;
const git_index_entry *entry;
cl_git_pass(merge_trivial(&result, "trivial-7", "trivial-7-branch", 0));
cl_assert((entry = git_index_get_bypath(result, "removed-in-7.txt", 0)) == NULL);
cl_assert(git_index_reuc_entrycount(result) == 0);
cl_assert(merge_trivial_conflict_entrycount(result) == 2);
cl_assert(entry = git_index_get_bypath(result, "removed-in-7.txt", 1));
cl_assert(entry = git_index_get_bypath(result, "removed-in-7.txt", 3));
git_index_free(result);
}
/* 7: ancest:ancest+, head:(empty), remote:remote = result:no merge */
void test_merge_trees_trivial__7_automerge(void)
{
git_index *result;
const git_index_entry *entry;
cl_git_pass(merge_trivial(&result, "trivial-7", "trivial-7-branch", 0));
cl_assert((entry = git_index_get_bypath(result, "removed-in-7.txt", 0)) == NULL);
cl_assert(git_index_reuc_entrycount(result) == 0);
cl_assert(merge_trivial_conflict_entrycount(result) == 2);
cl_assert(entry = git_index_get_bypath(result, "removed-in-7.txt", 1));
cl_assert(entry = git_index_get_bypath(result, "removed-in-7.txt", 3));
git_index_free(result);
}
/* 10: ancest:ancest^, head:ancest, remote:(empty) = result:no merge */
void test_merge_trees_trivial__10(void)
{
git_index *result;
const git_index_entry *entry;
cl_git_pass(merge_trivial(&result, "trivial-10", "trivial-10-branch", 0));
cl_assert((entry = git_index_get_bypath(result, "removed-in-10-branch.txt", 0)) == NULL);
cl_assert(git_index_reuc_entrycount(result) == 0);
cl_assert(merge_trivial_conflict_entrycount(result) == 2);
cl_assert(entry = git_index_get_bypath(result, "removed-in-10-branch.txt", 1));
cl_assert(entry = git_index_get_bypath(result, "removed-in-10-branch.txt", 2));
git_index_free(result);
}
/* 10: ancest:ancest^, head:ancest, remote:(empty) = result:no merge */
void test_merge_trees_trivial__10_automerge(void)
{
git_index *result;
const git_index_entry *entry;
const git_index_reuc_entry *reuc;
cl_git_pass(merge_trivial(&result, "trivial-10", "trivial-10-branch", 1));
cl_assert((entry = git_index_get_bypath(result, "removed-in-10-branch.txt", 0)) == NULL);
cl_assert(git_index_reuc_entrycount(result) == 1);
cl_assert(reuc = git_index_reuc_get_bypath(result, "removed-in-10-branch.txt"));
cl_assert(merge_trivial_conflict_entrycount(result) == 0);
git_index_free(result);
}
/* 9: ancest:ancest+, head:head, remote:(empty) = result:no merge */
void test_merge_trees_trivial__9(void)
{
git_index *result;
const git_index_entry *entry;
cl_git_pass(merge_trivial(&result, "trivial-9", "trivial-9-branch", 0));
cl_assert((entry = git_index_get_bypath(result, "removed-in-9-branch.txt", 0)) == NULL);
cl_assert(git_index_reuc_entrycount(result) == 0);
cl_assert(merge_trivial_conflict_entrycount(result) == 2);
cl_assert(entry = git_index_get_bypath(result, "removed-in-9-branch.txt", 1));
cl_assert(entry = git_index_get_bypath(result, "removed-in-9-branch.txt", 2));
git_index_free(result);
}
/* 9: ancest:ancest+, head:head, remote:(empty) = result:no merge */
void test_merge_trees_trivial__9_automerge(void)
{
git_index *result;
const git_index_entry *entry;
cl_git_pass(merge_trivial(&result, "trivial-9", "trivial-9-branch", 1));
cl_assert((entry = git_index_get_bypath(result, "removed-in-9-branch.txt", 0)) == NULL);
cl_assert(git_index_reuc_entrycount(result) == 0);
cl_assert(merge_trivial_conflict_entrycount(result) == 2);
cl_assert(entry = git_index_get_bypath(result, "removed-in-9-branch.txt", 1));
cl_assert(entry = git_index_get_bypath(result, "removed-in-9-branch.txt", 2));
git_index_free(result);
}
/* 13: ancest:ancest+, head:head, remote:ancest = result:head */
void test_merge_trees_trivial__13(void)
{
git_index *result;
const git_index_entry *entry;
git_oid expected_oid;
cl_git_pass(merge_trivial(&result, "trivial-13", "trivial-13-branch", 0));
cl_assert(entry = git_index_get_bypath(result, "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(result) == 0);
cl_assert(merge_trivial_conflict_entrycount(result) == 0);
git_index_free(result);
}
/* 14: ancest:ancest+, head:ancest, remote:remote = result:remote */
void test_merge_trees_trivial__14(void)
{
git_index *result;
const git_index_entry *entry;
git_oid expected_oid;
cl_git_pass(merge_trivial(&result, "trivial-14", "trivial-14-branch", 0));
cl_assert(entry = git_index_get_bypath(result, "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(result) == 0);
cl_assert(merge_trivial_conflict_entrycount(result) == 0);
git_index_free(result);
}
/* 11: ancest:ancest+, head:head, remote:remote = result:no merge */
void test_merge_trees_trivial__11(void)
{
git_index *result;
const git_index_entry *entry;
cl_git_pass(merge_trivial(&result, "trivial-11", "trivial-11-branch", 0));
cl_assert((entry = git_index_get_bypath(result, "modified-in-both.txt", 0)) == NULL);
cl_assert(git_index_reuc_entrycount(result) == 0);
cl_assert(merge_trivial_conflict_entrycount(result) == 3);
cl_assert(entry = git_index_get_bypath(result, "modified-in-both.txt", 1));
cl_assert(entry = git_index_get_bypath(result, "modified-in-both.txt", 2));
cl_assert(entry = git_index_get_bypath(result, "modified-in-both.txt", 3));
git_index_free(result);
}
......@@ -32,15 +32,15 @@ static git_index *repo_index;
#define OCTO5_OID "e4f618a2c3ed0669308735727df5ebf2447f022f"
// Fixture setup and teardown
void test_merge_setup__initialize(void)
void test_merge_workdir_setup__initialize(void)
{
repo = cl_git_sandbox_init(TEST_REPO_PATH);
git_repository_index(&repo_index, repo);
git_repository_index(&repo_index, repo);
}
void test_merge_setup__cleanup(void)
void test_merge_workdir_setup__cleanup(void)
{
git_index_free(repo_index);
git_index_free(repo_index);
cl_git_sandbox_cleanup();
}
......@@ -48,7 +48,8 @@ static void write_file_contents(const char *filename, const char *output)
{
git_buf file_path_buf = GIT_BUF_INIT;
git_buf_printf(&file_path_buf, "%s/%s", git_repository_path(repo), filename);
git_buf_printf(&file_path_buf, "%s/%s", git_repository_path(repo),
filename);
cl_git_rewritefile(file_path_buf.ptr, output);
git_buf_free(&file_path_buf);
......@@ -72,7 +73,7 @@ static int merge_head_foreach_cb(const git_oid *oid, void *payload)
return 0;
}
void test_merge_setup__head_notfound(void)
void test_merge_workdir_setup__head_notfound(void)
{
int error;
......@@ -81,7 +82,7 @@ void test_merge_setup__head_notfound(void)
cl_assert(error == GIT_ENOTFOUND);
}
void test_merge_setup__head_invalid_oid(void)
void test_merge_workdir_setup__head_invalid_oid(void)
{
int error;
......@@ -92,7 +93,7 @@ void test_merge_setup__head_invalid_oid(void)
cl_assert(error == -1);
}
void test_merge_setup__head_foreach_nonewline(void)
void test_merge_workdir_setup__head_foreach_nonewline(void)
{
int error;
......@@ -103,7 +104,7 @@ void test_merge_setup__head_foreach_nonewline(void)
cl_assert(error == -1);
}
void test_merge_setup__head_foreach_one(void)
void test_merge_workdir_setup__head_foreach_one(void)
{
const char *expected = THEIRS_SIMPLE_OID;
......@@ -117,7 +118,7 @@ void test_merge_setup__head_foreach_one(void)
cl_assert(cb_data.i == cb_data.len);
}
void test_merge_setup__head_foreach_octopus(void)
void test_merge_workdir_setup__head_foreach_octopus(void)
{
const char *expected[] = { THEIRS_SIMPLE_OID,
OCTO1_OID, OCTO2_OID, OCTO3_OID, OCTO4_OID, OCTO5_OID };
......
2392a2dacc9efb562b8635d6579fb458751c7c5b
[core]
repositoryformatversion = 0
filemode = true
bare = false
logallrefupdates = true
ignorecase = true
Unnamed repository; edit this file 'description' to name the repository.
0000000000000000000000000000000000000000 c607fc30883e335def28cd686b51f6cfa02b06ec Edward Thomson <ethomson@edwardthomson.com> 1351563869 -0500 commit (initial): initial
c607fc30883e335def28cd686b51f6cfa02b06ec c607fc30883e335def28cd686b51f6cfa02b06ec Edward Thomson <ethomson@edwardthomson.com> 1351563886 -0500 checkout: moving from master to branch
c607fc30883e335def28cd686b51f6cfa02b06ec 7cb63eed597130ba4abb87b3e544b85021905520 Edward Thomson <ethomson@edwardthomson.com> 1351563965 -0500 commit: branch
7cb63eed597130ba4abb87b3e544b85021905520 c607fc30883e335def28cd686b51f6cfa02b06ec Edward Thomson <ethomson@edwardthomson.com> 1351563968 -0500 checkout: moving from branch to master
c607fc30883e335def28cd686b51f6cfa02b06ec 977c696519c5a3004c5f1d15d60c89dbeb8f235f Edward Thomson <ethomson@edwardthomson.com> 1351564033 -0500 commit: master
977c696519c5a3004c5f1d15d60c89dbeb8f235f 977c696519c5a3004c5f1d15d60c89dbeb8f235f Edward Thomson <ethomson@edwardthomson.com> 1351605785 -0500 checkout: moving from master to ff_branch
977c696519c5a3004c5f1d15d60c89dbeb8f235f 33d500f588fbbe65901d82b4e6b008e549064be0 Edward Thomson <ethomson@edwardthomson.com> 1351605830 -0500 commit: fastforward
33d500f588fbbe65901d82b4e6b008e549064be0 977c696519c5a3004c5f1d15d60c89dbeb8f235f Edward Thomson <ethomson@edwardthomson.com> 1351605889 -0500 checkout: moving from ff_branch to master
977c696519c5a3004c5f1d15d60c89dbeb8f235f 977c696519c5a3004c5f1d15d60c89dbeb8f235f Edward Thomson <ethomson@edwardthomson.com> 1351874933 -0500 checkout: moving from master to octo1
977c696519c5a3004c5f1d15d60c89dbeb8f235f 16f825815cfd20a07a75c71554e82d8eede0b061 Edward Thomson <ethomson@edwardthomson.com> 1351874954 -0500 commit: octo1
16f825815cfd20a07a75c71554e82d8eede0b061 977c696519c5a3004c5f1d15d60c89dbeb8f235f Edward Thomson <ethomson@edwardthomson.com> 1351874957 -0500 checkout: moving from octo1 to master
977c696519c5a3004c5f1d15d60c89dbeb8f235f 977c696519c5a3004c5f1d15d60c89dbeb8f235f Edward Thomson <ethomson@edwardthomson.com> 1351874960 -0500 checkout: moving from master to octo2
977c696519c5a3004c5f1d15d60c89dbeb8f235f 158dc7bedb202f5b26502bf3574faa7f4238d56c Edward Thomson <ethomson@edwardthomson.com> 1351874974 -0500 commit: octo2
158dc7bedb202f5b26502bf3574faa7f4238d56c 977c696519c5a3004c5f1d15d60c89dbeb8f235f Edward Thomson <ethomson@edwardthomson.com> 1351874976 -0500 checkout: moving from octo2 to master
977c696519c5a3004c5f1d15d60c89dbeb8f235f 977c696519c5a3004c5f1d15d60c89dbeb8f235f Edward Thomson <ethomson@edwardthomson.com> 1351874980 -0500 checkout: moving from master to octo3
977c696519c5a3004c5f1d15d60c89dbeb8f235f 50ce7d7d01217679e26c55939eef119e0c93e272 Edward Thomson <ethomson@edwardthomson.com> 1351874998 -0500 commit: octo3
50ce7d7d01217679e26c55939eef119e0c93e272 977c696519c5a3004c5f1d15d60c89dbeb8f235f Edward Thomson <ethomson@edwardthomson.com> 1351875006 -0500 checkout: moving from octo3 to master
977c696519c5a3004c5f1d15d60c89dbeb8f235f 977c696519c5a3004c5f1d15d60c89dbeb8f235f Edward Thomson <ethomson@edwardthomson.com> 1351875010 -0500 checkout: moving from master to octo4
977c696519c5a3004c5f1d15d60c89dbeb8f235f 54269b3f6ec3d7d4ede24dd350dd5d605495c3ae Edward Thomson <ethomson@edwardthomson.com> 1351875023 -0500 commit: octo4
54269b3f6ec3d7d4ede24dd350dd5d605495c3ae 977c696519c5a3004c5f1d15d60c89dbeb8f235f Edward Thomson <ethomson@edwardthomson.com> 1351875031 -0500 checkout: moving from octo4 to master
977c696519c5a3004c5f1d15d60c89dbeb8f235f 977c696519c5a3004c5f1d15d60c89dbeb8f235f Edward Thomson <ethomson@edwardthomson.com> 1351875031 -0500 checkout: moving from master to octo5
977c696519c5a3004c5f1d15d60c89dbeb8f235f e4f618a2c3ed0669308735727df5ebf2447f022f Edward Thomson <ethomson@edwardthomson.com> 1351875041 -0500 commit: octo5
e4f618a2c3ed0669308735727df5ebf2447f022f 977c696519c5a3004c5f1d15d60c89dbeb8f235f Edward Thomson <ethomson@edwardthomson.com> 1351875046 -0500 checkout: moving from octo5 to master
977c696519c5a3004c5f1d15d60c89dbeb8f235f 977c696519c5a3004c5f1d15d60c89dbeb8f235f Edward Thomson <ethomson@edwardthomson.com> 1351875046 -0500 checkout: moving from master to octo6
977c696519c5a3004c5f1d15d60c89dbeb8f235f 4ca408a8c88655f7586a1b580be6fad138121e98 Edward Thomson <ethomson@edwardthomson.com> 1351875057 -0500 commit: octo5
4ca408a8c88655f7586a1b580be6fad138121e98 b6f610aef53bd343e6c96227de874c66f00ee8e8 Edward Thomson <ethomson@edwardthomson.com> 1351875065 -0500 commit (amend): octo6
b6f610aef53bd343e6c96227de874c66f00ee8e8 977c696519c5a3004c5f1d15d60c89dbeb8f235f Edward Thomson <ethomson@edwardthomson.com> 1351875071 -0500 checkout: moving from octo6 to master
977c696519c5a3004c5f1d15d60c89dbeb8f235f 4e0d9401aee78eb345a8685a859d37c8c3c0bbed Edward Thomson <ethomson@edwardthomson.com> 1351875091 -0500 merge octo1 octo2 octo3 octo4: Merge made by the 'octopus' strategy.
4e0d9401aee78eb345a8685a859d37c8c3c0bbed 54269b3f6ec3d7d4ede24dd350dd5d605495c3ae Edward Thomson <ethomson@edwardthomson.com> 1351875108 -0500 reset: moving to 54269b3f6ec3d7d4ede24dd350dd5d605495c3ae
54269b3f6ec3d7d4ede24dd350dd5d605495c3ae 977c696519c5a3004c5f1d15d60c89dbeb8f235f Edward Thomson <ethomson@edwardthomson.com> 1351875584 -0500 reset: moving to 977c696519c5a3004c5f1d15d60c89dbeb8f235f
bd593285fc7fe4ca18ccdbabf027f5d689101452 33d500f588fbbe65901d82b4e6b008e549064be0 Edward Thomson <ethomson@edwardthomson.com> 1351990193 -0500 checkout: moving from master to ff_branch
33d500f588fbbe65901d82b4e6b008e549064be0 c607fc30883e335def28cd686b51f6cfa02b06ec Edward Thomson <ethomson@edwardthomson.com> 1351990202 -0500 reset: moving to c607fc30883e335def28cd686b51f6cfa02b06ec
c607fc30883e335def28cd686b51f6cfa02b06ec bd593285fc7fe4ca18ccdbabf027f5d689101452 Edward Thomson <ethomson@edwardthomson.com> 1351990205 -0500 merge master: Fast-forward
bd593285fc7fe4ca18ccdbabf027f5d689101452 fd89f8cffb663ac89095a0f9764902e93ceaca6a Edward Thomson <ethomson@edwardthomson.com> 1351990229 -0500 commit: fastforward
fd89f8cffb663ac89095a0f9764902e93ceaca6a bd593285fc7fe4ca18ccdbabf027f5d689101452 Edward Thomson <ethomson@edwardthomson.com> 1351990233 -0500 checkout: moving from ff_branch to master
bd593285fc7fe4ca18ccdbabf027f5d689101452 c607fc30883e335def28cd686b51f6cfa02b06ec Edward Thomson <ethomson@edwardthomson.com> 1352091703 -0600 checkout: moving from master to trivial-2alt
c607fc30883e335def28cd686b51f6cfa02b06ec c607fc30883e335def28cd686b51f6cfa02b06ec Edward Thomson <ethomson@edwardthomson.com> 1352092411 -0600 checkout: moving from trivial-2alt to trivial-2alt-branch
c607fc30883e335def28cd686b51f6cfa02b06ec c9174cef549ec94ecbc43ef03cdc775b4950becb Edward Thomson <ethomson@edwardthomson.com> 1352092434 -0600 commit: 2alt-branch
c9174cef549ec94ecbc43ef03cdc775b4950becb c607fc30883e335def28cd686b51f6cfa02b06ec Edward Thomson <ethomson@edwardthomson.com> 1352092440 -0600 checkout: moving from trivial-2alt-branch to trivial-2alt
c607fc30883e335def28cd686b51f6cfa02b06ec 566ab53c220a2eafc1212af1a024513230280ab9 Edward Thomson <ethomson@edwardthomson.com> 1352092452 -0600 commit: 2alt
bd593285fc7fe4ca18ccdbabf027f5d689101452 566ab53c220a2eafc1212af1a024513230280ab9 Edward Thomson <ethomson@edwardthomson.com> 1352094476 -0600 checkout: moving from master to trivial-3alt
566ab53c220a2eafc1212af1a024513230280ab9 c607fc30883e335def28cd686b51f6cfa02b06ec Edward Thomson <ethomson@edwardthomson.com> 1352094547 -0600 reset: moving to c607fc30883e335def28cd686b51f6cfa02b06ec
c607fc30883e335def28cd686b51f6cfa02b06ec 5459c89aa0026d543ce8343bd89871bce543f9c2 Edward Thomson <ethomson@edwardthomson.com> 1352094580 -0600 commit: 3alt
5459c89aa0026d543ce8343bd89871bce543f9c2 4c9fac0707f8d4195037ae5a681aa48626491541 Edward Thomson <ethomson@edwardthomson.com> 1352094610 -0600 commit: 3alt-branch
4c9fac0707f8d4195037ae5a681aa48626491541 bd593285fc7fe4ca18ccdbabf027f5d689101452 Edward Thomson <ethomson@edwardthomson.com> 1352094620 -0600 checkout: moving from trivial-3alt to master
bd593285fc7fe4ca18ccdbabf027f5d689101452 566ab53c220a2eafc1212af1a024513230280ab9 Edward Thomson <ethomson@edwardthomson.com> 1352094752 -0600 checkout: moving from master to trivial-4
566ab53c220a2eafc1212af1a024513230280ab9 c607fc30883e335def28cd686b51f6cfa02b06ec Edward Thomson <ethomson@edwardthomson.com> 1352094764 -0600 reset: moving to c607fc30883e335def28cd686b51f6cfa02b06ec
c607fc30883e335def28cd686b51f6cfa02b06ec cc3e3009134cb88014129fc8858d1101359e5e2f Edward Thomson <ethomson@edwardthomson.com> 1352094815 -0600 commit: trivial-4
cc3e3009134cb88014129fc8858d1101359e5e2f c607fc30883e335def28cd686b51f6cfa02b06ec Edward Thomson <ethomson@edwardthomson.com> 1352094843 -0600 checkout: moving from trivial-4 to trivial-4-branch
c607fc30883e335def28cd686b51f6cfa02b06ec 183310e30fb1499af8c619108ffea4d300b5e778 Edward Thomson <ethomson@edwardthomson.com> 1352094856 -0600 commit: trivial-4-branch
183310e30fb1499af8c619108ffea4d300b5e778 bd593285fc7fe4ca18ccdbabf027f5d689101452 Edward Thomson <ethomson@edwardthomson.com> 1352094860 -0600 checkout: moving from trivial-4-branch to master
bd593285fc7fe4ca18ccdbabf027f5d689101452 cc3e3009134cb88014129fc8858d1101359e5e2f Edward Thomson <ethomson@edwardthomson.com> 1352096588 -0600 checkout: moving from master to trivial-4
cc3e3009134cb88014129fc8858d1101359e5e2f c607fc30883e335def28cd686b51f6cfa02b06ec Edward Thomson <ethomson@edwardthomson.com> 1352096612 -0600 checkout: moving from trivial-4 to trivial-5alt-1
c607fc30883e335def28cd686b51f6cfa02b06ec 4fe93c0ec83eb6305cbace3dace88ecee1b63cb6 Edward Thomson <ethomson@edwardthomson.com> 1352096643 -0600 commit: 5alt-1
4fe93c0ec83eb6305cbace3dace88ecee1b63cb6 c607fc30883e335def28cd686b51f6cfa02b06ec Edward Thomson <ethomson@edwardthomson.com> 1352096661 -0600 checkout: moving from trivial-5alt-1 to trivial-5alt-1-branch
c607fc30883e335def28cd686b51f6cfa02b06ec 4fe93c0ec83eb6305cbace3dace88ecee1b63cb6 Edward Thomson <ethomson@edwardthomson.com> 1352096671 -0600 checkout: moving from trivial-5alt-1-branch to trivial-5alt-1
4fe93c0ec83eb6305cbace3dace88ecee1b63cb6 c607fc30883e335def28cd686b51f6cfa02b06ec Edward Thomson <ethomson@edwardthomson.com> 1352096678 -0600 checkout: moving from trivial-5alt-1 to trivial-5alt-1-branch
c607fc30883e335def28cd686b51f6cfa02b06ec 478172cb2f5ff9b514bc9d04d3bd5ef5840cb3b2 Edward Thomson <ethomson@edwardthomson.com> 1352096689 -0600 commit: 5alt-1-branch
478172cb2f5ff9b514bc9d04d3bd5ef5840cb3b2 4fe93c0ec83eb6305cbace3dace88ecee1b63cb6 Edward Thomson <ethomson@edwardthomson.com> 1352096701 -0600 checkout: moving from trivial-5alt-1-branch to trivial-5alt-1
4fe93c0ec83eb6305cbace3dace88ecee1b63cb6 c607fc30883e335def28cd686b51f6cfa02b06ec Edward Thomson <ethomson@edwardthomson.com> 1352096715 -0600 checkout: moving from trivial-5alt-1 to trivial-5alt-2
c607fc30883e335def28cd686b51f6cfa02b06ec ebc09d0137cfb0c26697aed0109fb943ad906f3f Edward Thomson <ethomson@edwardthomson.com> 1352096764 -0600 commit: existing file
ebc09d0137cfb0c26697aed0109fb943ad906f3f 3b47b031b3e55ae11e14a05260b1c3ffd6838d55 Edward Thomson <ethomson@edwardthomson.com> 1352096815 -0600 commit: 5alt-2
3b47b031b3e55ae11e14a05260b1c3ffd6838d55 ebc09d0137cfb0c26697aed0109fb943ad906f3f Edward Thomson <ethomson@edwardthomson.com> 1352096840 -0600 checkout: moving from trivial-5alt-2 to trivial-5alt-2-branch
ebc09d0137cfb0c26697aed0109fb943ad906f3f f48097eb340dc5a7cae55aabcf1faf4548aa821f Edward Thomson <ethomson@edwardthomson.com> 1352096855 -0600 commit: 5alt-2-branch
f48097eb340dc5a7cae55aabcf1faf4548aa821f bd593285fc7fe4ca18ccdbabf027f5d689101452 Edward Thomson <ethomson@edwardthomson.com> 1352096858 -0600 checkout: moving from trivial-5alt-2-branch to master
bd593285fc7fe4ca18ccdbabf027f5d689101452 c607fc30883e335def28cd686b51f6cfa02b06ec Edward Thomson <ethomson@edwardthomson.com> 1352097377 -0600 checkout: moving from master to trivial-6
c607fc30883e335def28cd686b51f6cfa02b06ec f7c332bd4d4d4b777366cae4d24d1687477576bf Edward Thomson <ethomson@edwardthomson.com> 1352097389 -0600 commit: 6
f7c332bd4d4d4b777366cae4d24d1687477576bf 99b4f7e4f24470fa06b980bc21f1095c2a9425c0 Edward Thomson <ethomson@edwardthomson.com> 1352097404 -0600 commit: trivial-6
99b4f7e4f24470fa06b980bc21f1095c2a9425c0 f7c332bd4d4d4b777366cae4d24d1687477576bf Edward Thomson <ethomson@edwardthomson.com> 1352097420 -0600 checkout: moving from trivial-6 to trivial-6-branch
f7c332bd4d4d4b777366cae4d24d1687477576bf a43150a738849c59376cf30bb2a68348a83c8f48 Edward Thomson <ethomson@edwardthomson.com> 1352097431 -0600 commit: 6-branch
a43150a738849c59376cf30bb2a68348a83c8f48 bd593285fc7fe4ca18ccdbabf027f5d689101452 Edward Thomson <ethomson@edwardthomson.com> 1352097442 -0600 checkout: moving from trivial-6-branch to master
bd593285fc7fe4ca18ccdbabf027f5d689101452 99b4f7e4f24470fa06b980bc21f1095c2a9425c0 Edward Thomson <ethomson@edwardthomson.com> 1352098040 -0600 checkout: moving from master to trivial-6
99b4f7e4f24470fa06b980bc21f1095c2a9425c0 bd593285fc7fe4ca18ccdbabf027f5d689101452 Edward Thomson <ethomson@edwardthomson.com> 1352098057 -0600 checkout: moving from trivial-6 to master
bd593285fc7fe4ca18ccdbabf027f5d689101452 cc3e3009134cb88014129fc8858d1101359e5e2f Edward Thomson <ethomson@edwardthomson.com> 1352098792 -0600 checkout: moving from master to trivial-4
cc3e3009134cb88014129fc8858d1101359e5e2f c607fc30883e335def28cd686b51f6cfa02b06ec Edward Thomson <ethomson@edwardthomson.com> 1352098818 -0600 checkout: moving from trivial-4 to trivial-8
c607fc30883e335def28cd686b51f6cfa02b06ec 75a811bf6bc57694adb3fe604786f3a4efd1cd1b Edward Thomson <ethomson@edwardthomson.com> 1352098884 -0600 commit: trivial-8
75a811bf6bc57694adb3fe604786f3a4efd1cd1b 75a811bf6bc57694adb3fe604786f3a4efd1cd1b Edward Thomson <ethomson@edwardthomson.com> 1352098947 -0600 checkout: moving from trivial-8 to trivial-8-branch
75a811bf6bc57694adb3fe604786f3a4efd1cd1b 52d8bc572af2b6d4ee0d5e62ed5d1fbad92210a9 Edward Thomson <ethomson@edwardthomson.com> 1352098979 -0600 commit: trivial-8-branch
52d8bc572af2b6d4ee0d5e62ed5d1fbad92210a9 75a811bf6bc57694adb3fe604786f3a4efd1cd1b Edward Thomson <ethomson@edwardthomson.com> 1352098982 -0600 checkout: moving from trivial-8-branch to trivial-8
75a811bf6bc57694adb3fe604786f3a4efd1cd1b 3575826c96a975031d2c14368529cc5c4353a8fd Edward Thomson <ethomson@edwardthomson.com> 1352099000 -0600 commit: trivial-8
3575826c96a975031d2c14368529cc5c4353a8fd bd593285fc7fe4ca18ccdbabf027f5d689101452 Edward Thomson <ethomson@edwardthomson.com> 1352099008 -0600 checkout: moving from trivial-8 to master
bd593285fc7fe4ca18ccdbabf027f5d689101452 c607fc30883e335def28cd686b51f6cfa02b06ec Edward Thomson <ethomson@edwardthomson.com> 1352099776 -0600 checkout: moving from master to trivial-7
c607fc30883e335def28cd686b51f6cfa02b06ec 092ce8682d7f3a2a3a769a6daca58950168ba5c4 Edward Thomson <ethomson@edwardthomson.com> 1352099790 -0600 commit: trivial-7
092ce8682d7f3a2a3a769a6daca58950168ba5c4 092ce8682d7f3a2a3a769a6daca58950168ba5c4 Edward Thomson <ethomson@edwardthomson.com> 1352099799 -0600 checkout: moving from trivial-7 to trivial-7-branch
092ce8682d7f3a2a3a769a6daca58950168ba5c4 73cbfdc4fe843169e5b2af8dcad03cbf3acf306c Edward Thomson <ethomson@edwardthomson.com> 1352099812 -0600 commit: trivial-7-branch
73cbfdc4fe843169e5b2af8dcad03cbf3acf306c 092ce8682d7f3a2a3a769a6daca58950168ba5c4 Edward Thomson <ethomson@edwardthomson.com> 1352099815 -0600 checkout: moving from trivial-7-branch to trivial-7
092ce8682d7f3a2a3a769a6daca58950168ba5c4 73cbfdc4fe843169e5b2af8dcad03cbf3acf306c Edward Thomson <ethomson@edwardthomson.com> 1352099838 -0600 checkout: moving from trivial-7 to trivial-7-branch
73cbfdc4fe843169e5b2af8dcad03cbf3acf306c 092ce8682d7f3a2a3a769a6daca58950168ba5c4 Edward Thomson <ethomson@edwardthomson.com> 1352099874 -0600 reset: moving to 092ce8682d7f3a2a3a769a6daca58950168ba5c4
092ce8682d7f3a2a3a769a6daca58950168ba5c4 009b9cab6fdac02915a88ecd078b7a792ed802d8 Edward Thomson <ethomson@edwardthomson.com> 1352099921 -0600 commit: removed in 7
009b9cab6fdac02915a88ecd078b7a792ed802d8 5195a1b480f66691b667f10a9e41e70115a78351 Edward Thomson <ethomson@edwardthomson.com> 1352099927 -0600 commit (amend): trivial-7-branch
5195a1b480f66691b667f10a9e41e70115a78351 092ce8682d7f3a2a3a769a6daca58950168ba5c4 Edward Thomson <ethomson@edwardthomson.com> 1352099937 -0600 checkout: moving from trivial-7-branch to trivial-7
092ce8682d7f3a2a3a769a6daca58950168ba5c4 d874671ef5b20184836cb983bb273e5280384d0b Edward Thomson <ethomson@edwardthomson.com> 1352099947 -0600 commit: trivial-7
d874671ef5b20184836cb983bb273e5280384d0b bd593285fc7fe4ca18ccdbabf027f5d689101452 Edward Thomson <ethomson@edwardthomson.com> 1352099949 -0600 checkout: moving from trivial-7 to master
bd593285fc7fe4ca18ccdbabf027f5d689101452 c607fc30883e335def28cd686b51f6cfa02b06ec Edward Thomson <ethomson@edwardthomson.com> 1352100174 -0600 checkout: moving from master to trivial-10
c607fc30883e335def28cd686b51f6cfa02b06ec 53825f41ac8d640612f9423a2f03a69f3d96809a Edward Thomson <ethomson@edwardthomson.com> 1352100193 -0600 commit: trivial-10
53825f41ac8d640612f9423a2f03a69f3d96809a 53825f41ac8d640612f9423a2f03a69f3d96809a Edward Thomson <ethomson@edwardthomson.com> 1352100200 -0600 checkout: moving from trivial-10 to trivial-10-branch
53825f41ac8d640612f9423a2f03a69f3d96809a 11f4f3c08b737f5fd896cbefa1425ee63b21b2fa Edward Thomson <ethomson@edwardthomson.com> 1352100211 -0600 commit: trivial-10-branch
11f4f3c08b737f5fd896cbefa1425ee63b21b2fa 53825f41ac8d640612f9423a2f03a69f3d96809a Edward Thomson <ethomson@edwardthomson.com> 1352100214 -0600 checkout: moving from trivial-10-branch to trivial-10
53825f41ac8d640612f9423a2f03a69f3d96809a 0ec5f433959cd46177f745903353efb5be08d151 Edward Thomson <ethomson@edwardthomson.com> 1352100223 -0600 commit: trivial-10
0ec5f433959cd46177f745903353efb5be08d151 bd593285fc7fe4ca18ccdbabf027f5d689101452 Edward Thomson <ethomson@edwardthomson.com> 1352100225 -0600 checkout: moving from trivial-10 to master
bd593285fc7fe4ca18ccdbabf027f5d689101452 c607fc30883e335def28cd686b51f6cfa02b06ec Edward Thomson <ethomson@edwardthomson.com> 1352100270 -0600 checkout: moving from master to trivial-9
c607fc30883e335def28cd686b51f6cfa02b06ec f0053b8060bb3f0be5cbcc3147a07ece26bf097e Edward Thomson <ethomson@edwardthomson.com> 1352100304 -0600 commit: trivial-9
f0053b8060bb3f0be5cbcc3147a07ece26bf097e f0053b8060bb3f0be5cbcc3147a07ece26bf097e Edward Thomson <ethomson@edwardthomson.com> 1352100310 -0600 checkout: moving from trivial-9 to trivial-9-branch
f0053b8060bb3f0be5cbcc3147a07ece26bf097e 13d1be4ea52a6ced1d7a1d832f0ee3c399348e5e Edward Thomson <ethomson@edwardthomson.com> 1352100317 -0600 commit: trivial-9-branch
13d1be4ea52a6ced1d7a1d832f0ee3c399348e5e f0053b8060bb3f0be5cbcc3147a07ece26bf097e Edward Thomson <ethomson@edwardthomson.com> 1352100319 -0600 checkout: moving from trivial-9-branch to trivial-9
f0053b8060bb3f0be5cbcc3147a07ece26bf097e c35dee9bcc0e989f3b0c40f68372a9a51b6c4e6a Edward Thomson <ethomson@edwardthomson.com> 1352100333 -0600 commit: trivial-9
c35dee9bcc0e989f3b0c40f68372a9a51b6c4e6a bd593285fc7fe4ca18ccdbabf027f5d689101452 Edward Thomson <ethomson@edwardthomson.com> 1352100335 -0600 checkout: moving from trivial-9 to master
bd593285fc7fe4ca18ccdbabf027f5d689101452 c607fc30883e335def28cd686b51f6cfa02b06ec Edward Thomson <ethomson@edwardthomson.com> 1352100576 -0600 checkout: moving from master to trivial-13
c607fc30883e335def28cd686b51f6cfa02b06ec 8f4433f8593ddd65b7dd43dd4564d841f4d9c8aa Edward Thomson <ethomson@edwardthomson.com> 1352100589 -0600 commit: trivial-13
8f4433f8593ddd65b7dd43dd4564d841f4d9c8aa 8f4433f8593ddd65b7dd43dd4564d841f4d9c8aa Edward Thomson <ethomson@edwardthomson.com> 1352100604 -0600 checkout: moving from trivial-13 to trivial-13-branch
8f4433f8593ddd65b7dd43dd4564d841f4d9c8aa 05f3c1a2a56ca95c3d2ef28dc9ddf32b5cd6c91c Edward Thomson <ethomson@edwardthomson.com> 1352100610 -0600 commit: trivial-13-branch
05f3c1a2a56ca95c3d2ef28dc9ddf32b5cd6c91c 8f4433f8593ddd65b7dd43dd4564d841f4d9c8aa Edward Thomson <ethomson@edwardthomson.com> 1352100612 -0600 checkout: moving from trivial-13-branch to trivial-13
8f4433f8593ddd65b7dd43dd4564d841f4d9c8aa a3fabece9eb8748da810e1e08266fef9b7136ad4 Edward Thomson <ethomson@edwardthomson.com> 1352100625 -0600 commit: trivial-13
a3fabece9eb8748da810e1e08266fef9b7136ad4 bd593285fc7fe4ca18ccdbabf027f5d689101452 Edward Thomson <ethomson@edwardthomson.com> 1352100627 -0600 checkout: moving from trivial-13 to master
bd593285fc7fe4ca18ccdbabf027f5d689101452 c607fc30883e335def28cd686b51f6cfa02b06ec Edward Thomson <ethomson@edwardthomson.com> 1352100936 -0600 checkout: moving from master to trivial-11
c607fc30883e335def28cd686b51f6cfa02b06ec 35632e43612c06a3ea924bfbacd48333da874c29 Edward Thomson <ethomson@edwardthomson.com> 1352100958 -0600 commit: trivial-11
35632e43612c06a3ea924bfbacd48333da874c29 35632e43612c06a3ea924bfbacd48333da874c29 Edward Thomson <ethomson@edwardthomson.com> 1352100964 -0600 checkout: moving from trivial-11 to trivial-11-branch
35632e43612c06a3ea924bfbacd48333da874c29 6718a45909532d1fcf5600d0877f7fe7e78f0b86 Edward Thomson <ethomson@edwardthomson.com> 1352100978 -0600 commit: trivial-11-branch
6718a45909532d1fcf5600d0877f7fe7e78f0b86 35632e43612c06a3ea924bfbacd48333da874c29 Edward Thomson <ethomson@edwardthomson.com> 1352100981 -0600 checkout: moving from trivial-11-branch to trivial-11
35632e43612c06a3ea924bfbacd48333da874c29 3168dca1a561889b045a6441909f4c56145e666d Edward Thomson <ethomson@edwardthomson.com> 1352100992 -0600 commit: trivial-11
3168dca1a561889b045a6441909f4c56145e666d bd593285fc7fe4ca18ccdbabf027f5d689101452 Edward Thomson <ethomson@edwardthomson.com> 1352100996 -0600 checkout: moving from trivial-11 to master
bd593285fc7fe4ca18ccdbabf027f5d689101452 c607fc30883e335def28cd686b51f6cfa02b06ec Edward Thomson <ethomson@edwardthomson.com> 1352101098 -0600 checkout: moving from master to trivial-14
c607fc30883e335def28cd686b51f6cfa02b06ec 596803b523203a4851c824c07366906f8353f4ad Edward Thomson <ethomson@edwardthomson.com> 1352101113 -0600 commit: trivial-14
596803b523203a4851c824c07366906f8353f4ad 596803b523203a4851c824c07366906f8353f4ad Edward Thomson <ethomson@edwardthomson.com> 1352101117 -0600 checkout: moving from trivial-14 to trivial-14-branch
596803b523203a4851c824c07366906f8353f4ad 8187117062b750eed4f93fd7e899f17b52ce554d Edward Thomson <ethomson@edwardthomson.com> 1352101132 -0600 commit: trivial-14-branch
8187117062b750eed4f93fd7e899f17b52ce554d 596803b523203a4851c824c07366906f8353f4ad Edward Thomson <ethomson@edwardthomson.com> 1352101135 -0600 checkout: moving from trivial-14-branch to trivial-14
596803b523203a4851c824c07366906f8353f4ad 7e2d058d5fedf8329db44db4fac610d6b1a89159 Edward Thomson <ethomson@edwardthomson.com> 1352101141 -0600 commit: trivial-14
7e2d058d5fedf8329db44db4fac610d6b1a89159 bd593285fc7fe4ca18ccdbabf027f5d689101452 Edward Thomson <ethomson@edwardthomson.com> 1352101145 -0600 checkout: moving from trivial-14 to master
bd593285fc7fe4ca18ccdbabf027f5d689101452 c607fc30883e335def28cd686b51f6cfa02b06ec Edward Thomson <ethomson@edwardthomson.com> 1353177749 -0600 checkout: moving from master to renames1
c607fc30883e335def28cd686b51f6cfa02b06ec 412b32fb66137366147f1801ecc962452757d48a Edward Thomson <ethomson@edwardthomson.com> 1353177886 -0600 commit: renames
412b32fb66137366147f1801ecc962452757d48a bd593285fc7fe4ca18ccdbabf027f5d689101452 Edward Thomson <ethomson@edwardthomson.com> 1353794607 -0600 checkout: moving from renames1 to master
bd593285fc7fe4ca18ccdbabf027f5d689101452 bd593285fc7fe4ca18ccdbabf027f5d689101452 Edward Thomson <ethomson@edwardthomson.com> 1353794647 -0600 checkout: moving from master to renames2
bd593285fc7fe4ca18ccdbabf027f5d689101452 c607fc30883e335def28cd686b51f6cfa02b06ec Edward Thomson <ethomson@edwardthomson.com> 1353794677 -0600 reset: moving to c607fc30883e335def28cd686b51f6cfa02b06ec
c607fc30883e335def28cd686b51f6cfa02b06ec ab40af3cb8a3ed2e2843e96d9aa7871336b94573 Edward Thomson <ethomson@edwardthomson.com> 1353794852 -0600 commit: renames2
ab40af3cb8a3ed2e2843e96d9aa7871336b94573 bd593285fc7fe4ca18ccdbabf027f5d689101452 Edward Thomson <ethomson@edwardthomson.com> 1353794883 -0600 checkout: moving from renames2 to master
bd593285fc7fe4ca18ccdbabf027f5d689101452 bd593285fc7fe4ca18ccdbabf027f5d689101452 Edward Thomson <ethomson@edwardthomson.com> 1354574697 -0600 checkout: moving from master to df_side1
bd593285fc7fe4ca18ccdbabf027f5d689101452 d4207f77243500bec335ab477f9227fcdb1e271a Edward Thomson <ethomson@edwardthomson.com> 1354574962 -0600 commit: df_ancestor
d4207f77243500bec335ab477f9227fcdb1e271a c94b27e41064c521120627e07e2035cca1d24ffa Edward Thomson <ethomson@edwardthomson.com> 1354575027 -0600 commit: df_side1
c94b27e41064c521120627e07e2035cca1d24ffa d4207f77243500bec335ab477f9227fcdb1e271a Edward Thomson <ethomson@edwardthomson.com> 1354575070 -0600 checkout: moving from df_side1 to df_side2
d4207f77243500bec335ab477f9227fcdb1e271a f8958bdf4d365a84a9a178b1f5f35ff1dacbd884 Edward Thomson <ethomson@edwardthomson.com> 1354575206 -0600 commit: df_side2
f8958bdf4d365a84a9a178b1f5f35ff1dacbd884 bd593285fc7fe4ca18ccdbabf027f5d689101452 Edward Thomson <ethomson@edwardthomson.com> 1354575381 -0600 checkout: moving from df_side2 to master
bd593285fc7fe4ca18ccdbabf027f5d689101452 c94b27e41064c521120627e07e2035cca1d24ffa Edward Thomson <ethomson@edwardthomson.com> 1355017614 -0600 checkout: moving from master to df_side1
c94b27e41064c521120627e07e2035cca1d24ffa a90bc3fb6f15181972a2959a921429efbd81a473 Edward Thomson <ethomson@edwardthomson.com> 1355017650 -0600 commit: df_added
a90bc3fb6f15181972a2959a921429efbd81a473 c94b27e41064c521120627e07e2035cca1d24ffa Edward Thomson <ethomson@edwardthomson.com> 1355017673 -0600 checkout: moving from df_side1 to c94b27e
c94b27e41064c521120627e07e2035cca1d24ffa d4207f77243500bec335ab477f9227fcdb1e271a Edward Thomson <ethomson@edwardthomson.com> 1355017673 -0600 rebase -i (squash): updating HEAD
d4207f77243500bec335ab477f9227fcdb1e271a 005b6fcc8fec71d2550bef8462d169b3c26aa14b Edward Thomson <ethomson@edwardthomson.com> 1355017673 -0600 rebase -i (squash): df_side1
005b6fcc8fec71d2550bef8462d169b3c26aa14b 005b6fcc8fec71d2550bef8462d169b3c26aa14b Edward Thomson <ethomson@edwardthomson.com> 1355017676 -0600 rebase -i (finish): returning to refs/heads/df_side1
005b6fcc8fec71d2550bef8462d169b3c26aa14b f8958bdf4d365a84a9a178b1f5f35ff1dacbd884 Edward Thomson <ethomson@edwardthomson.com> 1355017715 -0600 reset: moving to df_side2
f8958bdf4d365a84a9a178b1f5f35ff1dacbd884 8c749d9968d4b10dcfb06c9f97d0e5d92d337071 Edward Thomson <ethomson@edwardthomson.com> 1355017744 -0600 commit: df_added
8c749d9968d4b10dcfb06c9f97d0e5d92d337071 f8958bdf4d365a84a9a178b1f5f35ff1dacbd884 Edward Thomson <ethomson@edwardthomson.com> 1355017754 -0600 checkout: moving from df_side1 to f8958bd
f8958bdf4d365a84a9a178b1f5f35ff1dacbd884 d4207f77243500bec335ab477f9227fcdb1e271a Edward Thomson <ethomson@edwardthomson.com> 1355017754 -0600 rebase -i (squash): updating HEAD
d4207f77243500bec335ab477f9227fcdb1e271a 0204a84f822acbf6386b36d33f1f6bc68bbbf858 Edward Thomson <ethomson@edwardthomson.com> 1355017754 -0600 rebase -i (squash): df_side2
0204a84f822acbf6386b36d33f1f6bc68bbbf858 0204a84f822acbf6386b36d33f1f6bc68bbbf858 Edward Thomson <ethomson@edwardthomson.com> 1355017756 -0600 rebase -i (finish): returning to refs/heads/df_side1
0204a84f822acbf6386b36d33f1f6bc68bbbf858 005b6fcc8fec71d2550bef8462d169b3c26aa14b Edward Thomson <ethomson@edwardthomson.com> 1355017793 -0600 reset: moving to 005b6fcc8fec71d2550bef8462d169b3c26aa14b
005b6fcc8fec71d2550bef8462d169b3c26aa14b 0204a84f822acbf6386b36d33f1f6bc68bbbf858 Edward Thomson <ethomson@edwardthomson.com> 1355017826 -0600 reset: moving to 0204a84
0204a84f822acbf6386b36d33f1f6bc68bbbf858 bd593285fc7fe4ca18ccdbabf027f5d689101452 Edward Thomson <ethomson@edwardthomson.com> 1355017847 -0600 checkout: moving from df_side1 to master
bd593285fc7fe4ca18ccdbabf027f5d689101452 0204a84f822acbf6386b36d33f1f6bc68bbbf858 Edward Thomson <ethomson@edwardthomson.com> 1355168677 -0600 checkout: moving from master to df_side1
005b6fcc8fec71d2550bef8462d169b3c26aa14b 005b6fcc8fec71d2550bef8462d169b3c26aa14b Edward Thomson <ethomson@edwardthomson.com> 1355168829 -0600 checkout: moving from df_side1 to df_side1
005b6fcc8fec71d2550bef8462d169b3c26aa14b 005b6fcc8fec71d2550bef8462d169b3c26aa14b Edward Thomson <ethomson@edwardthomson.com> 1355168838 -0600 checkout: moving from df_side1 to df_side1
005b6fcc8fec71d2550bef8462d169b3c26aa14b e8107f24196736b870a318a0e28f048e29f6feff Edward Thomson <ethomson@edwardthomson.com> 1355169065 -0600 commit: df_side1
e8107f24196736b870a318a0e28f048e29f6feff 005b6fcc8fec71d2550bef8462d169b3c26aa14b Edward Thomson <ethomson@edwardthomson.com> 1355169081 -0600 checkout: moving from df_side1 to 005b6fc
005b6fcc8fec71d2550bef8462d169b3c26aa14b d4207f77243500bec335ab477f9227fcdb1e271a Edward Thomson <ethomson@edwardthomson.com> 1355169081 -0600 rebase -i (squash): updating HEAD
d4207f77243500bec335ab477f9227fcdb1e271a 80a8fbb3abb1ba423d554e9630b8fc2e5698f86b Edward Thomson <ethomson@edwardthomson.com> 1355169081 -0600 rebase -i (squash): df_side1
80a8fbb3abb1ba423d554e9630b8fc2e5698f86b 80a8fbb3abb1ba423d554e9630b8fc2e5698f86b Edward Thomson <ethomson@edwardthomson.com> 1355169084 -0600 rebase -i (finish): returning to refs/heads/df_side1
80a8fbb3abb1ba423d554e9630b8fc2e5698f86b 0204a84f822acbf6386b36d33f1f6bc68bbbf858 Edward Thomson <ethomson@edwardthomson.com> 1355169141 -0600 checkout: moving from df_side1 to df_side2
0204a84f822acbf6386b36d33f1f6bc68bbbf858 944f5dd1a867cab4c2bbcb896493435cae1dcc1a Edward Thomson <ethomson@edwardthomson.com> 1355169174 -0600 commit: both
944f5dd1a867cab4c2bbcb896493435cae1dcc1a 0204a84f822acbf6386b36d33f1f6bc68bbbf858 Edward Thomson <ethomson@edwardthomson.com> 1355169182 -0600 checkout: moving from df_side2 to 0204a84
0204a84f822acbf6386b36d33f1f6bc68bbbf858 d4207f77243500bec335ab477f9227fcdb1e271a Edward Thomson <ethomson@edwardthomson.com> 1355169182 -0600 rebase -i (squash): updating HEAD
d4207f77243500bec335ab477f9227fcdb1e271a 57079a46233ae2b6df62e9ade71c4948512abefb Edward Thomson <ethomson@edwardthomson.com> 1355169182 -0600 rebase -i (squash): df_side2
57079a46233ae2b6df62e9ade71c4948512abefb 57079a46233ae2b6df62e9ade71c4948512abefb Edward Thomson <ethomson@edwardthomson.com> 1355169185 -0600 rebase -i (finish): returning to refs/heads/df_side2
57079a46233ae2b6df62e9ade71c4948512abefb 80a8fbb3abb1ba423d554e9630b8fc2e5698f86b Edward Thomson <ethomson@edwardthomson.com> 1355169241 -0600 checkout: moving from df_side2 to df_side1
80a8fbb3abb1ba423d554e9630b8fc2e5698f86b e65a9bb2af9f4c2d1c375dd0f8f8a46cf9c68812 Edward Thomson <ethomson@edwardthomson.com> 1355169419 -0600 commit: side1
e65a9bb2af9f4c2d1c375dd0f8f8a46cf9c68812 80a8fbb3abb1ba423d554e9630b8fc2e5698f86b Edward Thomson <ethomson@edwardthomson.com> 1355169431 -0600 checkout: moving from df_side1 to 80a8fbb
80a8fbb3abb1ba423d554e9630b8fc2e5698f86b d4207f77243500bec335ab477f9227fcdb1e271a Edward Thomson <ethomson@edwardthomson.com> 1355169431 -0600 rebase -i (squash): updating HEAD
d4207f77243500bec335ab477f9227fcdb1e271a 5dc1018e90b19654bee986b7a0c268804d39659d Edward Thomson <ethomson@edwardthomson.com> 1355169431 -0600 rebase -i (squash): df_side1
5dc1018e90b19654bee986b7a0c268804d39659d 5dc1018e90b19654bee986b7a0c268804d39659d Edward Thomson <ethomson@edwardthomson.com> 1355169435 -0600 rebase -i (finish): returning to refs/heads/df_side1
5dc1018e90b19654bee986b7a0c268804d39659d 57079a46233ae2b6df62e9ade71c4948512abefb Edward Thomson <ethomson@edwardthomson.com> 1355169439 -0600 checkout: moving from df_side1 to df_side2
57079a46233ae2b6df62e9ade71c4948512abefb 58e853f66699fd02629fd50bde08082bc005933a Edward Thomson <ethomson@edwardthomson.com> 1355169460 -0600 commit: side2
58e853f66699fd02629fd50bde08082bc005933a 57079a46233ae2b6df62e9ade71c4948512abefb Edward Thomson <ethomson@edwardthomson.com> 1355169469 -0600 checkout: moving from df_side2 to 57079a4
57079a46233ae2b6df62e9ade71c4948512abefb d4207f77243500bec335ab477f9227fcdb1e271a Edward Thomson <ethomson@edwardthomson.com> 1355169469 -0600 rebase -i (squash): updating HEAD
d4207f77243500bec335ab477f9227fcdb1e271a fada9356aa3f74622327a3038ae9c6f92e1c5c1d Edward Thomson <ethomson@edwardthomson.com> 1355169469 -0600 rebase -i (squash): df_side2
fada9356aa3f74622327a3038ae9c6f92e1c5c1d fada9356aa3f74622327a3038ae9c6f92e1c5c1d Edward Thomson <ethomson@edwardthomson.com> 1355169471 -0600 rebase -i (finish): returning to refs/heads/df_side2
fada9356aa3f74622327a3038ae9c6f92e1c5c1d 5dc1018e90b19654bee986b7a0c268804d39659d Edward Thomson <ethomson@edwardthomson.com> 1355169494 -0600 checkout: moving from df_side2 to df_side1
5dc1018e90b19654bee986b7a0c268804d39659d d4207f77243500bec335ab477f9227fcdb1e271a Edward Thomson <ethomson@edwardthomson.com> 1355169663 -0600 checkout: moving from df_side1 to d4207f77243500bec335ab477f9227fcdb1e271a
d4207f77243500bec335ab477f9227fcdb1e271a 849619b03ae540acee4d1edec96b86993da6b497 Edward Thomson <ethomson@edwardthomson.com> 1355169683 -0600 commit: both_dirs
849619b03ae540acee4d1edec96b86993da6b497 d4207f77243500bec335ab477f9227fcdb1e271a Edward Thomson <ethomson@edwardthomson.com> 1355169691 -0600 checkout: moving from 849619b03ae540acee4d1edec96b86993da6b497 to d4207f7
d4207f77243500bec335ab477f9227fcdb1e271a bd593285fc7fe4ca18ccdbabf027f5d689101452 Edward Thomson <ethomson@edwardthomson.com> 1355169691 -0600 rebase -i (squash): updating HEAD
bd593285fc7fe4ca18ccdbabf027f5d689101452 a765fb87eb2f7a1920b73b2d5a057f8f8476a42b Edward Thomson <ethomson@edwardthomson.com> 1355169691 -0600 rebase -i (squash): df_ancestor
a765fb87eb2f7a1920b73b2d5a057f8f8476a42b 5dc1018e90b19654bee986b7a0c268804d39659d Edward Thomson <ethomson@edwardthomson.com> 1355169706 -0600 checkout: moving from a765fb87eb2f7a1920b73b2d5a057f8f8476a42b to df_side1
5dc1018e90b19654bee986b7a0c268804d39659d a765fb87eb2f7a1920b73b2d5a057f8f8476a42b Edward Thomson <ethomson@edwardthomson.com> 1355169715 -0600 checkout: moving from df_side1 to a765fb87eb2f7a1920b73b2d5a057f8f8476a42b^0
a765fb87eb2f7a1920b73b2d5a057f8f8476a42b bc744705e1d8a019993cf88f62bc4020f1b80919 Edward Thomson <ethomson@edwardthomson.com> 1355169801 -0600 commit: df_side1
bc744705e1d8a019993cf88f62bc4020f1b80919 bc744705e1d8a019993cf88f62bc4020f1b80919 Edward Thomson <ethomson@edwardthomson.com> 1355169822 -0600 checkout: moving from bc744705e1d8a019993cf88f62bc4020f1b80919 to df_side1
bc744705e1d8a019993cf88f62bc4020f1b80919 fada9356aa3f74622327a3038ae9c6f92e1c5c1d Edward Thomson <ethomson@edwardthomson.com> 1355169826 -0600 checkout: moving from df_side1 to df_side2
fada9356aa3f74622327a3038ae9c6f92e1c5c1d a765fb87eb2f7a1920b73b2d5a057f8f8476a42b Edward Thomson <ethomson@edwardthomson.com> 1355169866 -0600 checkout: moving from df_side2 to a765fb87eb2f7a1920b73b2d5a057f8f8476a42b^0
a765fb87eb2f7a1920b73b2d5a057f8f8476a42b 95646149ab6b6ba6edc83cff678582538b457b2b Edward Thomson <ethomson@edwardthomson.com> 1355169897 -0600 rebase: df_side2
95646149ab6b6ba6edc83cff678582538b457b2b 95646149ab6b6ba6edc83cff678582538b457b2b Edward Thomson <ethomson@edwardthomson.com> 1355169897 -0600 rebase finished: returning to refs/heads/df_side2
95646149ab6b6ba6edc83cff678582538b457b2b bc744705e1d8a019993cf88f62bc4020f1b80919 Edward Thomson <ethomson@edwardthomson.com> 1355169949 -0600 checkout: moving from df_side2 to df_side1
bc744705e1d8a019993cf88f62bc4020f1b80919 bd593285fc7fe4ca18ccdbabf027f5d689101452 Edward Thomson <ethomson@edwardthomson.com> 1355170046 -0600 checkout: moving from df_side1 to master
bd593285fc7fe4ca18ccdbabf027f5d689101452 bd593285fc7fe4ca18ccdbabf027f5d689101452 Edward Thomson <ethomson@edwardthomson.com> 1355181639 -0600 checkout: moving from master to df_ancestor
bd593285fc7fe4ca18ccdbabf027f5d689101452 2da538570bc1e5b2c3e855bf702f35248ad0735f Edward Thomson <ethomson@edwardthomson.com> 1355181673 -0600 commit: df_ancestor
2da538570bc1e5b2c3e855bf702f35248ad0735f a7dbfcbfc1a60709cb80b5ca24539008456531d0 Edward Thomson <ethomson@edwardthomson.com> 1355181715 -0600 commit: df_side1
a7dbfcbfc1a60709cb80b5ca24539008456531d0 a7dbfcbfc1a60709cb80b5ca24539008456531d0 Edward Thomson <ethomson@edwardthomson.com> 1355181743 -0600 checkout: moving from df_ancestor to df_ancestor
a7dbfcbfc1a60709cb80b5ca24539008456531d0 9a301fbe6fada7dcb74fcd7c20269b5c743459a7 Edward Thomson <ethomson@edwardthomson.com> 1355181775 -0600 commit: df_side2
9a301fbe6fada7dcb74fcd7c20269b5c743459a7 a7dbfcbfc1a60709cb80b5ca24539008456531d0 Edward Thomson <ethomson@edwardthomson.com> 1355181793 -0600 checkout: moving from df_ancestor to df_side1
a7dbfcbfc1a60709cb80b5ca24539008456531d0 9a301fbe6fada7dcb74fcd7c20269b5c743459a7 Edward Thomson <ethomson@edwardthomson.com> 1355181797 -0600 checkout: moving from df_side1 to df_side2
9a301fbe6fada7dcb74fcd7c20269b5c743459a7 9a301fbe6fada7dcb74fcd7c20269b5c743459a7 Edward Thomson <ethomson@edwardthomson.com> 1355182062 -0600 checkout: moving from df_side2 to df_ancestor
9a301fbe6fada7dcb74fcd7c20269b5c743459a7 2da538570bc1e5b2c3e855bf702f35248ad0735f Edward Thomson <ethomson@edwardthomson.com> 1355182067 -0600 reset: moving to 2da538570bc1e5b2c3e855bf702f35248ad0735f
2da538570bc1e5b2c3e855bf702f35248ad0735f 2da538570bc1e5b2c3e855bf702f35248ad0735f Edward Thomson <ethomson@edwardthomson.com> 1355182087 -0600 checkout: moving from df_ancestor to df_side2
2da538570bc1e5b2c3e855bf702f35248ad0735f fc90237dc4891fa6c69827fc465632225e391618 Edward Thomson <ethomson@edwardthomson.com> 1355182104 -0600 commit: df_side2
fc90237dc4891fa6c69827fc465632225e391618 a7dbfcbfc1a60709cb80b5ca24539008456531d0 Edward Thomson <ethomson@edwardthomson.com> 1355182111 -0600 checkout: moving from df_side2 to df_side1
a7dbfcbfc1a60709cb80b5ca24539008456531d0 fc90237dc4891fa6c69827fc465632225e391618 Edward Thomson <ethomson@edwardthomson.com> 1355182115 -0600 checkout: moving from df_side1 to df_side2
fc90237dc4891fa6c69827fc465632225e391618 bd593285fc7fe4ca18ccdbabf027f5d689101452 Edward Thomson <ethomson@edwardthomson.com> 1355182122 -0600 checkout: moving from df_side2 to master
bd593285fc7fe4ca18ccdbabf027f5d689101452 d6cf6c7741b3316826af1314042550c97ded1d50 Edward Thomson <ethomson@edwardthomson.com> 1358997543 -0600 checkout: moving from master to unrelated
d6cf6c7741b3316826af1314042550c97ded1d50 55b4e4687e7a0d9ca367016ed930f385d4022e6f Edward Thomson <ethomson@edwardthomson.com> 1358997664 -0600 commit: conflicting changes
55b4e4687e7a0d9ca367016ed930f385d4022e6f bd593285fc7fe4ca18ccdbabf027f5d689101452 Edward Thomson <ethomson@edwardthomson.com> 1358997675 -0600 checkout: moving from unrelated to master
bd593285fc7fe4ca18ccdbabf027f5d689101452 88e185910a15cd13bdf44854ad037f4842b03b29 Edward Thomson <ethomson@microsoft.com> 1365714471 -0500 checkout: moving from master to rename_conflict_ours
88e185910a15cd13bdf44854ad037f4842b03b29 bef6e37b3ee632ba74159168836f382fed21d77d Edward Thomson <ethomson@microsoft.com> 1365714516 -0500 checkout: moving from rename_conflict_ours to bef6e37b3ee632ba74159168836f382fed21d77d
bef6e37b3ee632ba74159168836f382fed21d77d 01f149e1b8f84bd8896aaff6d6b22af88459ded0 Edward Thomson <ethomson@microsoft.com> 1365714831 -0500 commit: rename ancestor
0000000000000000000000000000000000000000 2392a2dacc9efb562b8635d6579fb458751c7c5b Edward Thomson <ethomson@microsoft.com> 1365714958 -0500 commit (initial): rename conflict ancestor
2392a2dacc9efb562b8635d6579fb458751c7c5b 88e185910a15cd13bdf44854ad037f4842b03b29 Edward Thomson <ethomson@microsoft.com> 1365714980 -0500 checkout: moving from rename_conflict_ancestor to rename_conflict_ours
88e185910a15cd13bdf44854ad037f4842b03b29 7c2c5228c9e90170d4a35e6558e47163daf092e5 Edward Thomson <ethomson@microsoft.com> 1365715250 -0500 commit: rename conflict ours
7c2c5228c9e90170d4a35e6558e47163daf092e5 2f4024ce528d36d8670c289cce5a7963e625bb0c Edward Thomson <ethomson@microsoft.com> 1365715274 -0500 checkout: moving from rename_conflict_ours to rename_conflict_theirs
2f4024ce528d36d8670c289cce5a7963e625bb0c 56a638b76b75e068590ac999c2f8621e7f3e264c Edward Thomson <ethomson@microsoft.com> 1365715362 -0500 commit: rename conflict theirs
56a638b76b75e068590ac999c2f8621e7f3e264c 2392a2dacc9efb562b8635d6579fb458751c7c5b Edward Thomson <ethomson@microsoft.com> 1365715368 -0500 checkout: moving from rename_conflict_theirs to rename_conflict_ancestor
2392a2dacc9efb562b8635d6579fb458751c7c5b 56a638b76b75e068590ac999c2f8621e7f3e264c Edward Thomson <ethomson@microsoft.com> 1365715371 -0500 checkout: moving from rename_conflict_ancestor to rename_conflict_theirs
56a638b76b75e068590ac999c2f8621e7f3e264c 2392a2dacc9efb562b8635d6579fb458751c7c5b Edward Thomson <ethomson@microsoft.com> 1365715404 -0500 checkout: moving from rename_conflict_theirs to rename_conflict_ancestor
2392a2dacc9efb562b8635d6579fb458751c7c5b 2392a2dacc9efb562b8635d6579fb458751c7c5b Edward Thomson <ethomson@microsoft.com> 1365715438 -0500 checkout: moving from rename_conflict_ancestor to rename_conflict_ours
2392a2dacc9efb562b8635d6579fb458751c7c5b 2392a2dacc9efb562b8635d6579fb458751c7c5b Edward Thomson <ethomson@microsoft.com> 1365715480 -0500 checkout: moving from rename_conflict_ours to rename_conflict_ancestor
2392a2dacc9efb562b8635d6579fb458751c7c5b 2392a2dacc9efb562b8635d6579fb458751c7c5b Edward Thomson <ethomson@microsoft.com> 1365715486 -0500 checkout: moving from rename_conflict_ancestor to rename_conflict_ours
2392a2dacc9efb562b8635d6579fb458751c7c5b f3293571dcd708b6a3faf03818cd2844d000e198 Edward Thomson <ethomson@microsoft.com> 1365715538 -0500 commit: rename conflict ours
f3293571dcd708b6a3faf03818cd2844d000e198 2392a2dacc9efb562b8635d6579fb458751c7c5b Edward Thomson <ethomson@microsoft.com> 1365715546 -0500 checkout: moving from rename_conflict_ours to rename_conflict_ancestor
2392a2dacc9efb562b8635d6579fb458751c7c5b 2392a2dacc9efb562b8635d6579fb458751c7c5b Edward Thomson <ethomson@microsoft.com> 1365715550 -0500 checkout: moving from rename_conflict_ancestor to rename_conflict_thiers
2392a2dacc9efb562b8635d6579fb458751c7c5b 2392a2dacc9efb562b8635d6579fb458751c7c5b Edward Thomson <ethomson@microsoft.com> 1365715554 -0500 checkout: moving from rename_conflict_thiers to rename_conflict_ancestor
2392a2dacc9efb562b8635d6579fb458751c7c5b 2392a2dacc9efb562b8635d6579fb458751c7c5b Edward Thomson <ethomson@microsoft.com> 1365715557 -0500 checkout: moving from rename_conflict_ancestor to rename_conflict_theirs
2392a2dacc9efb562b8635d6579fb458751c7c5b a802e06f1782a9645b9851bc7202cee74a8a4972 Edward Thomson <ethomson@microsoft.com> 1365715572 -0500 commit: rename conflict theirs
a802e06f1782a9645b9851bc7202cee74a8a4972 bd593285fc7fe4ca18ccdbabf027f5d689101452 Edward Thomson <ethomson@microsoft.com> 1365715620 -0500 checkout: moving from rename_conflict_theirs to master
0000000000000000000000000000000000000000 c607fc30883e335def28cd686b51f6cfa02b06ec Edward Thomson <ethomson@edwardthomson.com> 1351563886 -0500 branch: Created from HEAD
c607fc30883e335def28cd686b51f6cfa02b06ec 7cb63eed597130ba4abb87b3e544b85021905520 Edward Thomson <ethomson@edwardthomson.com> 1351563965 -0500 commit: branch
0000000000000000000000000000000000000000 bd593285fc7fe4ca18ccdbabf027f5d689101452 Edward Thomson <ethomson@edwardthomson.com> 1355181639 -0600 branch: Created from HEAD
bd593285fc7fe4ca18ccdbabf027f5d689101452 2da538570bc1e5b2c3e855bf702f35248ad0735f Edward Thomson <ethomson@edwardthomson.com> 1355181673 -0600 commit: df_ancestor
2da538570bc1e5b2c3e855bf702f35248ad0735f a7dbfcbfc1a60709cb80b5ca24539008456531d0 Edward Thomson <ethomson@edwardthomson.com> 1355181715 -0600 commit: df_side1
a7dbfcbfc1a60709cb80b5ca24539008456531d0 9a301fbe6fada7dcb74fcd7c20269b5c743459a7 Edward Thomson <ethomson@edwardthomson.com> 1355181775 -0600 commit: df_side2
9a301fbe6fada7dcb74fcd7c20269b5c743459a7 2da538570bc1e5b2c3e855bf702f35248ad0735f Edward Thomson <ethomson@edwardthomson.com> 1355182067 -0600 reset: moving to 2da538570bc1e5b2c3e855bf702f35248ad0735f
0000000000000000000000000000000000000000 bd593285fc7fe4ca18ccdbabf027f5d689101452 Edward Thomson <ethomson@edwardthomson.com> 1354574697 -0600 branch: Created from HEAD
bd593285fc7fe4ca18ccdbabf027f5d689101452 d4207f77243500bec335ab477f9227fcdb1e271a Edward Thomson <ethomson@edwardthomson.com> 1354574962 -0600 commit: df_ancestor
d4207f77243500bec335ab477f9227fcdb1e271a c94b27e41064c521120627e07e2035cca1d24ffa Edward Thomson <ethomson@edwardthomson.com> 1354575027 -0600 commit: df_side1
c94b27e41064c521120627e07e2035cca1d24ffa a90bc3fb6f15181972a2959a921429efbd81a473 Edward Thomson <ethomson@edwardthomson.com> 1355017650 -0600 commit: df_added
a90bc3fb6f15181972a2959a921429efbd81a473 005b6fcc8fec71d2550bef8462d169b3c26aa14b Edward Thomson <ethomson@edwardthomson.com> 1355017676 -0600 rebase -i (finish): refs/heads/df_side1 onto c94b27e
005b6fcc8fec71d2550bef8462d169b3c26aa14b f8958bdf4d365a84a9a178b1f5f35ff1dacbd884 Edward Thomson <ethomson@edwardthomson.com> 1355017715 -0600 reset: moving to df_side2
f8958bdf4d365a84a9a178b1f5f35ff1dacbd884 8c749d9968d4b10dcfb06c9f97d0e5d92d337071 Edward Thomson <ethomson@edwardthomson.com> 1355017744 -0600 commit: df_added
8c749d9968d4b10dcfb06c9f97d0e5d92d337071 0204a84f822acbf6386b36d33f1f6bc68bbbf858 Edward Thomson <ethomson@edwardthomson.com> 1355017756 -0600 rebase -i (finish): refs/heads/df_side1 onto f8958bd
0204a84f822acbf6386b36d33f1f6bc68bbbf858 005b6fcc8fec71d2550bef8462d169b3c26aa14b Edward Thomson <ethomson@edwardthomson.com> 1355017793 -0600 reset: moving to 005b6fcc8fec71d2550bef8462d169b3c26aa14b
005b6fcc8fec71d2550bef8462d169b3c26aa14b 0204a84f822acbf6386b36d33f1f6bc68bbbf858 Edward Thomson <ethomson@edwardthomson.com> 1355017826 -0600 reset: moving to 0204a84
005b6fcc8fec71d2550bef8462d169b3c26aa14b e8107f24196736b870a318a0e28f048e29f6feff Edward Thomson <ethomson@edwardthomson.com> 1355169065 -0600 commit: df_side1
e8107f24196736b870a318a0e28f048e29f6feff 80a8fbb3abb1ba423d554e9630b8fc2e5698f86b Edward Thomson <ethomson@edwardthomson.com> 1355169084 -0600 rebase -i (finish): refs/heads/df_side1 onto 005b6fc
80a8fbb3abb1ba423d554e9630b8fc2e5698f86b e65a9bb2af9f4c2d1c375dd0f8f8a46cf9c68812 Edward Thomson <ethomson@edwardthomson.com> 1355169419 -0600 commit: side1
e65a9bb2af9f4c2d1c375dd0f8f8a46cf9c68812 5dc1018e90b19654bee986b7a0c268804d39659d Edward Thomson <ethomson@edwardthomson.com> 1355169435 -0600 rebase -i (finish): refs/heads/df_side1 onto 80a8fbb
0000000000000000000000000000000000000000 d4207f77243500bec335ab477f9227fcdb1e271a Edward Thomson <ethomson@edwardthomson.com> 1354575051 -0600 branch: Created from d4207f77243500bec335ab477f9227fcdb1e271a
d4207f77243500bec335ab477f9227fcdb1e271a f8958bdf4d365a84a9a178b1f5f35ff1dacbd884 Edward Thomson <ethomson@edwardthomson.com> 1354575206 -0600 commit: df_side2
0204a84f822acbf6386b36d33f1f6bc68bbbf858 944f5dd1a867cab4c2bbcb896493435cae1dcc1a Edward Thomson <ethomson@edwardthomson.com> 1355169174 -0600 commit: both
944f5dd1a867cab4c2bbcb896493435cae1dcc1a 57079a46233ae2b6df62e9ade71c4948512abefb Edward Thomson <ethomson@edwardthomson.com> 1355169185 -0600 rebase -i (finish): refs/heads/df_side2 onto 0204a84
57079a46233ae2b6df62e9ade71c4948512abefb 58e853f66699fd02629fd50bde08082bc005933a Edward Thomson <ethomson@edwardthomson.com> 1355169460 -0600 commit: side2
58e853f66699fd02629fd50bde08082bc005933a fada9356aa3f74622327a3038ae9c6f92e1c5c1d Edward Thomson <ethomson@edwardthomson.com> 1355169471 -0600 rebase -i (finish): refs/heads/df_side2 onto 57079a4
fada9356aa3f74622327a3038ae9c6f92e1c5c1d 95646149ab6b6ba6edc83cff678582538b457b2b Edward Thomson <ethomson@edwardthomson.com> 1355169897 -0600 rebase finished: refs/heads/df_side2 onto a765fb87eb2f7a1920b73b2d5a057f8f8476a42b
0000000000000000000000000000000000000000 2da538570bc1e5b2c3e855bf702f35248ad0735f Edward Thomson <ethomson@edwardthomson.com> 1355182087 -0600 branch: Created from HEAD
2da538570bc1e5b2c3e855bf702f35248ad0735f fc90237dc4891fa6c69827fc465632225e391618 Edward Thomson <ethomson@edwardthomson.com> 1355182104 -0600 commit: df_side2
0000000000000000000000000000000000000000 977c696519c5a3004c5f1d15d60c89dbeb8f235f Edward Thomson <ethomson@edwardthomson.com> 1351605785 -0500 branch: Created from HEAD
977c696519c5a3004c5f1d15d60c89dbeb8f235f 33d500f588fbbe65901d82b4e6b008e549064be0 Edward Thomson <ethomson@edwardthomson.com> 1351605830 -0500 commit: fastforward
33d500f588fbbe65901d82b4e6b008e549064be0 c607fc30883e335def28cd686b51f6cfa02b06ec Edward Thomson <ethomson@edwardthomson.com> 1351990202 -0500 reset: moving to c607fc30883e335def28cd686b51f6cfa02b06ec
c607fc30883e335def28cd686b51f6cfa02b06ec bd593285fc7fe4ca18ccdbabf027f5d689101452 Edward Thomson <ethomson@edwardthomson.com> 1351990205 -0500 merge master: Fast-forward
bd593285fc7fe4ca18ccdbabf027f5d689101452 fd89f8cffb663ac89095a0f9764902e93ceaca6a Edward Thomson <ethomson@edwardthomson.com> 1351990229 -0500 commit: fastforward
0000000000000000000000000000000000000000 c607fc30883e335def28cd686b51f6cfa02b06ec Edward Thomson <ethomson@edwardthomson.com> 1351563869 -0500 commit (initial): initial
c607fc30883e335def28cd686b51f6cfa02b06ec 977c696519c5a3004c5f1d15d60c89dbeb8f235f Edward Thomson <ethomson@edwardthomson.com> 1351564033 -0500 commit: master
977c696519c5a3004c5f1d15d60c89dbeb8f235f 4e0d9401aee78eb345a8685a859d37c8c3c0bbed Edward Thomson <ethomson@edwardthomson.com> 1351875091 -0500 merge octo1 octo2 octo3 octo4: Merge made by the 'octopus' strategy.
4e0d9401aee78eb345a8685a859d37c8c3c0bbed 54269b3f6ec3d7d4ede24dd350dd5d605495c3ae Edward Thomson <ethomson@edwardthomson.com> 1351875108 -0500 reset: moving to 54269b3f6ec3d7d4ede24dd350dd5d605495c3ae
54269b3f6ec3d7d4ede24dd350dd5d605495c3ae 977c696519c5a3004c5f1d15d60c89dbeb8f235f Edward Thomson <ethomson@edwardthomson.com> 1351875584 -0500 reset: moving to 977c696519c5a3004c5f1d15d60c89dbeb8f235f
0000000000000000000000000000000000000000 977c696519c5a3004c5f1d15d60c89dbeb8f235f Edward Thomson <ethomson@edwardthomson.com> 1351874933 -0500 branch: Created from HEAD
977c696519c5a3004c5f1d15d60c89dbeb8f235f 16f825815cfd20a07a75c71554e82d8eede0b061 Edward Thomson <ethomson@edwardthomson.com> 1351874954 -0500 commit: octo1
0000000000000000000000000000000000000000 977c696519c5a3004c5f1d15d60c89dbeb8f235f Edward Thomson <ethomson@edwardthomson.com> 1351874960 -0500 branch: Created from HEAD
977c696519c5a3004c5f1d15d60c89dbeb8f235f 158dc7bedb202f5b26502bf3574faa7f4238d56c Edward Thomson <ethomson@edwardthomson.com> 1351874974 -0500 commit: octo2
0000000000000000000000000000000000000000 977c696519c5a3004c5f1d15d60c89dbeb8f235f Edward Thomson <ethomson@edwardthomson.com> 1351874980 -0500 branch: Created from HEAD
977c696519c5a3004c5f1d15d60c89dbeb8f235f 50ce7d7d01217679e26c55939eef119e0c93e272 Edward Thomson <ethomson@edwardthomson.com> 1351874998 -0500 commit: octo3
0000000000000000000000000000000000000000 977c696519c5a3004c5f1d15d60c89dbeb8f235f Edward Thomson <ethomson@edwardthomson.com> 1351875010 -0500 branch: Created from HEAD
977c696519c5a3004c5f1d15d60c89dbeb8f235f 54269b3f6ec3d7d4ede24dd350dd5d605495c3ae Edward Thomson <ethomson@edwardthomson.com> 1351875023 -0500 commit: octo4
0000000000000000000000000000000000000000 977c696519c5a3004c5f1d15d60c89dbeb8f235f Edward Thomson <ethomson@edwardthomson.com> 1351875031 -0500 branch: Created from HEAD
977c696519c5a3004c5f1d15d60c89dbeb8f235f e4f618a2c3ed0669308735727df5ebf2447f022f Edward Thomson <ethomson@edwardthomson.com> 1351875041 -0500 commit: octo5
0000000000000000000000000000000000000000 977c696519c5a3004c5f1d15d60c89dbeb8f235f Edward Thomson <ethomson@edwardthomson.com> 1351875046 -0500 branch: Created from HEAD
977c696519c5a3004c5f1d15d60c89dbeb8f235f 4ca408a8c88655f7586a1b580be6fad138121e98 Edward Thomson <ethomson@edwardthomson.com> 1351875057 -0500 commit: octo5
4ca408a8c88655f7586a1b580be6fad138121e98 b6f610aef53bd343e6c96227de874c66f00ee8e8 Edward Thomson <ethomson@edwardthomson.com> 1351875065 -0500 commit (amend): octo6
0000000000000000000000000000000000000000 c607fc30883e335def28cd686b51f6cfa02b06ec Edward Thomson <ethomson@edwardthomson.com> 1353177745 -0600 branch: Created from c607fc30883e335def28cd686b51f6cfa02b06ec
c607fc30883e335def28cd686b51f6cfa02b06ec 412b32fb66137366147f1801ecc962452757d48a Edward Thomson <ethomson@edwardthomson.com> 1353177886 -0600 commit: renames
0000000000000000000000000000000000000000 bd593285fc7fe4ca18ccdbabf027f5d689101452 Edward Thomson <ethomson@edwardthomson.com> 1353794647 -0600 branch: Created from HEAD
bd593285fc7fe4ca18ccdbabf027f5d689101452 c607fc30883e335def28cd686b51f6cfa02b06ec Edward Thomson <ethomson@edwardthomson.com> 1353794677 -0600 reset: moving to c607fc30883e335def28cd686b51f6cfa02b06ec
c607fc30883e335def28cd686b51f6cfa02b06ec ab40af3cb8a3ed2e2843e96d9aa7871336b94573 Edward Thomson <ethomson@edwardthomson.com> 1353794852 -0600 commit: renames2
0000000000000000000000000000000000000000 c607fc30883e335def28cd686b51f6cfa02b06ec Edward Thomson <ethomson@edwardthomson.com> 1352100171 -0600 branch: Created from c607fc30883e335def28cd686b51f6cfa02b06ec
c607fc30883e335def28cd686b51f6cfa02b06ec 53825f41ac8d640612f9423a2f03a69f3d96809a Edward Thomson <ethomson@edwardthomson.com> 1352100193 -0600 commit: trivial-10
53825f41ac8d640612f9423a2f03a69f3d96809a 0ec5f433959cd46177f745903353efb5be08d151 Edward Thomson <ethomson@edwardthomson.com> 1352100223 -0600 commit: trivial-10
0000000000000000000000000000000000000000 53825f41ac8d640612f9423a2f03a69f3d96809a Edward Thomson <ethomson@edwardthomson.com> 1352100200 -0600 branch: Created from HEAD
53825f41ac8d640612f9423a2f03a69f3d96809a 11f4f3c08b737f5fd896cbefa1425ee63b21b2fa Edward Thomson <ethomson@edwardthomson.com> 1352100211 -0600 commit: trivial-10-branch
0000000000000000000000000000000000000000 c607fc30883e335def28cd686b51f6cfa02b06ec Edward Thomson <ethomson@edwardthomson.com> 1352100930 -0600 branch: Created from c607fc30883e335def28cd686b51f6cfa02b06ec
c607fc30883e335def28cd686b51f6cfa02b06ec 35632e43612c06a3ea924bfbacd48333da874c29 Edward Thomson <ethomson@edwardthomson.com> 1352100958 -0600 commit: trivial-11
35632e43612c06a3ea924bfbacd48333da874c29 3168dca1a561889b045a6441909f4c56145e666d Edward Thomson <ethomson@edwardthomson.com> 1352100992 -0600 commit: trivial-11
0000000000000000000000000000000000000000 35632e43612c06a3ea924bfbacd48333da874c29 Edward Thomson <ethomson@edwardthomson.com> 1352100964 -0600 branch: Created from HEAD
35632e43612c06a3ea924bfbacd48333da874c29 6718a45909532d1fcf5600d0877f7fe7e78f0b86 Edward Thomson <ethomson@edwardthomson.com> 1352100978 -0600 commit: trivial-11-branch
0000000000000000000000000000000000000000 c607fc30883e335def28cd686b51f6cfa02b06ec Edward Thomson <ethomson@edwardthomson.com> 1352100559 -0600 branch: Created from c607fc30883e335def28cd686b51f6cfa02b06ec
c607fc30883e335def28cd686b51f6cfa02b06ec 8f4433f8593ddd65b7dd43dd4564d841f4d9c8aa Edward Thomson <ethomson@edwardthomson.com> 1352100589 -0600 commit: trivial-13
8f4433f8593ddd65b7dd43dd4564d841f4d9c8aa a3fabece9eb8748da810e1e08266fef9b7136ad4 Edward Thomson <ethomson@edwardthomson.com> 1352100625 -0600 commit: trivial-13
0000000000000000000000000000000000000000 8f4433f8593ddd65b7dd43dd4564d841f4d9c8aa Edward Thomson <ethomson@edwardthomson.com> 1352100604 -0600 branch: Created from HEAD
8f4433f8593ddd65b7dd43dd4564d841f4d9c8aa 05f3c1a2a56ca95c3d2ef28dc9ddf32b5cd6c91c Edward Thomson <ethomson@edwardthomson.com> 1352100610 -0600 commit: trivial-13-branch
0000000000000000000000000000000000000000 c607fc30883e335def28cd686b51f6cfa02b06ec Edward Thomson <ethomson@edwardthomson.com> 1352101083 -0600 branch: Created from c607fc30883e335def28cd686b51f6cfa02b06ec
c607fc30883e335def28cd686b51f6cfa02b06ec 596803b523203a4851c824c07366906f8353f4ad Edward Thomson <ethomson@edwardthomson.com> 1352101113 -0600 commit: trivial-14
596803b523203a4851c824c07366906f8353f4ad 7e2d058d5fedf8329db44db4fac610d6b1a89159 Edward Thomson <ethomson@edwardthomson.com> 1352101141 -0600 commit: trivial-14
0000000000000000000000000000000000000000 596803b523203a4851c824c07366906f8353f4ad Edward Thomson <ethomson@edwardthomson.com> 1352101117 -0600 branch: Created from HEAD
596803b523203a4851c824c07366906f8353f4ad 8187117062b750eed4f93fd7e899f17b52ce554d Edward Thomson <ethomson@edwardthomson.com> 1352101132 -0600 commit: trivial-14-branch
0000000000000000000000000000000000000000 c607fc30883e335def28cd686b51f6cfa02b06ec Edward Thomson <ethomson@edwardthomson.com> 1352091695 -0600 branch: Created from c607fc30883e335def28cd686b51f6cfa02b06ec
c607fc30883e335def28cd686b51f6cfa02b06ec 566ab53c220a2eafc1212af1a024513230280ab9 Edward Thomson <ethomson@edwardthomson.com> 1352092452 -0600 commit: 2alt
0000000000000000000000000000000000000000 c607fc30883e335def28cd686b51f6cfa02b06ec Edward Thomson <ethomson@edwardthomson.com> 1352092411 -0600 branch: Created from HEAD
c607fc30883e335def28cd686b51f6cfa02b06ec c9174cef549ec94ecbc43ef03cdc775b4950becb Edward Thomson <ethomson@edwardthomson.com> 1352092434 -0600 commit: 2alt-branch
566ab53c220a2eafc1212af1a024513230280ab9 c607fc30883e335def28cd686b51f6cfa02b06ec Edward Thomson <ethomson@edwardthomson.com> 1352094547 -0600 reset: moving to c607fc30883e335def28cd686b51f6cfa02b06ec
c607fc30883e335def28cd686b51f6cfa02b06ec 5459c89aa0026d543ce8343bd89871bce543f9c2 Edward Thomson <ethomson@edwardthomson.com> 1352094580 -0600 commit: 3alt
5459c89aa0026d543ce8343bd89871bce543f9c2 4c9fac0707f8d4195037ae5a681aa48626491541 Edward Thomson <ethomson@edwardthomson.com> 1352094610 -0600 commit: 3alt-branch
0000000000000000000000000000000000000000 c607fc30883e335def28cd686b51f6cfa02b06ec Edward Thomson <ethomson@edwardthomson.com> 1352094594 -0600 branch: Created from c607fc30883e335def28cd686b51f6cfa02b06ec
566ab53c220a2eafc1212af1a024513230280ab9 c607fc30883e335def28cd686b51f6cfa02b06ec Edward Thomson <ethomson@edwardthomson.com> 1352094764 -0600 reset: moving to c607fc30883e335def28cd686b51f6cfa02b06ec
c607fc30883e335def28cd686b51f6cfa02b06ec cc3e3009134cb88014129fc8858d1101359e5e2f Edward Thomson <ethomson@edwardthomson.com> 1352094815 -0600 commit: trivial-4
0000000000000000000000000000000000000000 c607fc30883e335def28cd686b51f6cfa02b06ec Edward Thomson <ethomson@edwardthomson.com> 1352094830 -0600 branch: Created from c607fc30883e335def28cd686b51f6cfa02b06ec
c607fc30883e335def28cd686b51f6cfa02b06ec 183310e30fb1499af8c619108ffea4d300b5e778 Edward Thomson <ethomson@edwardthomson.com> 1352094856 -0600 commit: trivial-4-branch
0000000000000000000000000000000000000000 c607fc30883e335def28cd686b51f6cfa02b06ec Edward Thomson <ethomson@edwardthomson.com> 1352096606 -0600 branch: Created from c607fc30883e335def28cd686b51f6cfa02b06ec
c607fc30883e335def28cd686b51f6cfa02b06ec 4fe93c0ec83eb6305cbace3dace88ecee1b63cb6 Edward Thomson <ethomson@edwardthomson.com> 1352096643 -0600 commit: 5alt-1
0000000000000000000000000000000000000000 c607fc30883e335def28cd686b51f6cfa02b06ec Edward Thomson <ethomson@edwardthomson.com> 1352096657 -0600 branch: Created from c607fc30883e335def28cd686b51f6cfa02b06ec
c607fc30883e335def28cd686b51f6cfa02b06ec 478172cb2f5ff9b514bc9d04d3bd5ef5840cb3b2 Edward Thomson <ethomson@edwardthomson.com> 1352096689 -0600 commit: 5alt-1-branch
0000000000000000000000000000000000000000 c607fc30883e335def28cd686b51f6cfa02b06ec Edward Thomson <ethomson@edwardthomson.com> 1352096711 -0600 branch: Created from c607fc30883e335def28cd686b51f6cfa02b06ec
c607fc30883e335def28cd686b51f6cfa02b06ec ebc09d0137cfb0c26697aed0109fb943ad906f3f Edward Thomson <ethomson@edwardthomson.com> 1352096764 -0600 commit: existing file
ebc09d0137cfb0c26697aed0109fb943ad906f3f 3b47b031b3e55ae11e14a05260b1c3ffd6838d55 Edward Thomson <ethomson@edwardthomson.com> 1352096815 -0600 commit: 5alt-2
0000000000000000000000000000000000000000 ebc09d0137cfb0c26697aed0109fb943ad906f3f Edward Thomson <ethomson@edwardthomson.com> 1352096833 -0600 branch: Created from ebc09d0
ebc09d0137cfb0c26697aed0109fb943ad906f3f f48097eb340dc5a7cae55aabcf1faf4548aa821f Edward Thomson <ethomson@edwardthomson.com> 1352096855 -0600 commit: 5alt-2-branch
0000000000000000000000000000000000000000 c607fc30883e335def28cd686b51f6cfa02b06ec Edward Thomson <ethomson@edwardthomson.com> 1352097371 -0600 branch: Created from c607fc30883e335def28cd686b51f6cfa02b06ec
c607fc30883e335def28cd686b51f6cfa02b06ec f7c332bd4d4d4b777366cae4d24d1687477576bf Edward Thomson <ethomson@edwardthomson.com> 1352097389 -0600 commit: 6
f7c332bd4d4d4b777366cae4d24d1687477576bf 99b4f7e4f24470fa06b980bc21f1095c2a9425c0 Edward Thomson <ethomson@edwardthomson.com> 1352097404 -0600 commit: trivial-6
0000000000000000000000000000000000000000 f7c332bd4d4d4b777366cae4d24d1687477576bf Edward Thomson <ethomson@edwardthomson.com> 1352097414 -0600 branch: Created from f7c332bd4d4d4b777366cae4d24d1687477576bf
f7c332bd4d4d4b777366cae4d24d1687477576bf a43150a738849c59376cf30bb2a68348a83c8f48 Edward Thomson <ethomson@edwardthomson.com> 1352097431 -0600 commit: 6-branch
0000000000000000000000000000000000000000 c607fc30883e335def28cd686b51f6cfa02b06ec Edward Thomson <ethomson@edwardthomson.com> 1352099765 -0600 branch: Created from c607fc30883e335def28cd686b51f6cfa02b06ec
c607fc30883e335def28cd686b51f6cfa02b06ec 092ce8682d7f3a2a3a769a6daca58950168ba5c4 Edward Thomson <ethomson@edwardthomson.com> 1352099790 -0600 commit: trivial-7
092ce8682d7f3a2a3a769a6daca58950168ba5c4 d874671ef5b20184836cb983bb273e5280384d0b Edward Thomson <ethomson@edwardthomson.com> 1352099947 -0600 commit: trivial-7
0000000000000000000000000000000000000000 092ce8682d7f3a2a3a769a6daca58950168ba5c4 Edward Thomson <ethomson@edwardthomson.com> 1352099799 -0600 branch: Created from HEAD
092ce8682d7f3a2a3a769a6daca58950168ba5c4 73cbfdc4fe843169e5b2af8dcad03cbf3acf306c Edward Thomson <ethomson@edwardthomson.com> 1352099812 -0600 commit: trivial-7-branch
73cbfdc4fe843169e5b2af8dcad03cbf3acf306c 092ce8682d7f3a2a3a769a6daca58950168ba5c4 Edward Thomson <ethomson@edwardthomson.com> 1352099874 -0600 reset: moving to 092ce8682d7f3a2a3a769a6daca58950168ba5c4
092ce8682d7f3a2a3a769a6daca58950168ba5c4 009b9cab6fdac02915a88ecd078b7a792ed802d8 Edward Thomson <ethomson@edwardthomson.com> 1352099921 -0600 commit: removed in 7
009b9cab6fdac02915a88ecd078b7a792ed802d8 5195a1b480f66691b667f10a9e41e70115a78351 Edward Thomson <ethomson@edwardthomson.com> 1352099927 -0600 commit (amend): trivial-7-branch
0000000000000000000000000000000000000000 c607fc30883e335def28cd686b51f6cfa02b06ec Edward Thomson <ethomson@edwardthomson.com> 1352098816 -0600 branch: Created from c607fc30883e335def28cd686b51f6cfa02b06ec
c607fc30883e335def28cd686b51f6cfa02b06ec 75a811bf6bc57694adb3fe604786f3a4efd1cd1b Edward Thomson <ethomson@edwardthomson.com> 1352098884 -0600 commit: trivial-8
75a811bf6bc57694adb3fe604786f3a4efd1cd1b 3575826c96a975031d2c14368529cc5c4353a8fd Edward Thomson <ethomson@edwardthomson.com> 1352099000 -0600 commit: trivial-8
0000000000000000000000000000000000000000 75a811bf6bc57694adb3fe604786f3a4efd1cd1b Edward Thomson <ethomson@edwardthomson.com> 1352098947 -0600 branch: Created from HEAD
75a811bf6bc57694adb3fe604786f3a4efd1cd1b 52d8bc572af2b6d4ee0d5e62ed5d1fbad92210a9 Edward Thomson <ethomson@edwardthomson.com> 1352098979 -0600 commit: trivial-8-branch
0000000000000000000000000000000000000000 c607fc30883e335def28cd686b51f6cfa02b06ec Edward Thomson <ethomson@edwardthomson.com> 1352100268 -0600 branch: Created from c607fc30883e335def28cd686b51f6cfa02b06ec
c607fc30883e335def28cd686b51f6cfa02b06ec f0053b8060bb3f0be5cbcc3147a07ece26bf097e Edward Thomson <ethomson@edwardthomson.com> 1352100304 -0600 commit: trivial-9
f0053b8060bb3f0be5cbcc3147a07ece26bf097e c35dee9bcc0e989f3b0c40f68372a9a51b6c4e6a Edward Thomson <ethomson@edwardthomson.com> 1352100333 -0600 commit: trivial-9
0000000000000000000000000000000000000000 f0053b8060bb3f0be5cbcc3147a07ece26bf097e Edward Thomson <ethomson@edwardthomson.com> 1352100310 -0600 branch: Created from HEAD
f0053b8060bb3f0be5cbcc3147a07ece26bf097e 13d1be4ea52a6ced1d7a1d832f0ee3c399348e5e Edward Thomson <ethomson@edwardthomson.com> 1352100317 -0600 commit: trivial-9-branch
d6cf6c7741b3316826af1314042550c97ded1d50 55b4e4687e7a0d9ca367016ed930f385d4022e6f Edward Thomson <ethomson@edwardthomson.com> 1358997664 -0600 commit: conflicting changes
this file is changed in master
this file is automergeable
this file is automergeable
this file is automergeable
this file is automergeable
this file is automergeable
this file is automergeable
this file is automergeable
this file is automergeable
this file is changed in master and branch
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