]> git.proxmox.com Git - libgit2.git/blob - src/merge_file.c
Merge pull request #1897 from libgit2/split-patch-from-diff
[libgit2.git] / src / merge_file.c
1 /*
2 * Copyright (C) the libgit2 contributors. All rights reserved.
3 *
4 * This file is part of libgit2, distributed under the GNU GPL v2 with
5 * a Linking Exception. For full terms see the included COPYING file.
6 */
7
8 #include "common.h"
9 #include "repository.h"
10 #include "merge_file.h"
11
12 #include "git2/repository.h"
13 #include "git2/object.h"
14 #include "git2/index.h"
15
16 #include "xdiff/xdiff.h"
17
18 #define GIT_MERGE_FILE_SIDE_EXISTS(X) ((X)->mode != 0)
19
20 GIT_INLINE(const char *) merge_file_best_path(
21 const git_merge_file_input *ancestor,
22 const git_merge_file_input *ours,
23 const git_merge_file_input *theirs)
24 {
25 if (!GIT_MERGE_FILE_SIDE_EXISTS(ancestor)) {
26 if (strcmp(ours->path, theirs->path) == 0)
27 return ours->path;
28
29 return NULL;
30 }
31
32 if (strcmp(ancestor->path, ours->path) == 0)
33 return theirs->path;
34 else if(strcmp(ancestor->path, theirs->path) == 0)
35 return ours->path;
36
37 return NULL;
38 }
39
40 GIT_INLINE(int) merge_file_best_mode(
41 const git_merge_file_input *ancestor,
42 const git_merge_file_input *ours,
43 const git_merge_file_input *theirs)
44 {
45 /*
46 * If ancestor didn't exist and either ours or theirs is executable,
47 * assume executable. Otherwise, if any mode changed from the ancestor,
48 * use that one.
49 */
50 if (!GIT_MERGE_FILE_SIDE_EXISTS(ancestor)) {
51 if (ours->mode == GIT_FILEMODE_BLOB_EXECUTABLE ||
52 theirs->mode == GIT_FILEMODE_BLOB_EXECUTABLE)
53 return GIT_FILEMODE_BLOB_EXECUTABLE;
54
55 return GIT_FILEMODE_BLOB;
56 }
57
58 if (ancestor->mode == ours->mode)
59 return theirs->mode;
60 else if(ancestor->mode == theirs->mode)
61 return ours->mode;
62
63 return 0;
64 }
65
66 int git_merge_file_input_from_index_entry(
67 git_merge_file_input *input,
68 git_repository *repo,
69 const git_index_entry *entry)
70 {
71 git_odb *odb = NULL;
72 int error = 0;
73
74 assert(input && repo && entry);
75
76 if (entry->mode == 0)
77 return 0;
78
79 if ((error = git_repository_odb(&odb, repo)) < 0 ||
80 (error = git_odb_read(&input->odb_object, odb, &entry->oid)) < 0)
81 goto done;
82
83 input->mode = entry->mode;
84 input->path = git__strdup(entry->path);
85 input->mmfile.size = git_odb_object_size(input->odb_object);
86 input->mmfile.ptr = (char *)git_odb_object_data(input->odb_object);
87
88 if (input->label == NULL)
89 input->label = entry->path;
90
91 done:
92 git_odb_free(odb);
93
94 return error;
95 }
96
97 int git_merge_file_input_from_diff_file(
98 git_merge_file_input *input,
99 git_repository *repo,
100 const git_diff_file *file)
101 {
102 git_odb *odb = NULL;
103 int error = 0;
104
105 assert(input && repo && file);
106
107 if (file->mode == 0)
108 return 0;
109
110 if ((error = git_repository_odb(&odb, repo)) < 0 ||
111 (error = git_odb_read(&input->odb_object, odb, &file->oid)) < 0)
112 goto done;
113
114 input->mode = file->mode;
115 input->path = git__strdup(file->path);
116 input->mmfile.size = git_odb_object_size(input->odb_object);
117 input->mmfile.ptr = (char *)git_odb_object_data(input->odb_object);
118
119 if (input->label == NULL)
120 input->label = file->path;
121
122 done:
123 git_odb_free(odb);
124
125 return error;
126 }
127
128 int git_merge_files(
129 git_merge_file_result *out,
130 git_merge_file_input *ancestor,
131 git_merge_file_input *ours,
132 git_merge_file_input *theirs,
133 git_merge_automerge_flags flags)
134 {
135 xmparam_t xmparam;
136 mmbuffer_t mmbuffer;
137 int xdl_result;
138 int error = 0;
139
140 assert(out && ancestor && ours && theirs);
141
142 memset(out, 0x0, sizeof(git_merge_file_result));
143
144 if (!GIT_MERGE_FILE_SIDE_EXISTS(ours) || !GIT_MERGE_FILE_SIDE_EXISTS(theirs))
145 return 0;
146
147 memset(&xmparam, 0x0, sizeof(xmparam_t));
148 xmparam.ancestor = ancestor->label;
149 xmparam.file1 = ours->label;
150 xmparam.file2 = theirs->label;
151
152 out->path = merge_file_best_path(ancestor, ours, theirs);
153 out->mode = merge_file_best_mode(ancestor, ours, theirs);
154
155 if (flags == GIT_MERGE_AUTOMERGE_FAVOR_OURS)
156 xmparam.favor = XDL_MERGE_FAVOR_OURS;
157
158 if (flags == GIT_MERGE_AUTOMERGE_FAVOR_THEIRS)
159 xmparam.favor = XDL_MERGE_FAVOR_THEIRS;
160
161 if ((xdl_result = xdl_merge(&ancestor->mmfile, &ours->mmfile,
162 &theirs->mmfile, &xmparam, &mmbuffer)) < 0) {
163 giterr_set(GITERR_MERGE, "Failed to merge files.");
164 error = -1;
165 goto done;
166 }
167
168 out->automergeable = (xdl_result == 0);
169 out->data = (unsigned char *)mmbuffer.ptr;
170 out->len = mmbuffer.size;
171
172 done:
173 return error;
174 }