1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50 package org.eclipse.jgit.merge;
51
52 import java.io.IOException;
53 import java.text.MessageFormat;
54 import java.util.ArrayList;
55 import java.util.Date;
56 import java.util.List;
57 import java.util.TimeZone;
58
59 import org.eclipse.jgit.dircache.DirCache;
60 import org.eclipse.jgit.errors.IncorrectObjectTypeException;
61 import org.eclipse.jgit.errors.NoMergeBaseException;
62 import org.eclipse.jgit.internal.JGitText;
63 import org.eclipse.jgit.lib.CommitBuilder;
64 import org.eclipse.jgit.lib.Config;
65 import org.eclipse.jgit.lib.ObjectId;
66 import org.eclipse.jgit.lib.ObjectInserter;
67 import org.eclipse.jgit.lib.PersonIdent;
68 import org.eclipse.jgit.lib.Repository;
69 import org.eclipse.jgit.revwalk.RevCommit;
70 import org.eclipse.jgit.revwalk.filter.RevFilter;
71 import org.eclipse.jgit.treewalk.AbstractTreeIterator;
72 import org.eclipse.jgit.treewalk.EmptyTreeIterator;
73 import org.eclipse.jgit.treewalk.WorkingTreeIterator;
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89 public class RecursiveMerger extends ResolveMerger {
90
91
92
93
94 public final int MAX_BASES = 200;
95
96
97
98
99
100
101
102
103
104
105 protected RecursiveMerger(Repository local, boolean inCore) {
106 super(local, inCore);
107 }
108
109
110
111
112
113
114 protected RecursiveMerger(Repository local) {
115 this(local, false);
116 }
117
118
119
120
121
122
123
124
125
126
127 protected RecursiveMerger(ObjectInserter inserter, Config config) {
128 super(inserter, config);
129 }
130
131
132
133
134
135
136
137
138 @Override
139 protected RevCommit getBaseCommit(RevCommit a, RevCommit b)
140 throws IncorrectObjectTypeException, IOException {
141 return getBaseCommit(a, b, 0);
142 }
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165 protected RevCommit getBaseCommit(RevCommit a, RevCommit b, int callDepth)
166 throws IOException {
167 ArrayList<RevCommit> baseCommits = new ArrayList<>();
168 walk.reset();
169 walk.setRevFilter(RevFilter.MERGE_BASE);
170 walk.markStart(a);
171 walk.markStart(b);
172 RevCommit c;
173 while ((c = walk.next()) != null)
174 baseCommits.add(c);
175
176 if (baseCommits.isEmpty())
177 return null;
178 if (baseCommits.size() == 1)
179 return baseCommits.get(0);
180 if (baseCommits.size() >= MAX_BASES)
181 throw new NoMergeBaseException(NoMergeBaseException.MergeBaseFailureReason.TOO_MANY_MERGE_BASES, MessageFormat.format(
182 JGitText.get().mergeRecursiveTooManyMergeBasesFor,
183 Integer.valueOf(MAX_BASES), a.name(), b.name(),
184 Integer.valueOf(baseCommits.size())));
185
186
187
188
189
190
191
192 RevCommit currentBase = baseCommits.get(0);
193 DirCache oldDircache = dircache;
194 boolean oldIncore = inCore;
195 WorkingTreeIterator oldWTreeIt = workingTreeIterator;
196 workingTreeIterator = null;
197 try {
198 dircache = DirCache.read(reader, currentBase.getTree());
199 inCore = true;
200
201 List<RevCommit> parents = new ArrayList<>();
202 parents.add(currentBase);
203 for (int commitIdx = 1; commitIdx < baseCommits.size(); commitIdx++) {
204 RevCommit nextBase = baseCommits.get(commitIdx);
205 if (commitIdx >= MAX_BASES)
206 throw new NoMergeBaseException(
207 NoMergeBaseException.MergeBaseFailureReason.TOO_MANY_MERGE_BASES,
208 MessageFormat.format(
209 JGitText.get().mergeRecursiveTooManyMergeBasesFor,
210 Integer.valueOf(MAX_BASES), a.name(), b.name(),
211 Integer.valueOf(baseCommits.size())));
212 parents.add(nextBase);
213 RevCommit bc = getBaseCommit(currentBase, nextBase,
214 callDepth + 1);
215 AbstractTreeIterator bcTree = (bc == null) ? new EmptyTreeIterator()
216 : openTree(bc.getTree());
217 if (mergeTrees(bcTree, currentBase.getTree(),
218 nextBase.getTree(), true))
219 currentBase = createCommitForTree(resultTree, parents);
220 else
221 throw new NoMergeBaseException(
222 NoMergeBaseException.MergeBaseFailureReason.CONFLICTS_DURING_MERGE_BASE_CALCULATION,
223 MessageFormat.format(
224 JGitText.get().mergeRecursiveConflictsWhenMergingCommonAncestors,
225 currentBase.getName(), nextBase.getName()));
226 }
227 } finally {
228 inCore = oldIncore;
229 dircache = oldDircache;
230 workingTreeIterator = oldWTreeIt;
231 toBeCheckedOut.clear();
232 toBeDeleted.clear();
233 modifiedFiles.clear();
234 unmergedPaths.clear();
235 mergeResults.clear();
236 failingPaths.clear();
237 }
238 return currentBase;
239 }
240
241
242
243
244
245
246
247
248
249
250
251
252
253 private RevCommit createCommitForTree(ObjectId tree, List<RevCommit> parents)
254 throws IOException {
255 CommitBuilder c = new CommitBuilder();
256 c.setTreeId(tree);
257 c.setParentIds(parents);
258 c.setAuthor(mockAuthor(parents));
259 c.setCommitter(c.getAuthor());
260 return RevCommit.parse(walk, c.build());
261 }
262
263 private static PersonIdent mockAuthor(List<RevCommit> parents) {
264 String name = RecursiveMerger.class.getSimpleName();
265 int time = 0;
266 for (RevCommit p : parents)
267 time = Math.max(time, p.getCommitTime());
268 return new PersonIdent(
269 name, name + "@JGit",
270 new Date((time + 1) * 1000L),
271 TimeZone.getTimeZone("GMT+0000"));
272 }
273 }