| /* |
| * Copyright (C) the libgit2 contributors. All rights reserved. |
| * |
| * This file is part of libgit2, distributed under the GNU GPL v2 with |
| * a Linking Exception. For full terms see the included COPYING file. |
| */ |
| |
| #include "common.h" |
| #include "repository.h" |
| #include "merge_file.h" |
| |
| #include "git2/repository.h" |
| #include "git2/object.h" |
| #include "git2/index.h" |
| |
| #include "xdiff/xdiff.h" |
| |
| #define GIT_MERGE_FILE_SIDE_EXISTS(X) ((X)->mode != 0) |
| |
| GIT_INLINE(const char *) merge_file_best_path( |
| const git_merge_file_input *ancestor, |
| const git_merge_file_input *ours, |
| const git_merge_file_input *theirs) |
| { |
| if (!GIT_MERGE_FILE_SIDE_EXISTS(ancestor)) { |
| if (strcmp(ours->path, theirs->path) == 0) |
| return ours->path; |
| |
| return NULL; |
| } |
| |
| if (strcmp(ancestor->path, ours->path) == 0) |
| return theirs->path; |
| else if(strcmp(ancestor->path, theirs->path) == 0) |
| return ours->path; |
| |
| return NULL; |
| } |
| |
| GIT_INLINE(int) merge_file_best_mode( |
| const git_merge_file_input *ancestor, |
| const git_merge_file_input *ours, |
| const git_merge_file_input *theirs) |
| { |
| /* |
| * If ancestor didn't exist and either ours or theirs is executable, |
| * assume executable. Otherwise, if any mode changed from the ancestor, |
| * use that one. |
| */ |
| if (!GIT_MERGE_FILE_SIDE_EXISTS(ancestor)) { |
| if (ours->mode == GIT_FILEMODE_BLOB_EXECUTABLE || |
| theirs->mode == GIT_FILEMODE_BLOB_EXECUTABLE) |
| return GIT_FILEMODE_BLOB_EXECUTABLE; |
| |
| return GIT_FILEMODE_BLOB; |
| } |
| |
| if (ancestor->mode == ours->mode) |
| return theirs->mode; |
| else if(ancestor->mode == theirs->mode) |
| return ours->mode; |
| |
| return 0; |
| } |
| |
| int git_merge_file_input_from_index_entry( |
| git_merge_file_input *input, |
| git_repository *repo, |
| const git_index_entry *entry) |
| { |
| git_odb *odb = NULL; |
| int error = 0; |
| |
| assert(input && repo && entry); |
| |
| if (entry->mode == 0) |
| return 0; |
| |
| if ((error = git_repository_odb(&odb, repo)) < 0 || |
| (error = git_odb_read(&input->odb_object, odb, &entry->oid)) < 0) |
| goto done; |
| |
| input->mode = entry->mode; |
| input->path = git__strdup(entry->path); |
| input->mmfile.size = git_odb_object_size(input->odb_object); |
| input->mmfile.ptr = (char *)git_odb_object_data(input->odb_object); |
| |
| if (input->label == NULL) |
| input->label = entry->path; |
| |
| done: |
| git_odb_free(odb); |
| |
| return error; |
| } |
| |
| int git_merge_file_input_from_diff_file( |
| git_merge_file_input *input, |
| git_repository *repo, |
| const git_diff_file *file) |
| { |
| git_odb *odb = NULL; |
| int error = 0; |
| |
| assert(input && repo && file); |
| |
| if (file->mode == 0) |
| return 0; |
| |
| if ((error = git_repository_odb(&odb, repo)) < 0 || |
| (error = git_odb_read(&input->odb_object, odb, &file->oid)) < 0) |
| goto done; |
| |
| input->mode = file->mode; |
| input->path = git__strdup(file->path); |
| input->mmfile.size = git_odb_object_size(input->odb_object); |
| input->mmfile.ptr = (char *)git_odb_object_data(input->odb_object); |
| |
| if (input->label == NULL) |
| input->label = file->path; |
| |
| done: |
| git_odb_free(odb); |
| |
| return error; |
| } |
| |
| int git_merge_files( |
| git_merge_file_result *out, |
| git_merge_file_input *ancestor, |
| git_merge_file_input *ours, |
| git_merge_file_input *theirs, |
| git_merge_automerge_flags flags) |
| { |
| xmparam_t xmparam; |
| mmbuffer_t mmbuffer; |
| int xdl_result; |
| int error = 0; |
| |
| assert(out && ancestor && ours && theirs); |
| |
| memset(out, 0x0, sizeof(git_merge_file_result)); |
| |
| if (!GIT_MERGE_FILE_SIDE_EXISTS(ours) || !GIT_MERGE_FILE_SIDE_EXISTS(theirs)) |
| return 0; |
| |
| memset(&xmparam, 0x0, sizeof(xmparam_t)); |
| xmparam.ancestor = ancestor->label; |
| xmparam.file1 = ours->label; |
| xmparam.file2 = theirs->label; |
| |
| out->path = merge_file_best_path(ancestor, ours, theirs); |
| out->mode = merge_file_best_mode(ancestor, ours, theirs); |
| |
| if (flags == GIT_MERGE_AUTOMERGE_FAVOR_OURS) |
| xmparam.favor = XDL_MERGE_FAVOR_OURS; |
| |
| if (flags == GIT_MERGE_AUTOMERGE_FAVOR_THEIRS) |
| xmparam.favor = XDL_MERGE_FAVOR_THEIRS; |
| |
| if ((xdl_result = xdl_merge(&ancestor->mmfile, &ours->mmfile, |
| &theirs->mmfile, &xmparam, &mmbuffer)) < 0) { |
| giterr_set(GITERR_MERGE, "Failed to merge files."); |
| error = -1; |
| goto done; |
| } |
| |
| out->automergeable = (xdl_result == 0); |
| out->data = (unsigned char *)mmbuffer.ptr; |
| out->len = mmbuffer.size; |
| |
| done: |
| return error; |
| } |