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