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.ObjectId;
65 import org.eclipse.jgit.lib.PersonIdent;
66 import org.eclipse.jgit.lib.Repository;
67 import org.eclipse.jgit.revwalk.RevCommit;
68 import org.eclipse.jgit.revwalk.filter.RevFilter;
69 import org.eclipse.jgit.treewalk.AbstractTreeIterator;
70 import org.eclipse.jgit.treewalk.EmptyTreeIterator;
71 import org.eclipse.jgit.treewalk.WorkingTreeIterator;
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86 public class RecursiveMerger extends ResolveMerger {
87
88
89
90
91 public final int MAX_BASES = 200;
92
93
94
95
96
97
98
99
100 protected RecursiveMerger(Repository local, boolean inCore) {
101 super(local, inCore);
102 }
103
104
105
106
107
108
109 protected RecursiveMerger(Repository local) {
110 this(local, false);
111 }
112
113
114
115
116
117
118
119
120
121 @Override
122 protected RevCommit getBaseCommit(RevCommit a, RevCommit b)
123 throws IncorrectObjectTypeException, IOException {
124 return getBaseCommit(a, b, 0);
125 }
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148 protected RevCommit getBaseCommit(RevCommit a, RevCommit b, int callDepth)
149 throws IOException {
150 ArrayList<RevCommit> baseCommits = new ArrayList<RevCommit>();
151 walk.reset();
152 walk.setRevFilter(RevFilter.MERGE_BASE);
153 walk.markStart(a);
154 walk.markStart(b);
155 RevCommit c;
156 while ((c = walk.next()) != null)
157 baseCommits.add(c);
158
159 if (baseCommits.isEmpty())
160 return null;
161 if (baseCommits.size() == 1)
162 return baseCommits.get(0);
163 if (baseCommits.size() >= MAX_BASES)
164 throw new NoMergeBaseException(NoMergeBaseException.MergeBaseFailureReason.TOO_MANY_MERGE_BASES, MessageFormat.format(
165 JGitText.get().mergeRecursiveTooManyMergeBasesFor,
166 Integer.valueOf(MAX_BASES), a.name(), b.name(),
167 Integer.valueOf(baseCommits.size())));
168
169
170
171
172
173
174
175 RevCommit currentBase = baseCommits.get(0);
176 DirCache oldDircache = dircache;
177 boolean oldIncore = inCore;
178 WorkingTreeIterator oldWTreeIt = workingTreeIterator;
179 workingTreeIterator = null;
180 try {
181 dircache = DirCache.read(reader, currentBase.getTree());
182 inCore = true;
183
184 List<RevCommit> parents = new ArrayList<RevCommit>();
185 parents.add(currentBase);
186 for (int commitIdx = 1; commitIdx < baseCommits.size(); commitIdx++) {
187 RevCommit nextBase = baseCommits.get(commitIdx);
188 if (commitIdx >= MAX_BASES)
189 throw new NoMergeBaseException(
190 NoMergeBaseException.MergeBaseFailureReason.TOO_MANY_MERGE_BASES,
191 MessageFormat.format(
192 JGitText.get().mergeRecursiveTooManyMergeBasesFor,
193 Integer.valueOf(MAX_BASES), a.name(), b.name(),
194 Integer.valueOf(baseCommits.size())));
195 parents.add(nextBase);
196 RevCommit bc = getBaseCommit(currentBase, nextBase,
197 callDepth + 1);
198 AbstractTreeIterator bcTree = (bc == null) ? new EmptyTreeIterator()
199 : openTree(bc.getTree());
200 if (mergeTrees(bcTree, currentBase.getTree(),
201 nextBase.getTree(), true))
202 currentBase = createCommitForTree(resultTree, parents);
203 else
204 throw new NoMergeBaseException(
205 NoMergeBaseException.MergeBaseFailureReason.CONFLICTS_DURING_MERGE_BASE_CALCULATION,
206 MessageFormat.format(
207 JGitText.get().mergeRecursiveConflictsWhenMergingCommonAncestors,
208 currentBase.getName(), nextBase.getName()));
209 }
210 } finally {
211 inCore = oldIncore;
212 dircache = oldDircache;
213 workingTreeIterator = oldWTreeIt;
214 toBeCheckedOut.clear();
215 toBeDeleted.clear();
216 modifiedFiles.clear();
217 unmergedPaths.clear();
218 mergeResults.clear();
219 failingPaths.clear();
220 }
221 return currentBase;
222 }
223
224
225
226
227
228
229
230
231
232
233
234
235
236 private RevCommit createCommitForTree(ObjectId tree, List<RevCommit> parents)
237 throws IOException {
238 CommitBuilder c = new CommitBuilder();
239 c.setTreeId(tree);
240 c.setParentIds(parents);
241 c.setAuthor(mockAuthor(parents));
242 c.setCommitter(c.getAuthor());
243 return RevCommit.parse(walk, c.build());
244 }
245
246 private static PersonIdent mockAuthor(List<RevCommit> parents) {
247 String name = RecursiveMerger.class.getSimpleName();
248 int time = 0;
249 for (RevCommit p : parents)
250 time = Math.max(time, p.getCommitTime());
251 return new PersonIdent(
252 name, name + "@JGit",
253 new Date((time + 1) * 1000L),
254 TimeZone.getTimeZone("GMT+0000"));
255 }
256 }