diff options
Diffstat (limited to 'src')
-rw-r--r-- | src/checkout.c | 95 | ||||
-rw-r--r-- | src/merge.c | 101 | ||||
-rw-r--r-- | src/merge.h | 2 | ||||
-rw-r--r-- | src/merge_file.c | 16 | ||||
-rw-r--r-- | src/merge_file.h | 23 |
5 files changed, 208 insertions, 29 deletions
diff --git a/src/checkout.c b/src/checkout.c index e642c975e..cfb0e72ab 100644 --- a/src/checkout.c +++ b/src/checkout.c @@ -70,7 +70,9 @@ typedef struct { int name_collision:1, directoryfile:1, - one_to_two:1; + one_to_two:1, + binary:1, + submodule:1; } checkout_conflictdata; static int checkout_notify( @@ -681,6 +683,51 @@ GIT_INLINE(bool) conflict_pathspec_match( 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) { git_index_conflict_iterator *iterator = NULL; @@ -705,6 +752,10 @@ static int checkout_conflicts_load(checkout_data *data, git_iterator *workdir, g conflict->ours = ours; 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); } @@ -1626,6 +1677,7 @@ static int checkout_write_merge( { git_buf our_label = GIT_BUF_INIT, their_label = 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, ours = GIT_MERGE_FILE_INPUT_INIT, theirs = GIT_MERGE_FILE_INPUT_INIT; @@ -1633,6 +1685,9 @@ static int checkout_write_merge( git_filebuf output = GIT_FILEBUF_INIT; 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 && (error = git_merge_file_input_from_index_entry( &ancestor, data->repo, conflict->ancestor)) < 0) || @@ -1642,7 +1697,7 @@ static int checkout_write_merge( &theirs, data->repo, conflict->theirs)) < 0) 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"; theirs.label = data->opts.their_label ? data->opts.their_label : "theirs"; @@ -1662,7 +1717,7 @@ static int checkout_write_merge( 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; if (result.path == NULL || result.mode == 0) { @@ -1705,6 +1760,7 @@ static int checkout_create_conflicts(checkout_data *data) int error = 0; git_vector_foreach(&data->conflicts, i, conflict) { + /* Both deleted: nothing to do */ if (conflict->ours == NULL && conflict->theirs == NULL) error = 0; @@ -1748,7 +1804,15 @@ static int checkout_create_conflicts(checkout_data *data) else if (S_ISLNK(conflict->theirs->mode)) 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); if (error) @@ -1891,6 +1955,29 @@ static int checkout_data_init( 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 || (error = git_vector_init(&data->conflicts, 0, NULL)) < 0 || (error = git_pool_init(&data->pool, 1, 0)) < 0 || diff --git a/src/merge.c b/src/merge.c index c0be37dd8..2fb1c5898 100644 --- a/src/merge.c +++ b/src/merge.c @@ -42,6 +42,7 @@ #include "git2/sys/index.h" #define GIT_MERGE_INDEX_ENTRY_EXISTS(X) ((X).mode != 0) +#define GIT_MERGE_INDEX_ENTRY_ISFILE(X) S_ISREG((X).mode) typedef enum { TREE_IDX_ANCESTOR = 0, @@ -447,7 +448,6 @@ static int merge_conflict_resolve_one_removed( return error; } - static int merge_conflict_resolve_one_renamed( int *resolved, git_merge_diff_list *diff_list, @@ -511,8 +511,9 @@ static int merge_conflict_resolve_automerge( int *resolved, git_merge_diff_list *diff_list, 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, ours = GIT_MERGE_FILE_INPUT_INIT, theirs = GIT_MERGE_FILE_INPUT_INIT; @@ -526,13 +527,18 @@ static int merge_conflict_resolve_automerge( *resolved = 0; - if (automerge_flags == GIT_MERGE_AUTOMERGE_NONE) - return 0; + merge_file_opts.favor = merge_file_favor; /* Reject D/F conflicts */ if (conflict->type == GIT_MERGE_DIFF_DIRECTORY_FILE) 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. */ 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))) @@ -548,11 +554,15 @@ static int merge_conflict_resolve_automerge( strcmp(conflict->ancestor_entry.path, conflict->their_entry.path) != 0) return 0; + /* Reject binary conflicts */ + if (conflict->binary) + return 0; + if ((error = git_repository_odb(&odb, diff_list->repo)) < 0 || (error = git_merge_file_input_from_index_entry(&ancestor, diff_list->repo, &conflict->ancestor_entry)) < 0 || (error = git_merge_file_input_from_index_entry(&ours, diff_list->repo, &conflict->our_entry)) < 0 || (error = git_merge_file_input_from_index_entry(&theirs, diff_list->repo, &conflict->their_entry)) < 0 || - (error = git_merge_files(&result, &ancestor, &ours, &theirs, automerge_flags)) < 0 || + (error = git_merge_files(&result, &ancestor, &ours, &theirs, &merge_file_opts)) < 0 || !result.automergeable || (error = git_odb_write(&automerge_oid, odb, result.data, result.len, GIT_OBJ_BLOB)) < 0) goto done; @@ -586,7 +596,7 @@ static int merge_conflict_resolve( int *out, git_merge_diff_list *diff_list, const git_merge_diff *conflict, - unsigned int automerge_flags) + unsigned int merge_file_favor) { int resolved = 0; int error = 0; @@ -596,16 +606,14 @@ static int merge_conflict_resolve( if ((error = merge_conflict_resolve_trivial(&resolved, diff_list, conflict)) < 0) goto done; - if (automerge_flags != GIT_MERGE_AUTOMERGE_NONE) { - if (!resolved && (error = merge_conflict_resolve_one_removed(&resolved, diff_list, conflict)) < 0) - goto done; + if (!resolved && (error = merge_conflict_resolve_one_removed(&resolved, diff_list, conflict)) < 0) + goto done; - if (!resolved && (error = merge_conflict_resolve_one_renamed(&resolved, diff_list, conflict)) < 0) - goto done; + if (!resolved && (error = merge_conflict_resolve_one_renamed(&resolved, diff_list, conflict)) < 0) + goto done; - if (!resolved && (error = merge_conflict_resolve_automerge(&resolved, diff_list, conflict, automerge_flags)) < 0) - goto done; - } + if (!resolved && (error = merge_conflict_resolve_automerge(&resolved, diff_list, conflict, merge_file_favor)) < 0) + goto done; *out = resolved; @@ -1147,6 +1155,44 @@ GIT_INLINE(int) merge_diff_detect_type( 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_index_entry *out, git_pool *pool, @@ -1218,6 +1264,7 @@ static int merge_diff_list_insert_conflict( if ((conflict = merge_diff_from_index_entries(diff_list, tree_items)) == NULL || merge_diff_detect_type(conflict) < 0 || merge_diff_detect_df_conflict(merge_df_data, conflict) < 0 || + merge_diff_detect_binary(diff_list->repo, conflict) < 0 || git_vector_insert(&diff_list->conflicts, conflict) < 0) return -1; @@ -1589,7 +1636,7 @@ int git_merge_trees( git_vector_foreach(&changes, i, conflict) { 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; if (!resolved) @@ -2119,6 +2166,8 @@ static int merge_normalize_opts( git_repository *repo, git_merge_opts *opts, const git_merge_opts *given, + const git_merge_head *ancestor_head, + const git_merge_head *our_head, size_t their_heads_len, const git_merge_head **their_heads) { @@ -2138,8 +2187,20 @@ static int merge_normalize_opts( if (!opts->checkout_opts.checkout_strategy) opts->checkout_opts.checkout_strategy = default_checkout_strategy; - if (!opts->checkout_opts.our_label) - opts->checkout_opts.our_label = "HEAD"; + /* TODO: for multiple ancestors in merge-recursive, this is "merged common ancestors" */ + 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 (their_heads_len == 1 && their_heads[0]->ref_name) @@ -2434,9 +2495,6 @@ int git_merge( their_trees = git__calloc(their_heads_len, sizeof(git_tree *)); 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) goto on_error; @@ -2448,6 +2506,9 @@ int git_merge( error != GIT_ENOTFOUND) 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 && ancestor_head != NULL && (merge_check_uptodate(result, ancestor_head, their_heads[0]) || diff --git a/src/merge.h b/src/merge.h index b240f6c44..dda023528 100644 --- a/src/merge.h +++ b/src/merge.h @@ -106,6 +106,8 @@ typedef struct { git_index_entry their_entry; git_delta_t their_status; + + int binary:1; } git_merge_diff; /** Internal structure for merge inputs */ diff --git a/src/merge_file.c b/src/merge_file.c index 48fc46e57..9961ef297 100644 --- a/src/merge_file.c +++ b/src/merge_file.c @@ -130,7 +130,7 @@ int git_merge_files( git_merge_file_input *ancestor, git_merge_file_input *ours, git_merge_file_input *theirs, - git_merge_automerge_flags flags) + git_merge_file_options *opts) { xmparam_t xmparam; mmbuffer_t mmbuffer; @@ -152,11 +152,19 @@ int git_merge_files( 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) + if (opts && opts->favor == GIT_MERGE_FILE_FAVOR_OURS) xmparam.favor = XDL_MERGE_FAVOR_OURS; - - if (flags == GIT_MERGE_AUTOMERGE_FAVOR_THEIRS) + else if (opts && opts->favor == GIT_MERGE_FILE_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, &theirs->mmfile, &xmparam, &mmbuffer)) < 0) { diff --git a/src/merge_file.h b/src/merge_file.h index 0af2f0a57..332be490b 100644 --- a/src/merge_file.h +++ b/src/merge_file.h @@ -34,6 +34,27 @@ typedef struct { #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( git_merge_file_input *input, git_repository *repo, @@ -49,7 +70,7 @@ int git_merge_files( git_merge_file_input *ancestor, git_merge_file_input *ours, 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) { |