Commit af2b969b by Vicent Marti

Merge pull request #2061 from ethomson/merge_improvements

Merge improvements
parents e49c98e6 0ef19fe1
...@@ -152,6 +152,12 @@ typedef enum { ...@@ -152,6 +152,12 @@ typedef enum {
/** Don't overwrite ignored files that exist in the checkout target */ /** Don't overwrite ignored files that exist in the checkout target */
GIT_CHECKOUT_DONT_OVERWRITE_IGNORED = (1u << 19), GIT_CHECKOUT_DONT_OVERWRITE_IGNORED = (1u << 19),
/** Write normal merge files for conflicts */
GIT_CHECKOUT_CONFLICT_STYLE_MERGE = (1u << 20),
/** Include common ancestor data in diff3 format files for conflicts */
GIT_CHECKOUT_CONFLICT_STYLE_DIFF3 = (1u << 21),
/** /**
* THE FOLLOWING OPTIONS ARE NOT YET IMPLEMENTED * THE FOLLOWING OPTIONS ARE NOT YET IMPLEMENTED
*/ */
...@@ -252,6 +258,7 @@ typedef struct git_checkout_opts { ...@@ -252,6 +258,7 @@ typedef struct git_checkout_opts {
const char *target_directory; /** alternative checkout path to workdir */ const char *target_directory; /** alternative checkout path to workdir */
const char *ancestor_label; /** the name of the common ancestor side of conflicts */
const char *our_label; /** the name of the "our" side of conflicts */ const char *our_label; /** the name of the "our" side of conflicts */
const char *their_label; /** the name of the "their" side of conflicts */ const char *their_label; /** the name of the "their" side of conflicts */
} git_checkout_opts; } git_checkout_opts;
......
...@@ -32,14 +32,21 @@ typedef enum { ...@@ -32,14 +32,21 @@ typedef enum {
} git_merge_tree_flag_t; } git_merge_tree_flag_t;
/** /**
* Automerge options for `git_merge_trees_opts`. * Merge file options for `git_merge_trees_opts`.
*/ */
typedef enum { typedef enum {
GIT_MERGE_AUTOMERGE_NORMAL = 0, /* Produce a conflict in a file when two similar regions are changed. */
GIT_MERGE_AUTOMERGE_NONE = 1, GIT_MERGE_FILE_FAVOR_NORMAL = 0,
GIT_MERGE_AUTOMERGE_FAVOR_OURS = 2,
GIT_MERGE_AUTOMERGE_FAVOR_THEIRS = 3, /* Produce a file containing the "ours" side of conflicting regions. */
} git_merge_automerge_flags; GIT_MERGE_FILE_FAVOR_OURS = 1,
/* Produce a file containing the "theirs" side of conflicting regions. */
GIT_MERGE_FILE_FAVOR_THEIRS = 2,
/* Produce a file blending the sides in a union of conflicting regions */
GIT_MERGE_FILE_FAVOR_UNION = 3,
} git_merge_file_favor_t;
typedef struct { typedef struct {
...@@ -58,7 +65,7 @@ typedef struct { ...@@ -58,7 +65,7 @@ typedef struct {
git_diff_similarity_metric *metric; git_diff_similarity_metric *metric;
/** Flags for automerging content. */ /** Flags for automerging content. */
git_merge_automerge_flags automerge_flags; git_merge_file_favor_t file_favor;
} git_merge_tree_opts; } git_merge_tree_opts;
#define GIT_MERGE_TREE_OPTS_VERSION 1 #define GIT_MERGE_TREE_OPTS_VERSION 1
......
...@@ -70,7 +70,9 @@ typedef struct { ...@@ -70,7 +70,9 @@ typedef struct {
int name_collision:1, int name_collision:1,
directoryfile:1, directoryfile:1,
one_to_two:1; one_to_two:1,
binary:1,
submodule:1;
} checkout_conflictdata; } checkout_conflictdata;
static int checkout_notify( static int checkout_notify(
...@@ -681,6 +683,51 @@ GIT_INLINE(bool) conflict_pathspec_match( ...@@ -681,6 +683,51 @@ GIT_INLINE(bool) conflict_pathspec_match(
return false; return false;
} }
GIT_INLINE(int) checkout_conflict_detect_submodule(checkout_conflictdata *conflict)
{
conflict->submodule = ((conflict->ancestor && S_ISGITLINK(conflict->ancestor->mode)) ||
(conflict->ours && S_ISGITLINK(conflict->ours->mode)) ||
(conflict->theirs && S_ISGITLINK(conflict->theirs->mode)));
return 0;
}
GIT_INLINE(int) checkout_conflict_detect_binary(git_repository *repo, checkout_conflictdata *conflict)
{
git_blob *ancestor_blob = NULL, *our_blob = NULL, *their_blob = NULL;
int error = 0;
if (conflict->submodule)
return 0;
if (conflict->ancestor) {
if ((error = git_blob_lookup(&ancestor_blob, repo, &conflict->ancestor->oid)) < 0)
goto done;
conflict->binary = git_blob_is_binary(ancestor_blob);
}
if (!conflict->binary && conflict->ours) {
if ((error = git_blob_lookup(&our_blob, repo, &conflict->ours->oid)) < 0)
goto done;
conflict->binary = git_blob_is_binary(our_blob);
}
if (!conflict->binary && conflict->theirs) {
if ((error = git_blob_lookup(&their_blob, repo, &conflict->theirs->oid)) < 0)
goto done;
conflict->binary = git_blob_is_binary(their_blob);
}
done:
git_blob_free(ancestor_blob);
git_blob_free(our_blob);
git_blob_free(their_blob);
return error;
}
static int checkout_conflicts_load(checkout_data *data, git_iterator *workdir, git_vector *pathspec) static int checkout_conflicts_load(checkout_data *data, git_iterator *workdir, git_vector *pathspec)
{ {
git_index_conflict_iterator *iterator = NULL; git_index_conflict_iterator *iterator = NULL;
...@@ -705,6 +752,10 @@ static int checkout_conflicts_load(checkout_data *data, git_iterator *workdir, g ...@@ -705,6 +752,10 @@ static int checkout_conflicts_load(checkout_data *data, git_iterator *workdir, g
conflict->ours = ours; conflict->ours = ours;
conflict->theirs = theirs; conflict->theirs = theirs;
if ((error = checkout_conflict_detect_submodule(conflict)) < 0 ||
(error = checkout_conflict_detect_binary(data->repo, conflict)) < 0)
goto done;
git_vector_insert(&data->conflicts, conflict); git_vector_insert(&data->conflicts, conflict);
} }
...@@ -1626,6 +1677,7 @@ static int checkout_write_merge( ...@@ -1626,6 +1677,7 @@ static int checkout_write_merge(
{ {
git_buf our_label = GIT_BUF_INIT, their_label = GIT_BUF_INIT, git_buf our_label = GIT_BUF_INIT, their_label = GIT_BUF_INIT,
path_suffixed = GIT_BUF_INIT, path_workdir = GIT_BUF_INIT; path_suffixed = GIT_BUF_INIT, path_workdir = GIT_BUF_INIT;
git_merge_file_options merge_file_opts = GIT_MERGE_FILE_OPTIONS_INIT;
git_merge_file_input ancestor = GIT_MERGE_FILE_INPUT_INIT, git_merge_file_input ancestor = GIT_MERGE_FILE_INPUT_INIT,
ours = GIT_MERGE_FILE_INPUT_INIT, ours = GIT_MERGE_FILE_INPUT_INIT,
theirs = GIT_MERGE_FILE_INPUT_INIT; theirs = GIT_MERGE_FILE_INPUT_INIT;
...@@ -1633,6 +1685,9 @@ static int checkout_write_merge( ...@@ -1633,6 +1685,9 @@ static int checkout_write_merge(
git_filebuf output = GIT_FILEBUF_INIT; git_filebuf output = GIT_FILEBUF_INIT;
int error = 0; int error = 0;
if (data->opts.checkout_strategy & GIT_CHECKOUT_CONFLICT_STYLE_DIFF3)
merge_file_opts.style = GIT_MERGE_FILE_STYLE_DIFF3;
if ((conflict->ancestor && if ((conflict->ancestor &&
(error = git_merge_file_input_from_index_entry( (error = git_merge_file_input_from_index_entry(
&ancestor, data->repo, conflict->ancestor)) < 0) || &ancestor, data->repo, conflict->ancestor)) < 0) ||
...@@ -1642,7 +1697,7 @@ static int checkout_write_merge( ...@@ -1642,7 +1697,7 @@ static int checkout_write_merge(
&theirs, data->repo, conflict->theirs)) < 0) &theirs, data->repo, conflict->theirs)) < 0)
goto done; goto done;
ancestor.label = NULL; ancestor.label = data->opts.ancestor_label ? data->opts.ancestor_label : "ancestor";
ours.label = data->opts.our_label ? data->opts.our_label : "ours"; ours.label = data->opts.our_label ? data->opts.our_label : "ours";
theirs.label = data->opts.their_label ? data->opts.their_label : "theirs"; theirs.label = data->opts.their_label ? data->opts.their_label : "theirs";
...@@ -1662,7 +1717,7 @@ static int checkout_write_merge( ...@@ -1662,7 +1717,7 @@ static int checkout_write_merge(
theirs.label = git_buf_cstr(&their_label); theirs.label = git_buf_cstr(&their_label);
} }
if ((error = git_merge_files(&result, &ancestor, &ours, &theirs, 0)) < 0) if ((error = git_merge_files(&result, &ancestor, &ours, &theirs, &merge_file_opts)) < 0)
goto done; goto done;
if (result.path == NULL || result.mode == 0) { if (result.path == NULL || result.mode == 0) {
...@@ -1705,6 +1760,7 @@ static int checkout_create_conflicts(checkout_data *data) ...@@ -1705,6 +1760,7 @@ static int checkout_create_conflicts(checkout_data *data)
int error = 0; int error = 0;
git_vector_foreach(&data->conflicts, i, conflict) { git_vector_foreach(&data->conflicts, i, conflict) {
/* Both deleted: nothing to do */ /* Both deleted: nothing to do */
if (conflict->ours == NULL && conflict->theirs == NULL) if (conflict->ours == NULL && conflict->theirs == NULL)
error = 0; error = 0;
...@@ -1748,7 +1804,15 @@ static int checkout_create_conflicts(checkout_data *data) ...@@ -1748,7 +1804,15 @@ static int checkout_create_conflicts(checkout_data *data)
else if (S_ISLNK(conflict->theirs->mode)) else if (S_ISLNK(conflict->theirs->mode))
error = checkout_write_entry(data, conflict, conflict->ours); error = checkout_write_entry(data, conflict, conflict->ours);
else /* If any side is a gitlink, do nothing. */
else if (conflict->submodule)
error = 0;
/* If any side is binary, write the ours side */
else if (conflict->binary)
error = checkout_write_entry(data, conflict, conflict->ours);
else if (!error)
error = checkout_write_merge(data, conflict); error = checkout_write_merge(data, conflict);
if (error) if (error)
...@@ -1891,6 +1955,29 @@ static int checkout_data_init( ...@@ -1891,6 +1955,29 @@ static int checkout_data_init(
goto cleanup; goto cleanup;
} }
if ((data->opts.checkout_strategy &
(GIT_CHECKOUT_CONFLICT_STYLE_MERGE | GIT_CHECKOUT_CONFLICT_STYLE_DIFF3)) == 0) {
const char *conflict_style;
git_config *cfg = NULL;
if ((error = git_repository_config__weakptr(&cfg, repo)) < 0 ||
(error = git_config_get_string(&conflict_style, cfg, "merge.conflictstyle")) < 0 ||
error == GIT_ENOTFOUND)
;
else if (error)
goto cleanup;
else if (strcmp(conflict_style, "merge") == 0)
data->opts.checkout_strategy |= GIT_CHECKOUT_CONFLICT_STYLE_MERGE;
else if (strcmp(conflict_style, "diff3") == 0)
data->opts.checkout_strategy |= GIT_CHECKOUT_CONFLICT_STYLE_DIFF3;
else {
giterr_set(GITERR_CHECKOUT, "unknown style '%s' given for 'merge.conflictstyle'",
conflict_style);
error = -1;
goto cleanup;
}
}
if ((error = git_vector_init(&data->removes, 0, git__strcmp_cb)) < 0 || if ((error = git_vector_init(&data->removes, 0, git__strcmp_cb)) < 0 ||
(error = git_vector_init(&data->conflicts, 0, NULL)) < 0 || (error = git_vector_init(&data->conflicts, 0, NULL)) < 0 ||
(error = git_pool_init(&data->pool, 1, 0)) < 0 || (error = git_pool_init(&data->pool, 1, 0)) < 0 ||
......
...@@ -42,6 +42,7 @@ ...@@ -42,6 +42,7 @@
#include "git2/sys/index.h" #include "git2/sys/index.h"
#define GIT_MERGE_INDEX_ENTRY_EXISTS(X) ((X).mode != 0) #define GIT_MERGE_INDEX_ENTRY_EXISTS(X) ((X).mode != 0)
#define GIT_MERGE_INDEX_ENTRY_ISFILE(X) S_ISREG((X).mode)
typedef enum { typedef enum {
TREE_IDX_ANCESTOR = 0, TREE_IDX_ANCESTOR = 0,
...@@ -447,7 +448,6 @@ static int merge_conflict_resolve_one_removed( ...@@ -447,7 +448,6 @@ static int merge_conflict_resolve_one_removed(
return error; return error;
} }
static int merge_conflict_resolve_one_renamed( static int merge_conflict_resolve_one_renamed(
int *resolved, int *resolved,
git_merge_diff_list *diff_list, git_merge_diff_list *diff_list,
...@@ -511,8 +511,9 @@ static int merge_conflict_resolve_automerge( ...@@ -511,8 +511,9 @@ static int merge_conflict_resolve_automerge(
int *resolved, int *resolved,
git_merge_diff_list *diff_list, git_merge_diff_list *diff_list,
const git_merge_diff *conflict, const git_merge_diff *conflict,
unsigned int automerge_flags) unsigned int merge_file_favor)
{ {
git_merge_file_options merge_file_opts = GIT_MERGE_FILE_OPTIONS_INIT;
git_merge_file_input ancestor = GIT_MERGE_FILE_INPUT_INIT, git_merge_file_input ancestor = GIT_MERGE_FILE_INPUT_INIT,
ours = GIT_MERGE_FILE_INPUT_INIT, ours = GIT_MERGE_FILE_INPUT_INIT,
theirs = GIT_MERGE_FILE_INPUT_INIT; theirs = GIT_MERGE_FILE_INPUT_INIT;
...@@ -526,13 +527,18 @@ static int merge_conflict_resolve_automerge( ...@@ -526,13 +527,18 @@ static int merge_conflict_resolve_automerge(
*resolved = 0; *resolved = 0;
if (automerge_flags == GIT_MERGE_AUTOMERGE_NONE) merge_file_opts.favor = merge_file_favor;
return 0;
/* Reject D/F conflicts */ /* Reject D/F conflicts */
if (conflict->type == GIT_MERGE_DIFF_DIRECTORY_FILE) if (conflict->type == GIT_MERGE_DIFF_DIRECTORY_FILE)
return 0; return 0;
/* Reject submodules. */
if (S_ISGITLINK(conflict->ancestor_entry.mode) ||
S_ISGITLINK(conflict->our_entry.mode) ||
S_ISGITLINK(conflict->their_entry.mode))
return 0;
/* Reject link/file conflicts. */ /* Reject link/file conflicts. */
if ((S_ISLNK(conflict->ancestor_entry.mode) ^ S_ISLNK(conflict->our_entry.mode)) || 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))) (S_ISLNK(conflict->ancestor_entry.mode) ^ S_ISLNK(conflict->their_entry.mode)))
...@@ -548,11 +554,15 @@ static int merge_conflict_resolve_automerge( ...@@ -548,11 +554,15 @@ static int merge_conflict_resolve_automerge(
strcmp(conflict->ancestor_entry.path, conflict->their_entry.path) != 0) strcmp(conflict->ancestor_entry.path, conflict->their_entry.path) != 0)
return 0; return 0;
/* Reject binary conflicts */
if (conflict->binary)
return 0;
if ((error = git_repository_odb(&odb, diff_list->repo)) < 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(&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(&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_file_input_from_index_entry(&theirs, diff_list->repo, &conflict->their_entry)) < 0 ||
(error = git_merge_files(&result, &ancestor, &ours, &theirs, automerge_flags)) < 0 || (error = git_merge_files(&result, &ancestor, &ours, &theirs, &merge_file_opts)) < 0 ||
!result.automergeable || !result.automergeable ||
(error = git_odb_write(&automerge_oid, odb, result.data, result.len, GIT_OBJ_BLOB)) < 0) (error = git_odb_write(&automerge_oid, odb, result.data, result.len, GIT_OBJ_BLOB)) < 0)
goto done; goto done;
...@@ -586,7 +596,7 @@ static int merge_conflict_resolve( ...@@ -586,7 +596,7 @@ static int merge_conflict_resolve(
int *out, int *out,
git_merge_diff_list *diff_list, git_merge_diff_list *diff_list,
const git_merge_diff *conflict, const git_merge_diff *conflict,
unsigned int automerge_flags) unsigned int merge_file_favor)
{ {
int resolved = 0; int resolved = 0;
int error = 0; int error = 0;
...@@ -596,16 +606,14 @@ static int merge_conflict_resolve( ...@@ -596,16 +606,14 @@ static int merge_conflict_resolve(
if ((error = merge_conflict_resolve_trivial(&resolved, diff_list, conflict)) < 0) if ((error = merge_conflict_resolve_trivial(&resolved, diff_list, conflict)) < 0)
goto done; goto done;
if (automerge_flags != GIT_MERGE_AUTOMERGE_NONE) {
if (!resolved && (error = merge_conflict_resolve_one_removed(&resolved, diff_list, conflict)) < 0) if (!resolved && (error = merge_conflict_resolve_one_removed(&resolved, diff_list, conflict)) < 0)
goto done; goto done;
if (!resolved && (error = merge_conflict_resolve_one_renamed(&resolved, diff_list, conflict)) < 0) if (!resolved && (error = merge_conflict_resolve_one_renamed(&resolved, diff_list, conflict)) < 0)
goto done; goto done;
if (!resolved && (error = merge_conflict_resolve_automerge(&resolved, diff_list, conflict, automerge_flags)) < 0) if (!resolved && (error = merge_conflict_resolve_automerge(&resolved, diff_list, conflict, merge_file_favor)) < 0)
goto done; goto done;
}
*out = resolved; *out = resolved;
...@@ -1147,6 +1155,44 @@ GIT_INLINE(int) merge_diff_detect_type( ...@@ -1147,6 +1155,44 @@ GIT_INLINE(int) merge_diff_detect_type(
return 0; return 0;
} }
GIT_INLINE(int) merge_diff_detect_binary(
git_repository *repo,
git_merge_diff *conflict)
{
git_blob *ancestor_blob = NULL, *our_blob = NULL, *their_blob = NULL;
int error = 0;
if (GIT_MERGE_INDEX_ENTRY_ISFILE(conflict->ancestor_entry)) {
if ((error = git_blob_lookup(&ancestor_blob, repo, &conflict->ancestor_entry.oid)) < 0)
goto done;
conflict->binary = git_blob_is_binary(ancestor_blob);
}
if (!conflict->binary &&
GIT_MERGE_INDEX_ENTRY_ISFILE(conflict->our_entry)) {
if ((error = git_blob_lookup(&our_blob, repo, &conflict->our_entry.oid)) < 0)
goto done;
conflict->binary = git_blob_is_binary(our_blob);
}
if (!conflict->binary &&
GIT_MERGE_INDEX_ENTRY_ISFILE(conflict->their_entry)) {
if ((error = git_blob_lookup(&their_blob, repo, &conflict->their_entry.oid)) < 0)
goto done;
conflict->binary = git_blob_is_binary(their_blob);
}
done:
git_blob_free(ancestor_blob);
git_blob_free(our_blob);
git_blob_free(their_blob);
return error;
}
GIT_INLINE(int) index_entry_dup( GIT_INLINE(int) index_entry_dup(
git_index_entry *out, git_index_entry *out,
git_pool *pool, git_pool *pool,
...@@ -1218,6 +1264,7 @@ static int merge_diff_list_insert_conflict( ...@@ -1218,6 +1264,7 @@ static int merge_diff_list_insert_conflict(
if ((conflict = merge_diff_from_index_entries(diff_list, tree_items)) == NULL || if ((conflict = merge_diff_from_index_entries(diff_list, tree_items)) == NULL ||
merge_diff_detect_type(conflict) < 0 || merge_diff_detect_type(conflict) < 0 ||
merge_diff_detect_df_conflict(merge_df_data, conflict) < 0 || merge_diff_detect_df_conflict(merge_df_data, conflict) < 0 ||
merge_diff_detect_binary(diff_list->repo, conflict) < 0 ||
git_vector_insert(&diff_list->conflicts, conflict) < 0) git_vector_insert(&diff_list->conflicts, conflict) < 0)
return -1; return -1;
...@@ -1589,7 +1636,7 @@ int git_merge_trees( ...@@ -1589,7 +1636,7 @@ int git_merge_trees(
git_vector_foreach(&changes, i, conflict) { git_vector_foreach(&changes, i, conflict) {
int resolved = 0; int resolved = 0;
if ((error = merge_conflict_resolve(&resolved, diff_list, conflict, opts.automerge_flags)) < 0) if ((error = merge_conflict_resolve(&resolved, diff_list, conflict, opts.file_favor)) < 0)
goto done; goto done;
if (!resolved) if (!resolved)
...@@ -2119,6 +2166,8 @@ static int merge_normalize_opts( ...@@ -2119,6 +2166,8 @@ static int merge_normalize_opts(
git_repository *repo, git_repository *repo,
git_merge_opts *opts, git_merge_opts *opts,
const git_merge_opts *given, const git_merge_opts *given,
const git_merge_head *ancestor_head,
const git_merge_head *our_head,
size_t their_heads_len, size_t their_heads_len,
const git_merge_head **their_heads) const git_merge_head **their_heads)
{ {
...@@ -2138,8 +2187,20 @@ static int merge_normalize_opts( ...@@ -2138,8 +2187,20 @@ static int merge_normalize_opts(
if (!opts->checkout_opts.checkout_strategy) if (!opts->checkout_opts.checkout_strategy)
opts->checkout_opts.checkout_strategy = default_checkout_strategy; opts->checkout_opts.checkout_strategy = default_checkout_strategy;
if (!opts->checkout_opts.our_label) /* TODO: for multiple ancestors in merge-recursive, this is "merged common ancestors" */
opts->checkout_opts.our_label = "HEAD"; if (!opts->checkout_opts.ancestor_label) {
if (ancestor_head && ancestor_head->commit)
opts->checkout_opts.ancestor_label = git_commit_summary(ancestor_head->commit);
else
opts->checkout_opts.ancestor_label = "ancestor";
}
if (!opts->checkout_opts.our_label) {
if (our_head && our_head->ref_name)
opts->checkout_opts.our_label = our_head->ref_name;
else
opts->checkout_opts.our_label = "ours";
}
if (!opts->checkout_opts.their_label) { if (!opts->checkout_opts.their_label) {
if (their_heads_len == 1 && their_heads[0]->ref_name) if (their_heads_len == 1 && their_heads[0]->ref_name)
...@@ -2434,9 +2495,6 @@ int git_merge( ...@@ -2434,9 +2495,6 @@ int git_merge(
their_trees = git__calloc(their_heads_len, sizeof(git_tree *)); their_trees = git__calloc(their_heads_len, sizeof(git_tree *));
GITERR_CHECK_ALLOC(their_trees); GITERR_CHECK_ALLOC(their_trees);
if ((error = merge_normalize_opts(repo, &opts, given_opts, their_heads_len, their_heads)) < 0)
goto on_error;
if ((error = git_repository__ensure_not_bare(repo, "merge")) < 0) if ((error = git_repository__ensure_not_bare(repo, "merge")) < 0)
goto on_error; goto on_error;
...@@ -2448,6 +2506,9 @@ int git_merge( ...@@ -2448,6 +2506,9 @@ int git_merge(
error != GIT_ENOTFOUND) error != GIT_ENOTFOUND)
goto on_error; goto on_error;
if ((error = merge_normalize_opts(repo, &opts, given_opts, ancestor_head, our_head, their_heads_len, their_heads)) < 0)
goto on_error;
if (their_heads_len == 1 && if (their_heads_len == 1 &&
ancestor_head != NULL && ancestor_head != NULL &&
(merge_check_uptodate(result, ancestor_head, their_heads[0]) || (merge_check_uptodate(result, ancestor_head, their_heads[0]) ||
......
...@@ -106,6 +106,8 @@ typedef struct { ...@@ -106,6 +106,8 @@ typedef struct {
git_index_entry their_entry; git_index_entry their_entry;
git_delta_t their_status; git_delta_t their_status;
int binary:1;
} git_merge_diff; } git_merge_diff;
/** Internal structure for merge inputs */ /** Internal structure for merge inputs */
......
...@@ -130,7 +130,7 @@ int git_merge_files( ...@@ -130,7 +130,7 @@ int git_merge_files(
git_merge_file_input *ancestor, git_merge_file_input *ancestor,
git_merge_file_input *ours, git_merge_file_input *ours,
git_merge_file_input *theirs, git_merge_file_input *theirs,
git_merge_automerge_flags flags) git_merge_file_options *opts)
{ {
xmparam_t xmparam; xmparam_t xmparam;
mmbuffer_t mmbuffer; mmbuffer_t mmbuffer;
...@@ -152,11 +152,19 @@ int git_merge_files( ...@@ -152,11 +152,19 @@ int git_merge_files(
out->path = merge_file_best_path(ancestor, ours, theirs); out->path = merge_file_best_path(ancestor, ours, theirs);
out->mode = merge_file_best_mode(ancestor, ours, theirs); out->mode = merge_file_best_mode(ancestor, ours, theirs);
if (flags == GIT_MERGE_AUTOMERGE_FAVOR_OURS) if (opts && opts->favor == GIT_MERGE_FILE_FAVOR_OURS)
xmparam.favor = XDL_MERGE_FAVOR_OURS; xmparam.favor = XDL_MERGE_FAVOR_OURS;
else if (opts && opts->favor == GIT_MERGE_FILE_FAVOR_THEIRS)
if (flags == GIT_MERGE_AUTOMERGE_FAVOR_THEIRS)
xmparam.favor = XDL_MERGE_FAVOR_THEIRS; xmparam.favor = XDL_MERGE_FAVOR_THEIRS;
else if (opts && opts->favor == GIT_MERGE_FILE_FAVOR_UNION)
xmparam.favor = XDL_MERGE_FAVOR_UNION;
xmparam.level =
(opts && (opts->flags & GIT_MERGE_FILE_SIMPLIFY_ALNUM)) ?
XDL_MERGE_ZEALOUS_ALNUM : XDL_MERGE_ZEALOUS;
if (opts && opts->style == GIT_MERGE_FILE_STYLE_DIFF3)
xmparam.style = XDL_MERGE_DIFF3;
if ((xdl_result = xdl_merge(&ancestor->mmfile, &ours->mmfile, if ((xdl_result = xdl_merge(&ancestor->mmfile, &ours->mmfile,
&theirs->mmfile, &xmparam, &mmbuffer)) < 0) { &theirs->mmfile, &xmparam, &mmbuffer)) < 0) {
......
...@@ -34,6 +34,27 @@ typedef struct { ...@@ -34,6 +34,27 @@ typedef struct {
#define GIT_MERGE_FILE_RESULT_INIT {0} #define GIT_MERGE_FILE_RESULT_INIT {0}
typedef enum {
/* Condense non-alphanumeric regions for simplified diff file */
GIT_MERGE_FILE_SIMPLIFY_ALNUM = (1 << 0),
} git_merge_file_flags_t;
typedef enum {
/* Create standard conflicted merge files */
GIT_MERGE_FILE_STYLE_MERGE = 0,
/* Create diff3-style files */
GIT_MERGE_FILE_STYLE_DIFF3 = 1,
} git_merge_file_style_t;
typedef struct {
git_merge_file_favor_t favor;
git_merge_file_flags_t flags;
git_merge_file_style_t style;
} git_merge_file_options;
#define GIT_MERGE_FILE_OPTIONS_INIT {0}
int git_merge_file_input_from_index_entry( int git_merge_file_input_from_index_entry(
git_merge_file_input *input, git_merge_file_input *input,
git_repository *repo, git_repository *repo,
...@@ -49,7 +70,7 @@ int git_merge_files( ...@@ -49,7 +70,7 @@ int git_merge_files(
git_merge_file_input *ancestor, git_merge_file_input *ancestor,
git_merge_file_input *ours, git_merge_file_input *ours,
git_merge_file_input *theirs, git_merge_file_input *theirs,
git_merge_automerge_flags flags); git_merge_file_options *opts);
GIT_INLINE(void) git_merge_file_input_free(git_merge_file_input *input) GIT_INLINE(void) git_merge_file_input_free(git_merge_file_input *input)
{ {
......
...@@ -149,7 +149,7 @@ void test_merge_trees_automerge__favor_ours(void) ...@@ -149,7 +149,7 @@ void test_merge_trees_automerge__favor_ours(void)
REMOVED_IN_MASTER_REUC_ENTRY, REMOVED_IN_MASTER_REUC_ENTRY,
}; };
opts.automerge_flags = GIT_MERGE_AUTOMERGE_FAVOR_OURS; opts.file_favor = GIT_MERGE_FILE_FAVOR_OURS;
cl_git_pass(merge_trees_from_branches(&index, repo, "master", THEIRS_AUTOMERGE_BRANCH, &opts)); cl_git_pass(merge_trees_from_branches(&index, repo, "master", THEIRS_AUTOMERGE_BRANCH, &opts));
...@@ -180,7 +180,7 @@ void test_merge_trees_automerge__favor_theirs(void) ...@@ -180,7 +180,7 @@ void test_merge_trees_automerge__favor_theirs(void)
REMOVED_IN_MASTER_REUC_ENTRY, REMOVED_IN_MASTER_REUC_ENTRY,
}; };
opts.automerge_flags = GIT_MERGE_AUTOMERGE_FAVOR_THEIRS; opts.file_favor = GIT_MERGE_FILE_FAVOR_THEIRS;
cl_git_pass(merge_trees_from_branches(&index, repo, "master", THEIRS_AUTOMERGE_BRANCH, &opts)); cl_git_pass(merge_trees_from_branches(&index, repo, "master", THEIRS_AUTOMERGE_BRANCH, &opts));
......
...@@ -25,7 +25,7 @@ void test_merge_trees_trivial__cleanup(void) ...@@ -25,7 +25,7 @@ void test_merge_trees_trivial__cleanup(void)
} }
static int merge_trivial(git_index **index, const char *ours, const char *theirs, bool automerge) static int merge_trivial(git_index **index, const char *ours, const char *theirs)
{ {
git_commit *our_commit, *their_commit, *ancestor_commit; git_commit *our_commit, *their_commit, *ancestor_commit;
git_tree *our_tree, *their_tree, *ancestor_tree; git_tree *our_tree, *their_tree, *ancestor_tree;
...@@ -33,8 +33,6 @@ static int merge_trivial(git_index **index, const char *ours, const char *theirs ...@@ -33,8 +33,6 @@ static int merge_trivial(git_index **index, const char *ours, const char *theirs
git_buf branch_buf = GIT_BUF_INIT; git_buf branch_buf = GIT_BUF_INIT;
git_merge_tree_opts opts = GIT_MERGE_TREE_OPTS_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); 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_reference_name_to_id(&our_oid, repo, branch_buf.ptr));
cl_git_pass(git_commit_lookup(&our_commit, repo, &our_oid)); cl_git_pass(git_commit_lookup(&our_commit, repo, &our_oid));
...@@ -86,7 +84,7 @@ void test_merge_trees_trivial__2alt(void) ...@@ -86,7 +84,7 @@ void test_merge_trees_trivial__2alt(void)
git_index *result; git_index *result;
const git_index_entry *entry; const git_index_entry *entry;
cl_git_pass(merge_trivial(&result, "trivial-2alt", "trivial-2alt-branch", 0)); cl_git_pass(merge_trivial(&result, "trivial-2alt", "trivial-2alt-branch"));
cl_assert(entry = git_index_get_bypath(result, "new-in-branch.txt", 0)); cl_assert(entry = git_index_get_bypath(result, "new-in-branch.txt", 0));
cl_assert(git_index_reuc_entrycount(result) == 0); cl_assert(git_index_reuc_entrycount(result) == 0);
...@@ -101,7 +99,7 @@ void test_merge_trees_trivial__3alt(void) ...@@ -101,7 +99,7 @@ void test_merge_trees_trivial__3alt(void)
git_index *result; git_index *result;
const git_index_entry *entry; const git_index_entry *entry;
cl_git_pass(merge_trivial(&result, "trivial-3alt", "trivial-3alt-branch", 0)); cl_git_pass(merge_trivial(&result, "trivial-3alt", "trivial-3alt-branch"));
cl_assert(entry = git_index_get_bypath(result, "new-in-3alt.txt", 0)); cl_assert(entry = git_index_get_bypath(result, "new-in-3alt.txt", 0));
cl_assert(git_index_reuc_entrycount(result) == 0); cl_assert(git_index_reuc_entrycount(result) == 0);
...@@ -116,7 +114,7 @@ void test_merge_trees_trivial__4(void) ...@@ -116,7 +114,7 @@ void test_merge_trees_trivial__4(void)
git_index *result; git_index *result;
const git_index_entry *entry; const git_index_entry *entry;
cl_git_pass(merge_trivial(&result, "trivial-4", "trivial-4-branch", 0)); cl_git_pass(merge_trivial(&result, "trivial-4", "trivial-4-branch"));
cl_assert((entry = git_index_get_bypath(result, "new-and-different.txt", 0)) == NULL); cl_assert((entry = git_index_get_bypath(result, "new-and-different.txt", 0)) == NULL);
cl_assert(git_index_reuc_entrycount(result) == 0); cl_assert(git_index_reuc_entrycount(result) == 0);
...@@ -134,7 +132,7 @@ void test_merge_trees_trivial__5alt_1(void) ...@@ -134,7 +132,7 @@ void test_merge_trees_trivial__5alt_1(void)
git_index *result; git_index *result;
const git_index_entry *entry; const git_index_entry *entry;
cl_git_pass(merge_trivial(&result, "trivial-5alt-1", "trivial-5alt-1-branch", 0)); cl_git_pass(merge_trivial(&result, "trivial-5alt-1", "trivial-5alt-1-branch"));
cl_assert(entry = git_index_get_bypath(result, "new-and-same.txt", 0)); cl_assert(entry = git_index_get_bypath(result, "new-and-same.txt", 0));
cl_assert(git_index_reuc_entrycount(result) == 0); cl_assert(git_index_reuc_entrycount(result) == 0);
...@@ -149,7 +147,7 @@ void test_merge_trees_trivial__5alt_2(void) ...@@ -149,7 +147,7 @@ void test_merge_trees_trivial__5alt_2(void)
git_index *result; git_index *result;
const git_index_entry *entry; const git_index_entry *entry;
cl_git_pass(merge_trivial(&result, "trivial-5alt-2", "trivial-5alt-2-branch", 0)); cl_git_pass(merge_trivial(&result, "trivial-5alt-2", "trivial-5alt-2-branch"));
cl_assert(entry = git_index_get_bypath(result, "modified-to-same.txt", 0)); cl_assert(entry = git_index_get_bypath(result, "modified-to-same.txt", 0));
cl_assert(git_index_reuc_entrycount(result) == 0); cl_assert(git_index_reuc_entrycount(result) == 0);
...@@ -163,26 +161,9 @@ void test_merge_trees_trivial__6(void) ...@@ -163,26 +161,9 @@ void test_merge_trees_trivial__6(void)
{ {
git_index *result; git_index *result;
const git_index_entry *entry; 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; const git_index_reuc_entry *reuc;
cl_git_pass(merge_trivial(&result, "trivial-6", "trivial-6-branch", 1)); cl_git_pass(merge_trivial(&result, "trivial-6", "trivial-6-branch"));
cl_assert((entry = git_index_get_bypath(result, "removed-in-both.txt", 0)) == NULL); cl_assert((entry = git_index_get_bypath(result, "removed-in-both.txt", 0)) == NULL);
cl_assert(git_index_reuc_entrycount(result) == 1); cl_assert(git_index_reuc_entrycount(result) == 1);
...@@ -198,27 +179,9 @@ void test_merge_trees_trivial__8(void) ...@@ -198,27 +179,9 @@ void test_merge_trees_trivial__8(void)
{ {
git_index *result; git_index *result;
const git_index_entry *entry; 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; const git_index_reuc_entry *reuc;
cl_git_pass(merge_trivial(&result, "trivial-8", "trivial-8-branch", 1)); cl_git_pass(merge_trivial(&result, "trivial-8", "trivial-8-branch"));
cl_assert((entry = git_index_get_bypath(result, "removed-in-8.txt", 0)) == NULL); cl_assert((entry = git_index_get_bypath(result, "removed-in-8.txt", 0)) == NULL);
...@@ -236,25 +199,7 @@ void test_merge_trees_trivial__7(void) ...@@ -236,25 +199,7 @@ void test_merge_trees_trivial__7(void)
git_index *result; git_index *result;
const git_index_entry *entry; const git_index_entry *entry;
cl_git_pass(merge_trivial(&result, "trivial-7", "trivial-7-branch", 0)); cl_git_pass(merge_trivial(&result, "trivial-7", "trivial-7-branch"));
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((entry = git_index_get_bypath(result, "removed-in-7.txt", 0)) == NULL);
cl_assert(git_index_reuc_entrycount(result) == 0); cl_assert(git_index_reuc_entrycount(result) == 0);
...@@ -271,27 +216,9 @@ void test_merge_trees_trivial__10(void) ...@@ -271,27 +216,9 @@ void test_merge_trees_trivial__10(void)
{ {
git_index *result; git_index *result;
const git_index_entry *entry; 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; const git_index_reuc_entry *reuc;
cl_git_pass(merge_trivial(&result, "trivial-10", "trivial-10-branch", 1)); cl_git_pass(merge_trivial(&result, "trivial-10", "trivial-10-branch"));
cl_assert((entry = git_index_get_bypath(result, "removed-in-10-branch.txt", 0)) == NULL); cl_assert((entry = git_index_get_bypath(result, "removed-in-10-branch.txt", 0)) == NULL);
...@@ -309,25 +236,7 @@ void test_merge_trees_trivial__9(void) ...@@ -309,25 +236,7 @@ void test_merge_trees_trivial__9(void)
git_index *result; git_index *result;
const git_index_entry *entry; const git_index_entry *entry;
cl_git_pass(merge_trivial(&result, "trivial-9", "trivial-9-branch", 0)); cl_git_pass(merge_trivial(&result, "trivial-9", "trivial-9-branch"));
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((entry = git_index_get_bypath(result, "removed-in-9-branch.txt", 0)) == NULL);
cl_assert(git_index_reuc_entrycount(result) == 0); cl_assert(git_index_reuc_entrycount(result) == 0);
...@@ -346,7 +255,7 @@ void test_merge_trees_trivial__13(void) ...@@ -346,7 +255,7 @@ void test_merge_trees_trivial__13(void)
const git_index_entry *entry; const git_index_entry *entry;
git_oid expected_oid; git_oid expected_oid;
cl_git_pass(merge_trivial(&result, "trivial-13", "trivial-13-branch", 0)); cl_git_pass(merge_trivial(&result, "trivial-13", "trivial-13-branch"));
cl_assert(entry = git_index_get_bypath(result, "modified-in-13.txt", 0)); cl_assert(entry = git_index_get_bypath(result, "modified-in-13.txt", 0));
cl_git_pass(git_oid_fromstr(&expected_oid, "1cff9ec6a47a537380dedfdd17c9e76d74259a2b")); cl_git_pass(git_oid_fromstr(&expected_oid, "1cff9ec6a47a537380dedfdd17c9e76d74259a2b"));
...@@ -365,7 +274,7 @@ void test_merge_trees_trivial__14(void) ...@@ -365,7 +274,7 @@ void test_merge_trees_trivial__14(void)
const git_index_entry *entry; const git_index_entry *entry;
git_oid expected_oid; git_oid expected_oid;
cl_git_pass(merge_trivial(&result, "trivial-14", "trivial-14-branch", 0)); cl_git_pass(merge_trivial(&result, "trivial-14", "trivial-14-branch"));
cl_assert(entry = git_index_get_bypath(result, "modified-in-14-branch.txt", 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_git_pass(git_oid_fromstr(&expected_oid, "26153a3ff3649b6c2bb652d3f06878c6e0a172f9"));
...@@ -383,7 +292,7 @@ void test_merge_trees_trivial__11(void) ...@@ -383,7 +292,7 @@ void test_merge_trees_trivial__11(void)
git_index *result; git_index *result;
const git_index_entry *entry; const git_index_entry *entry;
cl_git_pass(merge_trivial(&result, "trivial-11", "trivial-11-branch", 0)); cl_git_pass(merge_trivial(&result, "trivial-11", "trivial-11-branch"));
cl_assert((entry = git_index_get_bypath(result, "modified-in-both.txt", 0)) == NULL); cl_assert((entry = git_index_get_bypath(result, "modified-in-both.txt", 0)) == NULL);
cl_assert(git_index_reuc_entrycount(result) == 0); cl_assert(git_index_reuc_entrycount(result) == 0);
......
#include "clar_libgit2.h"
#include "git2/repository.h"
#include "git2/merge.h"
#include "buffer.h"
#include "merge.h"
#include "../merge_helpers.h"
static git_repository *repo;
#define TEST_REPO_PATH "merge-resolve"
#define SUBMODULE_MAIN_BRANCH "submodules"
#define SUBMODULE_OTHER_BRANCH "submodules-branch"
#define SUBMODULE_OTHER2_BRANCH "submodules-branch2"
#define TEST_INDEX_PATH TEST_REPO_PATH "/.git/index"
// Fixture setup and teardown
void test_merge_workdir_submodules__initialize(void)
{
repo = cl_git_sandbox_init(TEST_REPO_PATH);
}
void test_merge_workdir_submodules__cleanup(void)
{
cl_git_sandbox_cleanup();
}
void test_merge_workdir_submodules__automerge(void)
{
git_reference *our_ref, *their_ref;
git_commit *our_commit;
git_merge_head *their_head;
git_merge_result *result;
git_merge_opts opts = GIT_MERGE_OPTS_INIT;
git_index *index;
struct merge_index_entry merge_index_entries[] = {
{ 0100644, "caff6b7d44973f53e3e0cf31d0d695188b19aec6", 0, ".gitmodules" },
{ 0100644, "950a663a6a7b2609eed1ed1ba9f41eb1a3192a9f", 0, "file1.txt" },
{ 0100644, "343e660b9cb4bee5f407c2e33fcb9df24d9407a4", 0, "file2.txt" },
{ 0160000, "d3d806a4bef96889117fd7ebac0e3cb5ec152932", 1, "submodule" },
{ 0160000, "297aa6cd028b3336c7802c7a6f49143da4e1602d", 2, "submodule" },
{ 0160000, "ae39c77c70cb6bad18bb471912460c4e1ba0f586", 3, "submodule" },
};
cl_git_pass(git_reference_lookup(&our_ref, repo, "refs/heads/" SUBMODULE_MAIN_BRANCH));
cl_git_pass(git_commit_lookup(&our_commit, repo, git_reference_target(our_ref)));
cl_git_pass(git_reset(repo, (git_object *)our_commit, GIT_RESET_HARD));
cl_git_pass(git_reference_lookup(&their_ref, repo, "refs/heads/" SUBMODULE_OTHER_BRANCH));
cl_git_pass(git_merge_head_from_ref(&their_head, repo, their_ref));
cl_git_pass(git_merge(&result, repo, (const git_merge_head **)&their_head, 1, &opts));
cl_git_pass(git_repository_index(&index, repo));
cl_assert(merge_test_index(index, merge_index_entries, 6));
git_index_free(index);
git_merge_result_free(result);
git_merge_head_free(their_head);
git_commit_free(our_commit);
git_reference_free(their_ref);
git_reference_free(our_ref);
}
void test_merge_workdir_submodules__take_changed(void)
{
git_reference *our_ref, *their_ref;
git_commit *our_commit;
git_merge_head *their_head;
git_merge_result *result;
git_merge_opts opts = GIT_MERGE_OPTS_INIT;
git_index *index;
struct merge_index_entry merge_index_entries[] = {
{ 0100644, "caff6b7d44973f53e3e0cf31d0d695188b19aec6", 0, ".gitmodules" },
{ 0100644, "b438ff23300b2e0f80b84a6f30140dfa91e71423", 0, "file1.txt" },
{ 0100644, "f27fbafdfa6693f8f7a5128506fe3e338dbfcad2", 0, "file2.txt" },
{ 0160000, "297aa6cd028b3336c7802c7a6f49143da4e1602d", 0, "submodule" },
};
cl_git_pass(git_reference_lookup(&our_ref, repo, "refs/heads/" SUBMODULE_MAIN_BRANCH));
cl_git_pass(git_commit_lookup(&our_commit, repo, git_reference_target(our_ref)));
cl_git_pass(git_reset(repo, (git_object *)our_commit, GIT_RESET_HARD));
cl_git_pass(git_reference_lookup(&their_ref, repo, "refs/heads/" SUBMODULE_OTHER2_BRANCH));
cl_git_pass(git_merge_head_from_ref(&their_head, repo, their_ref));
cl_git_pass(git_merge(&result, repo, (const git_merge_head **)&their_head, 1, &opts));
cl_git_pass(git_repository_index(&index, repo));
cl_assert(merge_test_index(index, merge_index_entries, 4));
git_index_free(index);
git_merge_result_free(result);
git_merge_head_free(their_head);
git_commit_free(our_commit);
git_reference_free(their_ref);
git_reference_free(our_ref);
}
...@@ -28,7 +28,7 @@ void test_merge_workdir_trivial__cleanup(void) ...@@ -28,7 +28,7 @@ void test_merge_workdir_trivial__cleanup(void)
} }
static int merge_trivial(const char *ours, const char *theirs, bool automerge) static int merge_trivial(const char *ours, const char *theirs)
{ {
git_buf branch_buf = GIT_BUF_INIT; git_buf branch_buf = GIT_BUF_INIT;
git_checkout_opts checkout_opts = GIT_CHECKOUT_OPTS_INIT; git_checkout_opts checkout_opts = GIT_CHECKOUT_OPTS_INIT;
...@@ -39,8 +39,6 @@ static int merge_trivial(const char *ours, const char *theirs, bool automerge) ...@@ -39,8 +39,6 @@ static int merge_trivial(const char *ours, const char *theirs, bool automerge)
checkout_opts.checkout_strategy = GIT_CHECKOUT_FORCE; checkout_opts.checkout_strategy = GIT_CHECKOUT_FORCE;
opts.merge_tree_opts.automerge_flags |= automerge ? 0 : GIT_MERGE_AUTOMERGE_NONE;
git_buf_printf(&branch_buf, "%s%s", GIT_REFS_HEADS_DIR, ours); git_buf_printf(&branch_buf, "%s%s", GIT_REFS_HEADS_DIR, ours);
cl_git_pass(git_reference_symbolic_create(&our_ref, repo, "HEAD", branch_buf.ptr, 1, NULL, NULL)); cl_git_pass(git_reference_symbolic_create(&our_ref, repo, "HEAD", branch_buf.ptr, 1, NULL, NULL));
...@@ -83,7 +81,7 @@ void test_merge_workdir_trivial__2alt(void) ...@@ -83,7 +81,7 @@ void test_merge_workdir_trivial__2alt(void)
{ {
const git_index_entry *entry; const git_index_entry *entry;
cl_git_pass(merge_trivial("trivial-2alt", "trivial-2alt-branch", 0)); cl_git_pass(merge_trivial("trivial-2alt", "trivial-2alt-branch"));
cl_assert(entry = git_index_get_bypath(repo_index, "new-in-branch.txt", 0)); cl_assert(entry = git_index_get_bypath(repo_index, "new-in-branch.txt", 0));
cl_assert(git_index_reuc_entrycount(repo_index) == 0); cl_assert(git_index_reuc_entrycount(repo_index) == 0);
...@@ -95,7 +93,7 @@ void test_merge_workdir_trivial__3alt(void) ...@@ -95,7 +93,7 @@ void test_merge_workdir_trivial__3alt(void)
{ {
const git_index_entry *entry; const git_index_entry *entry;
cl_git_pass(merge_trivial("trivial-3alt", "trivial-3alt-branch", 0)); cl_git_pass(merge_trivial("trivial-3alt", "trivial-3alt-branch"));
cl_assert(entry = git_index_get_bypath(repo_index, "new-in-3alt.txt", 0)); cl_assert(entry = git_index_get_bypath(repo_index, "new-in-3alt.txt", 0));
cl_assert(git_index_reuc_entrycount(repo_index) == 0); cl_assert(git_index_reuc_entrycount(repo_index) == 0);
...@@ -107,7 +105,7 @@ void test_merge_workdir_trivial__4(void) ...@@ -107,7 +105,7 @@ void test_merge_workdir_trivial__4(void)
{ {
const git_index_entry *entry; const git_index_entry *entry;
cl_git_pass(merge_trivial("trivial-4", "trivial-4-branch", 0)); cl_git_pass(merge_trivial("trivial-4", "trivial-4-branch"));
cl_assert((entry = git_index_get_bypath(repo_index, "new-and-different.txt", 0)) == NULL); cl_assert((entry = git_index_get_bypath(repo_index, "new-and-different.txt", 0)) == NULL);
cl_assert(git_index_reuc_entrycount(repo_index) == 0); cl_assert(git_index_reuc_entrycount(repo_index) == 0);
...@@ -122,7 +120,7 @@ void test_merge_workdir_trivial__5alt_1(void) ...@@ -122,7 +120,7 @@ void test_merge_workdir_trivial__5alt_1(void)
{ {
const git_index_entry *entry; const git_index_entry *entry;
cl_git_pass(merge_trivial("trivial-5alt-1", "trivial-5alt-1-branch", 0)); cl_git_pass(merge_trivial("trivial-5alt-1", "trivial-5alt-1-branch"));
cl_assert(entry = git_index_get_bypath(repo_index, "new-and-same.txt", 0)); cl_assert(entry = git_index_get_bypath(repo_index, "new-and-same.txt", 0));
cl_assert(git_index_reuc_entrycount(repo_index) == 0); cl_assert(git_index_reuc_entrycount(repo_index) == 0);
...@@ -134,7 +132,7 @@ void test_merge_workdir_trivial__5alt_2(void) ...@@ -134,7 +132,7 @@ void test_merge_workdir_trivial__5alt_2(void)
{ {
const git_index_entry *entry; const git_index_entry *entry;
cl_git_pass(merge_trivial("trivial-5alt-2", "trivial-5alt-2-branch", 0)); cl_git_pass(merge_trivial("trivial-5alt-2", "trivial-5alt-2-branch"));
cl_assert(entry = git_index_get_bypath(repo_index, "modified-to-same.txt", 0)); cl_assert(entry = git_index_get_bypath(repo_index, "modified-to-same.txt", 0));
cl_assert(git_index_reuc_entrycount(repo_index) == 0); cl_assert(git_index_reuc_entrycount(repo_index) == 0);
...@@ -145,23 +143,9 @@ void test_merge_workdir_trivial__5alt_2(void) ...@@ -145,23 +143,9 @@ void test_merge_workdir_trivial__5alt_2(void)
void test_merge_workdir_trivial__6(void) void test_merge_workdir_trivial__6(void)
{ {
const git_index_entry *entry; const git_index_entry *entry;
cl_git_pass(merge_trivial("trivial-6", "trivial-6-branch", 0));
cl_assert((entry = git_index_get_bypath(repo_index, "removed-in-both.txt", 0)) == NULL);
cl_assert(git_index_reuc_entrycount(repo_index) == 0);
cl_assert(merge_trivial_conflict_entrycount() == 1);
cl_assert(entry = git_index_get_bypath(repo_index, "removed-in-both.txt", 1));
}
/* 6: ancest:ancest+, head:(empty), remote:(empty) = result:no merge */
void test_merge_workdir_trivial__6_automerge(void)
{
const git_index_entry *entry;
const git_index_reuc_entry *reuc; const git_index_reuc_entry *reuc;
cl_git_pass(merge_trivial("trivial-6", "trivial-6-branch", 1)); cl_git_pass(merge_trivial("trivial-6", "trivial-6-branch"));
cl_assert((entry = git_index_get_bypath(repo_index, "removed-in-both.txt", 0)) == NULL); cl_assert((entry = git_index_get_bypath(repo_index, "removed-in-both.txt", 0)) == NULL);
cl_assert(git_index_reuc_entrycount(repo_index) == 1); cl_assert(git_index_reuc_entrycount(repo_index) == 1);
...@@ -174,24 +158,9 @@ void test_merge_workdir_trivial__6_automerge(void) ...@@ -174,24 +158,9 @@ void test_merge_workdir_trivial__6_automerge(void)
void test_merge_workdir_trivial__8(void) void test_merge_workdir_trivial__8(void)
{ {
const git_index_entry *entry; const git_index_entry *entry;
cl_git_pass(merge_trivial("trivial-8", "trivial-8-branch", 0));
cl_assert((entry = git_index_get_bypath(repo_index, "removed-in-8.txt", 0)) == NULL);
cl_assert(git_index_reuc_entrycount(repo_index) == 0);
cl_assert(merge_trivial_conflict_entrycount() == 2);
cl_assert(entry = git_index_get_bypath(repo_index, "removed-in-8.txt", 1));
cl_assert(entry = git_index_get_bypath(repo_index, "removed-in-8.txt", 3));
}
/* 8: ancest:ancest^, head:(empty), remote:ancest = result:no merge */
void test_merge_workdir_trivial__8_automerge(void)
{
const git_index_entry *entry;
const git_index_reuc_entry *reuc; const git_index_reuc_entry *reuc;
cl_git_pass(merge_trivial("trivial-8", "trivial-8-branch", 1)); cl_git_pass(merge_trivial("trivial-8", "trivial-8-branch"));
cl_assert((entry = git_index_get_bypath(repo_index, "removed-in-8.txt", 0)) == NULL); cl_assert((entry = git_index_get_bypath(repo_index, "removed-in-8.txt", 0)) == NULL);
...@@ -206,22 +175,7 @@ void test_merge_workdir_trivial__7(void) ...@@ -206,22 +175,7 @@ void test_merge_workdir_trivial__7(void)
{ {
const git_index_entry *entry; const git_index_entry *entry;
cl_git_pass(merge_trivial("trivial-7", "trivial-7-branch", 0)); cl_git_pass(merge_trivial("trivial-7", "trivial-7-branch"));
cl_assert((entry = git_index_get_bypath(repo_index, "removed-in-7.txt", 0)) == NULL);
cl_assert(git_index_reuc_entrycount(repo_index) == 0);
cl_assert(merge_trivial_conflict_entrycount() == 2);
cl_assert(entry = git_index_get_bypath(repo_index, "removed-in-7.txt", 1));
cl_assert(entry = git_index_get_bypath(repo_index, "removed-in-7.txt", 3));
}
/* 7: ancest:ancest+, head:(empty), remote:remote = result:no merge */
void test_merge_workdir_trivial__7_automerge(void)
{
const git_index_entry *entry;
cl_git_pass(merge_trivial("trivial-7", "trivial-7-branch", 0));
cl_assert((entry = git_index_get_bypath(repo_index, "removed-in-7.txt", 0)) == NULL); cl_assert((entry = git_index_get_bypath(repo_index, "removed-in-7.txt", 0)) == NULL);
cl_assert(git_index_reuc_entrycount(repo_index) == 0); cl_assert(git_index_reuc_entrycount(repo_index) == 0);
...@@ -235,24 +189,9 @@ void test_merge_workdir_trivial__7_automerge(void) ...@@ -235,24 +189,9 @@ void test_merge_workdir_trivial__7_automerge(void)
void test_merge_workdir_trivial__10(void) void test_merge_workdir_trivial__10(void)
{ {
const git_index_entry *entry; const git_index_entry *entry;
cl_git_pass(merge_trivial("trivial-10", "trivial-10-branch", 0));
cl_assert((entry = git_index_get_bypath(repo_index, "removed-in-10-branch.txt", 0)) == NULL);
cl_assert(git_index_reuc_entrycount(repo_index) == 0);
cl_assert(merge_trivial_conflict_entrycount() == 2);
cl_assert(entry = git_index_get_bypath(repo_index, "removed-in-10-branch.txt", 1));
cl_assert(entry = git_index_get_bypath(repo_index, "removed-in-10-branch.txt", 2));
}
/* 10: ancest:ancest^, head:ancest, remote:(empty) = result:no merge */
void test_merge_workdir_trivial__10_automerge(void)
{
const git_index_entry *entry;
const git_index_reuc_entry *reuc; const git_index_reuc_entry *reuc;
cl_git_pass(merge_trivial("trivial-10", "trivial-10-branch", 1)); cl_git_pass(merge_trivial("trivial-10", "trivial-10-branch"));
cl_assert((entry = git_index_get_bypath(repo_index, "removed-in-10-branch.txt", 0)) == NULL); cl_assert((entry = git_index_get_bypath(repo_index, "removed-in-10-branch.txt", 0)) == NULL);
...@@ -267,22 +206,7 @@ void test_merge_workdir_trivial__9(void) ...@@ -267,22 +206,7 @@ void test_merge_workdir_trivial__9(void)
{ {
const git_index_entry *entry; const git_index_entry *entry;
cl_git_pass(merge_trivial("trivial-9", "trivial-9-branch", 0)); cl_git_pass(merge_trivial("trivial-9", "trivial-9-branch"));
cl_assert((entry = git_index_get_bypath(repo_index, "removed-in-9-branch.txt", 0)) == NULL);
cl_assert(git_index_reuc_entrycount(repo_index) == 0);
cl_assert(merge_trivial_conflict_entrycount() == 2);
cl_assert(entry = git_index_get_bypath(repo_index, "removed-in-9-branch.txt", 1));
cl_assert(entry = git_index_get_bypath(repo_index, "removed-in-9-branch.txt", 2));
}
/* 9: ancest:ancest+, head:head, remote:(empty) = result:no merge */
void test_merge_workdir_trivial__9_automerge(void)
{
const git_index_entry *entry;
cl_git_pass(merge_trivial("trivial-9", "trivial-9-branch", 1));
cl_assert((entry = git_index_get_bypath(repo_index, "removed-in-9-branch.txt", 0)) == NULL); cl_assert((entry = git_index_get_bypath(repo_index, "removed-in-9-branch.txt", 0)) == NULL);
cl_assert(git_index_reuc_entrycount(repo_index) == 0); cl_assert(git_index_reuc_entrycount(repo_index) == 0);
...@@ -298,7 +222,7 @@ void test_merge_workdir_trivial__13(void) ...@@ -298,7 +222,7 @@ void test_merge_workdir_trivial__13(void)
const git_index_entry *entry; const git_index_entry *entry;
git_oid expected_oid; git_oid expected_oid;
cl_git_pass(merge_trivial("trivial-13", "trivial-13-branch", 0)); cl_git_pass(merge_trivial("trivial-13", "trivial-13-branch"));
cl_assert(entry = git_index_get_bypath(repo_index, "modified-in-13.txt", 0)); cl_assert(entry = git_index_get_bypath(repo_index, "modified-in-13.txt", 0));
cl_git_pass(git_oid_fromstr(&expected_oid, "1cff9ec6a47a537380dedfdd17c9e76d74259a2b")); cl_git_pass(git_oid_fromstr(&expected_oid, "1cff9ec6a47a537380dedfdd17c9e76d74259a2b"));
...@@ -314,7 +238,7 @@ void test_merge_workdir_trivial__14(void) ...@@ -314,7 +238,7 @@ void test_merge_workdir_trivial__14(void)
const git_index_entry *entry; const git_index_entry *entry;
git_oid expected_oid; git_oid expected_oid;
cl_git_pass(merge_trivial("trivial-14", "trivial-14-branch", 0)); cl_git_pass(merge_trivial("trivial-14", "trivial-14-branch"));
cl_assert(entry = git_index_get_bypath(repo_index, "modified-in-14-branch.txt", 0)); cl_assert(entry = git_index_get_bypath(repo_index, "modified-in-14-branch.txt", 0));
cl_git_pass(git_oid_fromstr(&expected_oid, "26153a3ff3649b6c2bb652d3f06878c6e0a172f9")); cl_git_pass(git_oid_fromstr(&expected_oid, "26153a3ff3649b6c2bb652d3f06878c6e0a172f9"));
...@@ -329,7 +253,7 @@ void test_merge_workdir_trivial__11(void) ...@@ -329,7 +253,7 @@ void test_merge_workdir_trivial__11(void)
{ {
const git_index_entry *entry; const git_index_entry *entry;
cl_git_pass(merge_trivial("trivial-11", "trivial-11-branch", 0)); cl_git_pass(merge_trivial("trivial-11", "trivial-11-branch"));
cl_assert((entry = git_index_get_bypath(repo_index, "modified-in-both.txt", 0)) == NULL); cl_assert((entry = git_index_get_bypath(repo_index, "modified-in-both.txt", 0)) == NULL);
cl_assert(git_index_reuc_entrycount(repo_index) == 0); cl_assert(git_index_reuc_entrycount(repo_index) == 0);
......
...@@ -4,3 +4,5 @@ ...@@ -4,3 +4,5 @@
bare = false bare = false
logallrefupdates = true logallrefupdates = true
ignorecase = true ignorecase = true
[submodule "submodule"]
url = ../submodule
[core]
repositoryformatversion = 0
filemode = false
bare = false
logallrefupdates = true
worktree = ../../../submodule
symlinks = false
ignorecase = true
hideDotFiles = dotGitOnly
[remote "origin"]
url = c:/Temp/TestRepos/submodule
fetch = +refs/heads/*:refs/remotes/origin/*
[branch "master"]
remote = origin
merge = refs/heads/master
# git ls-files --others --exclude-from=.git/info/exclude
# Lines that start with '#' are comments.
# For a project mostly in C, the following would be a good set of
# exclude patterns (uncomment them if you want to use them):
# *.[oa]
# *~
# pack-refs with: peeled
297aa6cd028b3336c7802c7a6f49143da4e1602d refs/remotes/origin/master
ae39c77c70cb6bad18bb471912460c4e1ba0f586 refs/remotes/origin/submodule-branch
e34ef1afe54eb526fd92eec66084125f340f1d65
...@@ -137,6 +137,203 @@ void test_revert_workdir__orphan(void) ...@@ -137,6 +137,203 @@ void test_revert_workdir__orphan(void)
git_commit_free(head); git_commit_free(head);
} }
/*
* revert the same commit twice (when the first reverts cleanly):
*
* git revert 2d440f2
* git revert 2d440f2
*/
void test_revert_workdir__again(void)
{
git_reference *head_ref;
git_commit *orig_head;
git_tree *reverted_tree;
git_oid reverted_tree_oid, reverted_commit_oid;
git_signature *signature;
struct merge_index_entry merge_index_entries[] = {
{ 0100644, "7731926a337c4eaba1e2187d90ebfa0a93659382", 0, "file1.txt" },
{ 0100644, "0ab09ea6d4c3634bdf6c221626d8b6f7dd890767", 0, "file2.txt" },
{ 0100644, "f4e107c230d08a60fb419d19869f1f282b272d9c", 0, "file3.txt" },
{ 0100644, "0f5bfcf58c558d865da6be0281d7795993646cee", 0, "file6.txt" },
};
cl_git_pass(git_repository_head(&head_ref, repo));
cl_git_pass(git_reference_peel((git_object **)&orig_head, head_ref, GIT_OBJ_COMMIT));
cl_git_pass(git_reset(repo, (git_object *)orig_head, GIT_RESET_HARD));
cl_git_pass(git_revert(repo, orig_head, NULL));
cl_assert(merge_test_index(repo_index, merge_index_entries, 4));
cl_git_pass(git_index_write_tree(&reverted_tree_oid, repo_index));
cl_git_pass(git_tree_lookup(&reverted_tree, repo, &reverted_tree_oid));
cl_git_pass(git_signature_new(&signature, "Reverter", "reverter@example.org", time(NULL), 0));
cl_git_pass(git_commit_create(&reverted_commit_oid, repo, "HEAD", signature, signature, NULL, "Reverted!", reverted_tree, 1, (const git_commit **)&orig_head));
cl_git_pass(git_revert(repo, orig_head, NULL));
cl_assert(merge_test_index(repo_index, merge_index_entries, 4));
git_signature_free(signature);
git_tree_free(reverted_tree);
git_commit_free(orig_head);
git_reference_free(head_ref);
}
/* git reset --hard 72333f47d4e83616630ff3b0ffe4c0faebcc3c45
* git revert --no-commit d1d403d22cbe24592d725f442835cf46fe60c8ac */
void test_revert_workdir__again_after_automerge(void)
{
git_commit *head, *commit;
git_tree *reverted_tree;
git_oid head_oid, revert_oid, reverted_tree_oid, reverted_commit_oid;
git_signature *signature;
struct merge_index_entry merge_index_entries[] = {
{ 0100644, "caf99de3a49827117bb66721010eac461b06a80c", 0, "file1.txt" },
{ 0100644, "0ab09ea6d4c3634bdf6c221626d8b6f7dd890767", 0, "file2.txt" },
{ 0100644, "f4e107c230d08a60fb419d19869f1f282b272d9c", 0, "file3.txt" },
{ 0100644, "0f5bfcf58c558d865da6be0281d7795993646cee", 0, "file6.txt" },
};
struct merge_index_entry second_revert_entries[] = {
{ 0100644, "3a3ef367eaf3fe79effbfb0a56b269c04c2b59fe", 1, "file1.txt" },
{ 0100644, "caf99de3a49827117bb66721010eac461b06a80c", 2, "file1.txt" },
{ 0100644, "747726e021bc5f44b86de60e3032fd6f9f1b8383", 3, "file1.txt" },
{ 0100644, "0ab09ea6d4c3634bdf6c221626d8b6f7dd890767", 0, "file2.txt" },
{ 0100644, "f4e107c230d08a60fb419d19869f1f282b272d9c", 0, "file3.txt" },
{ 0100644, "0f5bfcf58c558d865da6be0281d7795993646cee", 0, "file6.txt" },
};
git_oid_fromstr(&head_oid, "72333f47d4e83616630ff3b0ffe4c0faebcc3c45");
cl_git_pass(git_commit_lookup(&head, repo, &head_oid));
cl_git_pass(git_reset(repo, (git_object *)head, GIT_RESET_HARD));
git_oid_fromstr(&revert_oid, "d1d403d22cbe24592d725f442835cf46fe60c8ac");
cl_git_pass(git_commit_lookup(&commit, repo, &revert_oid));
cl_git_pass(git_revert(repo, commit, NULL));
cl_assert(merge_test_index(repo_index, merge_index_entries, 4));
cl_git_pass(git_index_write_tree(&reverted_tree_oid, repo_index));
cl_git_pass(git_tree_lookup(&reverted_tree, repo, &reverted_tree_oid));
cl_git_pass(git_signature_new(&signature, "Reverter", "reverter@example.org", time(NULL), 0));
cl_git_pass(git_commit_create(&reverted_commit_oid, repo, "HEAD", signature, signature, NULL, "Reverted!", reverted_tree, 1, (const git_commit **)&head));
cl_git_pass(git_revert(repo, commit, NULL));
cl_assert(merge_test_index(repo_index, second_revert_entries, 6));
git_signature_free(signature);
git_tree_free(reverted_tree);
git_commit_free(commit);
git_commit_free(head);
}
/*
* revert the same commit twice (when the first reverts cleanly):
*
* git revert 2d440f2
* git revert 2d440f2
*/
void test_revert_workdir__again_after_edit(void)
{
git_reference *head_ref;
git_commit *orig_head, *commit;
git_tree *reverted_tree;
git_oid orig_head_oid, revert_oid, reverted_tree_oid, reverted_commit_oid;
git_signature *signature;
struct merge_index_entry merge_index_entries[] = {
{ 0100644, "3721552e06c4bdc7d478e0674e6304888545d5fd", 0, "file1.txt" },
{ 0100644, "0ab09ea6d4c3634bdf6c221626d8b6f7dd890767", 0, "file2.txt" },
{ 0100644, "f4e107c230d08a60fb419d19869f1f282b272d9c", 0, "file3.txt" },
{ 0100644, "0f5bfcf58c558d865da6be0281d7795993646cee", 0, "file6.txt" },
};
cl_git_pass(git_repository_head(&head_ref, repo));
cl_git_pass(git_oid_fromstr(&orig_head_oid, "399fb3aba3d9d13f7d40a9254ce4402067ef3149"));
cl_git_pass(git_commit_lookup(&orig_head, repo, &orig_head_oid));
cl_git_pass(git_reset(repo, (git_object *)orig_head, GIT_RESET_HARD));
cl_git_pass(git_oid_fromstr(&revert_oid, "2d440f2b3147d3dc7ad1085813478d6d869d5a4d"));
cl_git_pass(git_commit_lookup(&commit, repo, &revert_oid));
cl_git_pass(git_revert(repo, commit, NULL));
cl_assert(merge_test_index(repo_index, merge_index_entries, 4));
cl_git_pass(git_index_write_tree(&reverted_tree_oid, repo_index));
cl_git_pass(git_tree_lookup(&reverted_tree, repo, &reverted_tree_oid));
cl_git_pass(git_signature_new(&signature, "Reverter", "reverter@example.org", time(NULL), 0));
cl_git_pass(git_commit_create(&reverted_commit_oid, repo, "HEAD", signature, signature, NULL, "Reverted!", reverted_tree, 1, (const git_commit **)&orig_head));
cl_git_pass(git_revert(repo, commit, NULL));
cl_assert(merge_test_index(repo_index, merge_index_entries, 4));
git_signature_free(signature);
git_tree_free(reverted_tree);
git_commit_free(commit);
git_commit_free(orig_head);
git_reference_free(head_ref);
}
/*
* revert the same commit twice (when the first reverts cleanly):
*
* git reset --hard e34ef1a
* git revert 71eb9c2
*/
void test_revert_workdir__again_after_edit_two(void)
{
git_buf diff_buf = GIT_BUF_INIT;
git_config *config;
git_oid head_commit_oid, revert_commit_oid;
git_commit *head_commit, *revert_commit;
struct merge_index_entry merge_index_entries[] = {
{ 0100644, "1ff0c423042b46cb1d617b81efb715defbe8054d", 0, ".gitattributes" },
{ 0100644, "1bc915c5cb7185a9438de28a7b1a7dfe8c01ee7f", 0, ".gitignore" },
{ 0100644, "a8c86221b400b836010567cc3593db6e96c1a83a", 1, "file.txt" },
{ 0100644, "46ff0854663aeb2182b9838c8da68e33ac23bc1e", 2, "file.txt" },
{ 0100644, "21a96a98ed84d45866e1de6e266fd3a61a4ae9dc", 3, "file.txt" },
};
cl_git_pass(git_repository_config(&config, repo));
cl_git_pass(git_config_set_bool(config, "core.autocrlf", 0));
cl_git_pass(git_oid_fromstr(&head_commit_oid, "e34ef1afe54eb526fd92eec66084125f340f1d65"));
cl_git_pass(git_commit_lookup(&head_commit, repo, &head_commit_oid));
cl_git_pass(git_reset(repo, (git_object *)head_commit, GIT_RESET_HARD));
cl_git_pass(git_oid_fromstr(&revert_commit_oid, "71eb9c2b53dbbf3c45fb28b27c850db4b7fb8011"));
cl_git_pass(git_commit_lookup(&revert_commit, repo, &revert_commit_oid));
cl_git_pass(git_revert(repo, revert_commit, NULL));
cl_assert(merge_test_index(repo_index, merge_index_entries, 5));
cl_git_pass(git_futils_readbuffer(&diff_buf, "revert/file.txt"));
cl_assert(strcmp(diff_buf.ptr, "a\n" \
"<<<<<<< HEAD\n" \
"=======\n" \
"a\n" \
">>>>>>> parent of 71eb9c2... revert me\n" \
"a\n" \
"a\n" \
"a\n" \
"a\n" \
"ab\n") == 0);
git_commit_free(revert_commit);
git_commit_free(head_commit);
git_config_free(config);
git_buf_free(&diff_buf);
}
/* git reset --hard 72333f47d4e83616630ff3b0ffe4c0faebcc3c45 /* git reset --hard 72333f47d4e83616630ff3b0ffe4c0faebcc3c45
* git revert --no-commit d1d403d22cbe24592d725f442835cf46fe60c8ac */ * git revert --no-commit d1d403d22cbe24592d725f442835cf46fe60c8ac */
void test_revert_workdir__conflict_use_ours(void) void test_revert_workdir__conflict_use_ours(void)
...@@ -146,22 +343,19 @@ void test_revert_workdir__conflict_use_ours(void) ...@@ -146,22 +343,19 @@ void test_revert_workdir__conflict_use_ours(void)
git_revert_opts opts = GIT_REVERT_OPTS_INIT; git_revert_opts opts = GIT_REVERT_OPTS_INIT;
struct merge_index_entry merge_index_entries[] = { struct merge_index_entry merge_index_entries[] = {
{ 0100644, "3a3ef367eaf3fe79effbfb0a56b269c04c2b59fe", 1, "file1.txt" }, { 0100644, "caf99de3a49827117bb66721010eac461b06a80c", 0, "file1.txt" },
{ 0100644, "7731926a337c4eaba1e2187d90ebfa0a93659382", 2, "file1.txt" },
{ 0100644, "747726e021bc5f44b86de60e3032fd6f9f1b8383", 3, "file1.txt" },
{ 0100644, "0ab09ea6d4c3634bdf6c221626d8b6f7dd890767", 0, "file2.txt" }, { 0100644, "0ab09ea6d4c3634bdf6c221626d8b6f7dd890767", 0, "file2.txt" },
{ 0100644, "f4e107c230d08a60fb419d19869f1f282b272d9c", 0, "file3.txt" }, { 0100644, "f4e107c230d08a60fb419d19869f1f282b272d9c", 0, "file3.txt" },
{ 0100644, "0f5bfcf58c558d865da6be0281d7795993646cee", 0, "file6.txt" }, { 0100644, "0f5bfcf58c558d865da6be0281d7795993646cee", 0, "file6.txt" },
}; };
struct merge_index_entry merge_filesystem_entries[] = { struct merge_index_entry merge_filesystem_entries[] = {
{ 0100644, "7731926a337c4eaba1e2187d90ebfa0a93659382", 0, "file1.txt" }, { 0100644, "caf99de3a49827117bb66721010eac461b06a80c", 0, "file1.txt" },
{ 0100644, "0ab09ea6d4c3634bdf6c221626d8b6f7dd890767", 0, "file2.txt" }, { 0100644, "0ab09ea6d4c3634bdf6c221626d8b6f7dd890767", 0, "file2.txt" },
{ 0100644, "f4e107c230d08a60fb419d19869f1f282b272d9c", 0, "file3.txt" }, { 0100644, "f4e107c230d08a60fb419d19869f1f282b272d9c", 0, "file3.txt" },
{ 0100644, "0f5bfcf58c558d865da6be0281d7795993646cee", 0, "file6.txt" }, { 0100644, "0f5bfcf58c558d865da6be0281d7795993646cee", 0, "file6.txt" },
}; };
opts.merge_tree_opts.automerge_flags = GIT_MERGE_AUTOMERGE_NONE;
opts.checkout_opts.checkout_strategy = GIT_CHECKOUT_SAFE | GIT_CHECKOUT_USE_OURS; opts.checkout_opts.checkout_strategy = GIT_CHECKOUT_SAFE | GIT_CHECKOUT_USE_OURS;
git_oid_fromstr(&head_oid, "72333f47d4e83616630ff3b0ffe4c0faebcc3c45"); git_oid_fromstr(&head_oid, "72333f47d4e83616630ff3b0ffe4c0faebcc3c45");
...@@ -172,7 +366,7 @@ void test_revert_workdir__conflict_use_ours(void) ...@@ -172,7 +366,7 @@ void test_revert_workdir__conflict_use_ours(void)
cl_git_pass(git_commit_lookup(&commit, repo, &revert_oid)); cl_git_pass(git_commit_lookup(&commit, repo, &revert_oid));
cl_git_pass(git_revert(repo, commit, &opts)); cl_git_pass(git_revert(repo, commit, &opts));
cl_assert(merge_test_index(repo_index, merge_index_entries, 6)); cl_assert(merge_test_index(repo_index, merge_index_entries, 4));
cl_assert(merge_test_workdir(repo, merge_filesystem_entries, 4)); cl_assert(merge_test_workdir(repo, merge_filesystem_entries, 4));
git_commit_free(commit); git_commit_free(commit);
......
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