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;
......
......@@ -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);
}
......@@ -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> 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
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