]>
Commit | Line | Data |
---|---|---|
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 | #ifndef INCLUDE_merge_h__ | |
8 | #define INCLUDE_merge_h__ | |
9 | ||
10 | #include "common.h" | |
11 | ||
12 | #include "vector.h" | |
13 | #include "commit_list.h" | |
14 | #include "pool.h" | |
15 | #include "iterator.h" | |
16 | ||
17 | #include "git2/types.h" | |
18 | #include "git2/merge.h" | |
19 | #include "git2/sys/merge.h" | |
20 | ||
21 | #define GIT_MERGE_MSG_FILE "MERGE_MSG" | |
22 | #define GIT_MERGE_MODE_FILE "MERGE_MODE" | |
23 | #define GIT_MERGE_FILE_MODE 0666 | |
24 | ||
25 | #define GIT_MERGE_DEFAULT_RENAME_THRESHOLD 50 | |
26 | #define GIT_MERGE_DEFAULT_TARGET_LIMIT 1000 | |
27 | ||
28 | /** Types of changes when files are merged from branch to branch. */ | |
29 | typedef enum { | |
30 | /* No conflict - a change only occurs in one branch. */ | |
31 | GIT_MERGE_DIFF_NONE = 0, | |
32 | ||
33 | /* Occurs when a file is modified in both branches. */ | |
34 | GIT_MERGE_DIFF_BOTH_MODIFIED = (1 << 0), | |
35 | ||
36 | /* Occurs when a file is added in both branches. */ | |
37 | GIT_MERGE_DIFF_BOTH_ADDED = (1 << 1), | |
38 | ||
39 | /* Occurs when a file is deleted in both branches. */ | |
40 | GIT_MERGE_DIFF_BOTH_DELETED = (1 << 2), | |
41 | ||
42 | /* Occurs when a file is modified in one branch and deleted in the other. */ | |
43 | GIT_MERGE_DIFF_MODIFIED_DELETED = (1 << 3), | |
44 | ||
45 | /* Occurs when a file is renamed in one branch and modified in the other. */ | |
46 | GIT_MERGE_DIFF_RENAMED_MODIFIED = (1 << 4), | |
47 | ||
48 | /* Occurs when a file is renamed in one branch and deleted in the other. */ | |
49 | GIT_MERGE_DIFF_RENAMED_DELETED = (1 << 5), | |
50 | ||
51 | /* Occurs when a file is renamed in one branch and a file with the same | |
52 | * name is added in the other. Eg, A->B and new file B. Core git calls | |
53 | * this a "rename/delete". */ | |
54 | GIT_MERGE_DIFF_RENAMED_ADDED = (1 << 6), | |
55 | ||
56 | /* Occurs when both a file is renamed to the same name in the ours and | |
57 | * theirs branches. Eg, A->B and A->B in both. Automergeable. */ | |
58 | GIT_MERGE_DIFF_BOTH_RENAMED = (1 << 7), | |
59 | ||
60 | /* Occurs when a file is renamed to different names in the ours and theirs | |
61 | * branches. Eg, A->B and A->C. */ | |
62 | GIT_MERGE_DIFF_BOTH_RENAMED_1_TO_2 = (1 << 8), | |
63 | ||
64 | /* Occurs when two files are renamed to the same name in the ours and | |
65 | * theirs branches. Eg, A->C and B->C. */ | |
66 | GIT_MERGE_DIFF_BOTH_RENAMED_2_TO_1 = (1 << 9), | |
67 | ||
68 | /* Occurs when an item at a path in one branch is a directory, and an | |
69 | * item at the same path in a different branch is a file. */ | |
70 | GIT_MERGE_DIFF_DIRECTORY_FILE = (1 << 10), | |
71 | ||
72 | /* The child of a folder that is in a directory/file conflict. */ | |
73 | GIT_MERGE_DIFF_DF_CHILD = (1 << 11) | |
74 | } git_merge_diff_t; | |
75 | ||
76 | typedef struct { | |
77 | git_repository *repo; | |
78 | git_pool pool; | |
79 | ||
80 | /* Vector of git_index_entry that represent the merged items that | |
81 | * have been staged, either because only one side changed, or because | |
82 | * the two changes were non-conflicting and mergeable. These items | |
83 | * will be written as staged entries in the main index. | |
84 | */ | |
85 | git_vector staged; | |
86 | ||
87 | /* Vector of git_merge_diff entries that represent the conflicts that | |
88 | * have not been automerged. These items will be written to high-stage | |
89 | * entries in the main index. | |
90 | */ | |
91 | git_vector conflicts; | |
92 | ||
93 | /* Vector of git_merge_diff that have been automerged. These items | |
94 | * will be written to the REUC when the index is produced. | |
95 | */ | |
96 | git_vector resolved; | |
97 | } git_merge_diff_list; | |
98 | ||
99 | /** | |
100 | * Description of changes to one file across three trees. | |
101 | */ | |
102 | typedef struct { | |
103 | git_merge_diff_t type; | |
104 | ||
105 | git_index_entry ancestor_entry; | |
106 | ||
107 | git_index_entry our_entry; | |
108 | git_delta_t our_status; | |
109 | ||
110 | git_index_entry their_entry; | |
111 | git_delta_t their_status; | |
112 | ||
113 | } git_merge_diff; | |
114 | ||
115 | int git_merge__bases_many( | |
116 | git_commit_list **out, | |
117 | git_revwalk *walk, | |
118 | git_commit_list_node *one, | |
119 | git_vector *twos, | |
120 | uint32_t minimum_generation); | |
121 | ||
122 | /* | |
123 | * Three-way tree differencing | |
124 | */ | |
125 | ||
126 | git_merge_diff_list *git_merge_diff_list__alloc(git_repository *repo); | |
127 | ||
128 | int git_merge_diff_list__find_differences( | |
129 | git_merge_diff_list *merge_diff_list, | |
130 | git_iterator *ancestor_iterator, | |
131 | git_iterator *ours_iter, | |
132 | git_iterator *theirs_iter); | |
133 | ||
134 | int git_merge_diff_list__find_renames(git_repository *repo, git_merge_diff_list *merge_diff_list, const git_merge_options *opts); | |
135 | ||
136 | void git_merge_diff_list__free(git_merge_diff_list *diff_list); | |
137 | ||
138 | /* Merge metadata setup */ | |
139 | ||
140 | int git_merge__setup( | |
141 | git_repository *repo, | |
142 | const git_annotated_commit *our_head, | |
143 | const git_annotated_commit *heads[], | |
144 | size_t heads_len); | |
145 | ||
146 | int git_merge__iterators( | |
147 | git_index **out, | |
148 | git_repository *repo, | |
149 | git_iterator *ancestor_iter, | |
150 | git_iterator *our_iter, | |
151 | git_iterator *their_iter, | |
152 | const git_merge_options *given_opts); | |
153 | ||
154 | int git_merge__check_result(git_repository *repo, git_index *index_new); | |
155 | ||
156 | int git_merge__append_conflicts_to_merge_msg(git_repository *repo, git_index *index); | |
157 | ||
158 | /* Merge files */ | |
159 | ||
160 | GIT_INLINE(const char *) git_merge_file__best_path( | |
161 | const char *ancestor, | |
162 | const char *ours, | |
163 | const char *theirs) | |
164 | { | |
165 | if (!ancestor) { | |
166 | if (ours && theirs && strcmp(ours, theirs) == 0) | |
167 | return ours; | |
168 | ||
169 | return NULL; | |
170 | } | |
171 | ||
172 | if (ours && strcmp(ancestor, ours) == 0) | |
173 | return theirs; | |
174 | else if(theirs && strcmp(ancestor, theirs) == 0) | |
175 | return ours; | |
176 | ||
177 | return NULL; | |
178 | } | |
179 | ||
180 | GIT_INLINE(uint32_t) git_merge_file__best_mode( | |
181 | uint32_t ancestor, uint32_t ours, uint32_t theirs) | |
182 | { | |
183 | /* | |
184 | * If ancestor didn't exist and either ours or theirs is executable, | |
185 | * assume executable. Otherwise, if any mode changed from the ancestor, | |
186 | * use that one. | |
187 | */ | |
188 | if (!ancestor) { | |
189 | if (ours == GIT_FILEMODE_BLOB_EXECUTABLE || | |
190 | theirs == GIT_FILEMODE_BLOB_EXECUTABLE) | |
191 | return GIT_FILEMODE_BLOB_EXECUTABLE; | |
192 | ||
193 | return GIT_FILEMODE_BLOB; | |
194 | } else if (ours && theirs) { | |
195 | if (ancestor == ours) | |
196 | return theirs; | |
197 | ||
198 | return ours; | |
199 | } | |
200 | ||
201 | return 0; | |
202 | } | |
203 | ||
204 | #endif |