Welcome to mirror list, hosted at ThFree Co, Russian Federation.

github.com/mono/libgit2.git - Unnamed repository; edit this file 'description' to name the repository.
summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
Diffstat (limited to 'src/merge_file.c')
-rw-r--r--src/merge_file.c261
1 files changed, 183 insertions, 78 deletions
diff --git a/src/merge_file.c b/src/merge_file.c
index 48fc46e57..ff0364432 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,128 +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->oid)) < 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;
+ 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));
+ }
+}
+
+static int git_merge_file__from_inputs(
+ 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 *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(input && repo && file);
+ memset(out, 0x0, sizeof(git_merge_file_result));
- if (file->mode == 0)
- return 0;
+ merge_file_normalize_opts(&options, given_opts);
- if ((error = git_repository_odb(&odb, repo)) < 0 ||
- (error = git_odb_read(&input->odb_object, odb, &file->oid)) < 0)
+ memset(&xmparam, 0x0, sizeof(xmparam_t));
+
+ if (ancestor) {
+ xmparam.ancestor = (options.ancestor_label) ?
+ options.ancestor_label : ancestor->path;
+ ancestor_mmfile.ptr = (char *)ancestor->ptr;
+ ancestor_mmfile.size = ancestor->size;
+ }
+
+ 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 (options.favor == GIT_MERGE_FILE_FAVOR_THEIRS)
+ xmparam.favor = XDL_MERGE_FAVOR_THEIRS;
+ else if (options.favor == GIT_MERGE_FILE_FAVOR_UNION)
+ xmparam.favor = XDL_MERGE_FAVOR_UNION;
+
+ xmparam.level = (options.flags & GIT_MERGE_FILE_SIMPLIFY_ALNUM) ?
+ XDL_MERGE_ZEALOUS_ALNUM : XDL_MERGE_ZEALOUS;
+
+ if (options.flags & GIT_MERGE_FILE_STYLE_DIFF3)
+ xmparam.style = XDL_MERGE_DIFF3;
+
+ 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;
+ }
- 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 ((path = merge_file_best_path(ancestor, ours, theirs)) != NULL &&
+ (out->path = strdup(path)) == NULL) {
+ error = -1;
+ goto done;
+ }
- if (input->label == NULL)
- input->label = file->path;
+ out->automergeable = (xdl_result == 0);
+ out->ptr = (const char *)mmbuffer.ptr;
+ out->len = mmbuffer.size;
+ out->mode = merge_file_best_mode(ancestor, ours, theirs);
done:
- git_odb_free(odb);
+ if (error < 0)
+ git_merge_file_result_free(out);
return error;
}
-int git_merge_files(
+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,
- git_merge_file_input *ancestor,
- git_merge_file_input *ours,
- git_merge_file_input *theirs,
- git_merge_automerge_flags flags)
+ const git_merge_file_input *ancestor,
+ const git_merge_file_input *ours,
+ const git_merge_file_input *theirs,
+ const git_merge_file_options *options)
{
- xmparam_t xmparam;
- mmbuffer_t mmbuffer;
- int xdl_result;
- int error = 0;
+ git_merge_file_input inputs[3] = { {0} };
- assert(out && ancestor && ours && theirs);
+ assert(out && 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;
+ if (ancestor)
+ ancestor = git_merge_file__normalize_inputs(&inputs[0], ancestor);
- memset(&xmparam, 0x0, sizeof(xmparam_t));
- xmparam.ancestor = ancestor->label;
- xmparam.file1 = ours->label;
- xmparam.file2 = theirs->label;
+ ours = git_merge_file__normalize_inputs(&inputs[1], ours);
+ theirs = git_merge_file__normalize_inputs(&inputs[2], theirs);
- out->path = merge_file_best_path(ancestor, ours, theirs);
- out->mode = merge_file_best_mode(ancestor, ours, theirs);
+ return git_merge_file__from_inputs(out, ancestor, ours, theirs, options);
+}
- if (flags == GIT_MERGE_AUTOMERGE_FAVOR_OURS)
- xmparam.favor = XDL_MERGE_FAVOR_OURS;
+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;
- if (flags == GIT_MERGE_AUTOMERGE_FAVOR_THEIRS)
- xmparam.favor = XDL_MERGE_FAVOR_THEIRS;
+ assert(out && repo && ours && 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;
+ 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];
}
- out->automergeable = (xdl_result == 0);
- out->data = (unsigned char *)mmbuffer.ptr;
- out->len = mmbuffer.size;
+ 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((char *)result->path);
+
+ /* xdiff uses malloc() not git_malloc, so we use free(), not git_free() */
+ free((char *)result->ptr);
+}