diff options
Diffstat (limited to 'src')
-rw-r--r-- | src/checkout.c | 43 | ||||
-rw-r--r-- | src/index.c | 8 | ||||
-rw-r--r-- | src/index.h | 2 | ||||
-rw-r--r-- | src/merge.c | 55 | ||||
-rw-r--r-- | src/merge_file.c | 263 | ||||
-rw-r--r-- | src/merge_file.h | 78 |
6 files changed, 243 insertions, 206 deletions
diff --git a/src/checkout.c b/src/checkout.c index 5dd4ec71c..f882f3593 100644 --- a/src/checkout.c +++ b/src/checkout.c @@ -1681,29 +1681,20 @@ 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; - git_merge_file_result result = GIT_MERGE_FILE_RESULT_INIT; + git_merge_file_options opts = GIT_MERGE_FILE_OPTIONS_INIT; + git_merge_file_result result = {0}; 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) || - (error = git_merge_file_input_from_index_entry( - &ours, data->repo, conflict->ours)) < 0 || - (error = git_merge_file_input_from_index_entry( - &theirs, data->repo, conflict->theirs)) < 0) - goto done; + opts.flags |= GIT_MERGE_FILE_STYLE_DIFF3; - 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"; + opts.ancestor_label = data->opts.ancestor_label ? + data->opts.ancestor_label : "ancestor"; + opts.our_label = data->opts.our_label ? + data->opts.our_label : "ours"; + opts.their_label = data->opts.their_label ? + data->opts.their_label : "theirs"; /* If all the paths are identical, decorate the diff3 file with the branch * names. Otherwise, append branch_name:path. @@ -1712,16 +1703,17 @@ static int checkout_write_merge( strcmp(conflict->ours->path, conflict->theirs->path) != 0) { if ((error = conflict_entry_name( - &our_label, ours.label, conflict->ours->path)) < 0 || + &our_label, opts.our_label, conflict->ours->path)) < 0 || (error = conflict_entry_name( - &their_label, theirs.label, conflict->theirs->path)) < 0) + &their_label, opts.their_label, conflict->theirs->path)) < 0) goto done; - ours.label = git_buf_cstr(&our_label); - theirs.label = git_buf_cstr(&their_label); + opts.our_label = git_buf_cstr(&our_label); + opts.their_label = git_buf_cstr(&their_label); } - if ((error = git_merge_files(&result, &ancestor, &ours, &theirs, &merge_file_opts)) < 0) + if ((error = git_merge_file_from_index(&result, data->repo, + conflict->ancestor, conflict->ours, conflict->theirs, &opts)) < 0) goto done; if (result.path == NULL || result.mode == 0) { @@ -1739,7 +1731,7 @@ static int checkout_write_merge( if ((error = git_futils_mkpath2file(path_workdir.ptr, 0755)) < 0 || (error = git_filebuf_open(&output, path_workdir.ptr, GIT_FILEBUF_DO_NOT_BUFFER, result.mode)) < 0 || - (error = git_filebuf_write(&output, result.data, result.len)) < 0 || + (error = git_filebuf_write(&output, result.ptr, result.len)) < 0 || (error = git_filebuf_commit(&output)) < 0) goto done; @@ -1747,9 +1739,6 @@ done: git_buf_free(&our_label); git_buf_free(&their_label); - git_merge_file_input_free(&ancestor); - git_merge_file_input_free(&ours); - git_merge_file_input_free(&theirs); git_merge_file_result_free(&result); git_buf_free(&path_workdir); git_buf_free(&path_suffixed); diff --git a/src/index.c b/src/index.c index 0d7d50668..ea0815e4c 100644 --- a/src/index.c +++ b/src/index.c @@ -300,7 +300,7 @@ static void index_entry_free(git_index_entry *entry) git__free(entry); } -static unsigned int index_create_mode(unsigned int mode) +unsigned int git_index__create_mode(unsigned int mode) { if (S_ISLNK(mode)) return S_IFLNK; @@ -320,9 +320,9 @@ static unsigned int index_merge_mode( if (index->distrust_filemode && S_ISREG(mode)) return (existing && S_ISREG(existing->mode)) ? - existing->mode : index_create_mode(0666); + existing->mode : git_index__create_mode(0666); - return index_create_mode(mode); + return git_index__create_mode(mode); } void git_index__set_ignore_case(git_index *index, bool ignore_case) @@ -619,7 +619,7 @@ void git_index_entry__init_from_stat( entry->dev = st->st_rdev; entry->ino = st->st_ino; entry->mode = (!trust_mode && S_ISREG(st->st_mode)) ? - index_create_mode(0666) : index_create_mode(st->st_mode); + git_index__create_mode(0666) : git_index__create_mode(st->st_mode); entry->uid = st->st_uid; entry->gid = st->st_gid; entry->file_size = st->st_size; diff --git a/src/index.h b/src/index.h index f88d110f7..259a3149f 100644 --- a/src/index.h +++ b/src/index.h @@ -60,4 +60,6 @@ extern int git_index__find( extern void git_index__set_ignore_case(git_index *index, bool ignore_case); +extern unsigned int git_index__create_mode(unsigned int mode); + #endif diff --git a/src/merge.c b/src/merge.c index 124e8c3a7..d382a70bc 100644 --- a/src/merge.c +++ b/src/merge.c @@ -514,11 +514,9 @@ static int merge_conflict_resolve_automerge( const git_merge_diff *conflict, 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; - git_merge_file_result result = GIT_MERGE_FILE_RESULT_INIT; + const git_index_entry *ancestor = NULL, *ours = NULL, *theirs = NULL; + git_merge_file_options opts = GIT_MERGE_FILE_OPTIONS_INIT; + git_merge_file_result result = {0}; git_index_entry *index_entry; git_odb *odb = NULL; git_oid automerge_oid; @@ -528,7 +526,9 @@ static int merge_conflict_resolve_automerge( *resolved = 0; - merge_file_opts.favor = merge_file_favor; + if (!GIT_MERGE_INDEX_ENTRY_EXISTS(conflict->our_entry) || + !GIT_MERGE_INDEX_ENTRY_EXISTS(conflict->their_entry)) + return 0; /* Reject D/F conflicts */ if (conflict->type == GIT_MERGE_DIFF_DIRECTORY_FILE) @@ -559,13 +559,19 @@ static int merge_conflict_resolve_automerge( if (conflict->binary) return 0; + ancestor = GIT_MERGE_INDEX_ENTRY_EXISTS(conflict->ancestor_entry) ? + &conflict->ancestor_entry : NULL; + ours = GIT_MERGE_INDEX_ENTRY_EXISTS(conflict->our_entry) ? + &conflict->our_entry : NULL; + theirs = GIT_MERGE_INDEX_ENTRY_EXISTS(conflict->their_entry) ? + &conflict->their_entry : NULL; + + opts.favor = merge_file_favor; + 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, &merge_file_opts)) < 0 || + (error = git_merge_file_from_index(&result, diff_list->repo, ancestor, ours, theirs, &opts)) < 0 || !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.ptr, result.len, GIT_OBJ_BLOB)) < 0) goto done; if ((index_entry = git_pool_malloc(&diff_list->pool, sizeof(git_index_entry))) == NULL) @@ -584,9 +590,6 @@ static int merge_conflict_resolve_automerge( *resolved = 1; done: - git_merge_file_input_free(&ancestor); - git_merge_file_input_free(&ours); - git_merge_file_input_free(&theirs); git_merge_file_result_free(&result); git_odb_free(odb); @@ -2768,3 +2771,27 @@ int git_merge_tree_init_opts(git_merge_tree_opts* opts, int version) return 0; } } + +int git_merge_file_init_input(git_merge_file_input *input, int version) +{ + if (version != GIT_MERGE_FILE_INPUT_VERSION) { + giterr_set(GITERR_INVALID, "Invalid version %d for git_merge_file_input", version); + return -1; + } else { + git_merge_file_input i = GIT_MERGE_FILE_INPUT_INIT; + memcpy(input, &i, sizeof(i)); + return 0; + } +} + +int git_merge_file_init_options(git_merge_file_options *opts, int version) +{ + if (version != GIT_MERGE_FILE_OPTIONS_VERSION) { + giterr_set(GITERR_INVALID, "Invalid version %d for git_merge_file_options", version); + return -1; + } else { + git_merge_file_options o = GIT_MERGE_FILE_OPTIONS_INIT; + memcpy(opts, &o, sizeof(o)); + return 0; + } +} diff --git a/src/merge_file.c b/src/merge_file.c index 986fbf9fe..fc45cbfbf 100644 --- a/src/merge_file.c +++ b/src/merge_file.c @@ -8,6 +8,9 @@ #include "common.h" #include "repository.h" #include "merge_file.h" +#include "posix.h" +#include "fileops.h" +#include "index.h" #include "git2/repository.h" #include "git2/object.h" @@ -22,17 +25,17 @@ GIT_INLINE(const char *) merge_file_best_path( 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) + if (!ancestor) { + if (ours && theirs && 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; + if (ours && strcmp(ancestor->path, ours->path) == 0) + return theirs ? theirs->path : NULL; + else if(theirs && strcmp(ancestor->path, theirs->path) == 0) + return ours ? ours->path : NULL; return NULL; } @@ -47,136 +50,230 @@ GIT_INLINE(int) merge_file_best_mode( * 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) + if (!ancestor) { + if ((ours && ours->mode == GIT_FILEMODE_BLOB_EXECUTABLE) || + (theirs && theirs->mode == GIT_FILEMODE_BLOB_EXECUTABLE)) return GIT_FILEMODE_BLOB_EXECUTABLE; return GIT_FILEMODE_BLOB; - } + } else if (ours && theirs) { + if (ancestor->mode == ours->mode) + return theirs->mode; - 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, +int git_merge_file__input_from_index( + git_merge_file_input *input_out, + git_odb_object **odb_object_out, + git_odb *odb, const git_index_entry *entry) { - git_odb *odb = NULL; int error = 0; - assert(input && repo && entry); - - if (entry->mode == 0) - return 0; + assert(input_out && odb_object_out && odb && entry); - if ((error = git_repository_odb(&odb, repo)) < 0 || - (error = git_odb_read(&input->odb_object, odb, &entry->id)) < 0) + if ((error = git_odb_read(odb_object_out, odb, &entry->id)) < 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; + input_out->path = entry->path; + input_out->mode = entry->mode; + input_out->ptr = (char *)git_odb_object_data(*odb_object_out); + input_out->size = git_odb_object_size(*odb_object_out); 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) +static void merge_file_normalize_opts( + git_merge_file_options *out, + const git_merge_file_options *given_opts) { - 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->id)) < 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; + if (given_opts) + memcpy(out, given_opts, sizeof(git_merge_file_options)); + else { + git_merge_file_options default_opts = GIT_MERGE_FILE_OPTIONS_INIT; + memcpy(out, &default_opts, sizeof(git_merge_file_options)); + } } -int git_merge_files( +static int git_merge_file__from_inputs( git_merge_file_result *out, - git_merge_file_input *ancestor, - git_merge_file_input *ours, - git_merge_file_input *theirs, - git_merge_file_options *opts) + const git_merge_file_input *ancestor, + const git_merge_file_input *ours, + const git_merge_file_input *theirs, + const git_merge_file_options *given_opts) { xmparam_t xmparam; + mmfile_t ancestor_mmfile = {0}, our_mmfile = {0}, their_mmfile = {0}; mmbuffer_t mmbuffer; + git_merge_file_options options = GIT_MERGE_FILE_OPTIONS_INIT; + const char *path; 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; + merge_file_normalize_opts(&options, given_opts); 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 (ancestor) { + xmparam.ancestor = (options.ancestor_label) ? + options.ancestor_label : ancestor->path; + ancestor_mmfile.ptr = (char *)ancestor->ptr; + ancestor_mmfile.size = ancestor->size; + } - if (opts && opts->favor == GIT_MERGE_FILE_FAVOR_OURS) + xmparam.file1 = (options.our_label) ? + options.our_label : ours->path; + our_mmfile.ptr = (char *)ours->ptr; + our_mmfile.size = ours->size; + + xmparam.file2 = (options.their_label) ? + options.their_label : theirs->path; + their_mmfile.ptr = (char *)theirs->ptr; + their_mmfile.size = theirs->size; + + if (options.favor == GIT_MERGE_FILE_FAVOR_OURS) xmparam.favor = XDL_MERGE_FAVOR_OURS; - else if (opts && opts->favor == GIT_MERGE_FILE_FAVOR_THEIRS) + else if (options.favor == GIT_MERGE_FILE_FAVOR_THEIRS) xmparam.favor = XDL_MERGE_FAVOR_THEIRS; - else if (opts && opts->favor == GIT_MERGE_FILE_FAVOR_UNION) + else if (options.favor == GIT_MERGE_FILE_FAVOR_UNION) xmparam.favor = XDL_MERGE_FAVOR_UNION; - xmparam.level = - (opts && (opts->flags & GIT_MERGE_FILE_SIMPLIFY_ALNUM)) ? + xmparam.level = (options.flags & GIT_MERGE_FILE_SIMPLIFY_ALNUM) ? XDL_MERGE_ZEALOUS_ALNUM : XDL_MERGE_ZEALOUS; - if (opts && opts->style == GIT_MERGE_FILE_STYLE_DIFF3) + if (options.flags & GIT_MERGE_FILE_STYLE_DIFF3) xmparam.style = XDL_MERGE_DIFF3; - if ((xdl_result = xdl_merge(&ancestor->mmfile, &ours->mmfile, - &theirs->mmfile, &xmparam, &mmbuffer)) < 0) { + if ((xdl_result = xdl_merge(&ancestor_mmfile, &our_mmfile, + &their_mmfile, &xmparam, &mmbuffer)) < 0) { giterr_set(GITERR_MERGE, "Failed to merge files."); error = -1; goto done; } + if ((path = merge_file_best_path(ancestor, ours, theirs)) != NULL && + (out->path = strdup(path)) == NULL) { + error = -1; + goto done; + } + out->automergeable = (xdl_result == 0); - out->data = (unsigned char *)mmbuffer.ptr; + out->ptr = (unsigned char *)mmbuffer.ptr; out->len = mmbuffer.size; + out->mode = merge_file_best_mode(ancestor, ours, theirs); done: + if (error < 0) + git_merge_file_result_free(out); + return error; } + +static git_merge_file_input *git_merge_file__normalize_inputs( + git_merge_file_input *out, + const git_merge_file_input *given) +{ + memcpy(out, given, sizeof(git_merge_file_input)); + + if (!out->path) + out->path = "file.txt"; + + if (!out->mode) + out->mode = 0100644; + + return out; +} + +int git_merge_file( + git_merge_file_result *out, + const git_merge_file_input *ancestor, + const git_merge_file_input *ours, + const git_merge_file_input *theirs, + const git_merge_file_options *options) +{ + git_merge_file_input inputs[3] = { {0} }; + + assert(out && ours && theirs); + + memset(out, 0x0, sizeof(git_merge_file_result)); + + if (ancestor) + ancestor = git_merge_file__normalize_inputs(&inputs[0], ancestor); + + ours = git_merge_file__normalize_inputs(&inputs[1], ours); + theirs = git_merge_file__normalize_inputs(&inputs[2], theirs); + + return git_merge_file__from_inputs(out, ancestor, ours, theirs, options); +} + +int git_merge_file_from_index( + git_merge_file_result *out, + git_repository *repo, + const git_index_entry *ancestor, + const git_index_entry *ours, + const git_index_entry *theirs, + const git_merge_file_options *options) +{ + git_merge_file_input inputs[3] = { {0} }, + *ancestor_input = NULL, *our_input = NULL, *their_input = NULL; + git_odb *odb = NULL; + git_odb_object *odb_object[3] = { 0 }; + int error = 0; + + assert(out && repo && ours && theirs); + + memset(out, 0x0, sizeof(git_merge_file_result)); + + if ((error = git_repository_odb(&odb, repo)) < 0) + goto done; + + if (ancestor) { + if ((error = git_merge_file__input_from_index( + &inputs[0], &odb_object[0], odb, ancestor)) < 0) + goto done; + + ancestor_input = &inputs[0]; + } + + if ((error = git_merge_file__input_from_index( + &inputs[1], &odb_object[1], odb, ours)) < 0) + goto done; + + our_input = &inputs[1]; + + if ((error = git_merge_file__input_from_index( + &inputs[2], &odb_object[2], odb, theirs)) < 0) + goto done; + + their_input = &inputs[2]; + + if ((error = git_merge_file__from_inputs(out, + ancestor_input, our_input, their_input, options)) < 0) + goto done; + +done: + git_odb_object_free(odb_object[0]); + git_odb_object_free(odb_object[1]); + git_odb_object_free(odb_object[2]); + git_odb_free(odb); + + return error; +} + +void git_merge_file_result_free(git_merge_file_result *result) +{ + if (result == NULL) + return; + + git__free(result->path); + + /* xdiff uses malloc() not git_malloc, so we use free(), not git_free() */ + free(result->ptr); +} diff --git a/src/merge_file.h b/src/merge_file.h index 332be490b..263391ee3 100644 --- a/src/merge_file.h +++ b/src/merge_file.h @@ -11,82 +11,4 @@ #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} - -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, - 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_file_options *opts); - -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 |