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 package org.eclipse.jgit.internal.storage.pack;
45
46 import static org.eclipse.jgit.internal.storage.file.PackBitmapIndex.FLAG_REUSE;
47
48 import java.io.IOException;
49 import java.util.ArrayList;
50 import java.util.Collection;
51 import java.util.Collections;
52 import java.util.Comparator;
53 import java.util.HashSet;
54 import java.util.Iterator;
55 import java.util.List;
56 import java.util.Set;
57
58 import com.googlecode.javaewah.EWAHCompressedBitmap;
59
60 import org.eclipse.jgit.errors.IncorrectObjectTypeException;
61 import org.eclipse.jgit.errors.MissingObjectException;
62 import org.eclipse.jgit.internal.JGitText;
63 import org.eclipse.jgit.internal.storage.file.BitmapIndexImpl;
64 import org.eclipse.jgit.internal.storage.file.PackBitmapIndex;
65 import org.eclipse.jgit.internal.storage.file.PackBitmapIndexBuilder;
66 import org.eclipse.jgit.internal.storage.file.PackBitmapIndexRemapper;
67 import org.eclipse.jgit.lib.AnyObjectId;
68 import org.eclipse.jgit.lib.Constants;
69 import org.eclipse.jgit.lib.ObjectId;
70 import org.eclipse.jgit.lib.ObjectReader;
71 import org.eclipse.jgit.lib.ProgressMonitor;
72 import org.eclipse.jgit.lib.BitmapIndex.BitmapBuilder;
73 import org.eclipse.jgit.revwalk.ObjectWalk;
74 import org.eclipse.jgit.revwalk.RevCommit;
75 import org.eclipse.jgit.revwalk.RevObject;
76 import org.eclipse.jgit.revwalk.RevWalk;
77 import org.eclipse.jgit.util.BlockList;
78
79
80 class PackWriterBitmapPreparer {
81
82 private static final Comparator<BitmapBuilder> BUILDER_BY_CARDINALITY_DSC =
83 new Comparator<BitmapBuilder>() {
84 public int compare(BitmapBuilder a, BitmapBuilder b) {
85 return Integer.signum(b.cardinality() - a.cardinality());
86 }
87 };
88
89 private final ObjectReader reader;
90 private final ProgressMonitor pm;
91 private final Set<? extends ObjectId> want;
92 private final PackBitmapIndexBuilder writeBitmaps;
93 private final BitmapIndexImpl commitBitmapIndex;
94 private final PackBitmapIndexRemapper bitmapRemapper;
95 private final BitmapIndexImpl bitmapIndex;
96 private final int minCommits = 100;
97 private final int maxCommits = 5000;
98
99 PackWriterBitmapPreparer(ObjectReader reader,
100 PackBitmapIndexBuilder writeBitmaps, ProgressMonitor pm,
101 Set<? extends ObjectId> want) throws IOException {
102 this.reader = reader;
103 this.writeBitmaps = writeBitmaps;
104 this.pm = pm;
105 this.want = want;
106 this.commitBitmapIndex = new BitmapIndexImpl(writeBitmaps);
107 this.bitmapRemapper = PackBitmapIndexRemapper.newPackBitmapIndex(
108 reader.getBitmapIndex(), writeBitmaps);
109 this.bitmapIndex = new BitmapIndexImpl(bitmapRemapper);
110 }
111
112 Collection<BitmapCommit> doCommitSelection(int expectedNumCommits)
113 throws MissingObjectException, IncorrectObjectTypeException,
114 IOException {
115 pm.beginTask(JGitText.get().selectingCommits, ProgressMonitor.UNKNOWN);
116 RevWalk rw = new RevWalk(reader);
117 WalkResult result = findPaths(rw, expectedNumCommits);
118 pm.endTask();
119
120 int totCommits = result.commitsByOldest.length - result.commitStartPos;
121 BlockList<BitmapCommit> selections = new BlockList<BitmapCommit>(
122 totCommits / minCommits + 1);
123 for (BitmapCommit reuse : result.reuse)
124 selections.add(reuse);
125
126 if (totCommits == 0) {
127 for (AnyObjectId id : result.peeledWant)
128 selections.add(new BitmapCommit(id, false, 0));
129 return selections;
130 }
131
132 pm.beginTask(JGitText.get().selectingCommits, totCommits);
133
134 for (BitmapBuilder bitmapableCommits : result.paths) {
135 int cardinality = bitmapableCommits.cardinality();
136
137 List<List<BitmapCommit>> running = new ArrayList<
138 List<BitmapCommit>>();
139
140
141
142 int index = -1;
143 int nextIn = nextSelectionDistance(0, cardinality);
144 int nextFlg = nextIn == maxCommits ? PackBitmapIndex.FLAG_REUSE : 0;
145 boolean mustPick = nextIn == 0;
146 for (RevCommit c : result) {
147 if (!bitmapableCommits.contains(c))
148 continue;
149
150 index++;
151 nextIn--;
152 pm.update(1);
153
154
155 if (result.peeledWant.remove(c)) {
156 if (nextIn > 0)
157 nextFlg = 0;
158 } else if (!mustPick && ((nextIn > 0)
159 || (c.getParentCount() <= 1 && nextIn > -minCommits))) {
160 continue;
161 }
162
163 int flags = nextFlg;
164 nextIn = nextSelectionDistance(index, cardinality);
165 nextFlg = nextIn == maxCommits ? PackBitmapIndex.FLAG_REUSE : 0;
166 mustPick = nextIn == 0;
167
168 BitmapBuilder fullBitmap = commitBitmapIndex.newBitmapBuilder();
169 rw.reset();
170 rw.markStart(c);
171 for (AnyObjectId objectId : result.reuse)
172 rw.markUninteresting(rw.parseCommit(objectId));
173 rw.setRevFilter(
174 PackWriterBitmapWalker.newRevFilter(null, fullBitmap));
175
176 while (rw.next() != null) {
177
178 }
179
180 List<List<BitmapCommit>> matches = new ArrayList<
181 List<BitmapCommit>>();
182 for (List<BitmapCommit> list : running) {
183 BitmapCommit last = list.get(list.size() - 1);
184 if (fullBitmap.contains(last))
185 matches.add(list);
186 }
187
188 List<BitmapCommit> match;
189 if (matches.isEmpty()) {
190 match = new ArrayList<BitmapCommit>();
191 running.add(match);
192 } else {
193 match = matches.get(0);
194
195 for (List<BitmapCommit> list : matches) {
196 if (list.size() > match.size())
197 match = list;
198 }
199 }
200 match.add(new BitmapCommit(c, !match.isEmpty(), flags));
201 writeBitmaps.addBitmap(c, fullBitmap, 0);
202 }
203
204 for (List<BitmapCommit> list : running)
205 selections.addAll(list);
206 }
207 writeBitmaps.clearBitmaps();
208
209
210 for (AnyObjectId remainingWant : result.peeledWant)
211 selections.add(new BitmapCommit(remainingWant, false, 0));
212
213 pm.endTask();
214 return selections;
215 }
216
217 private WalkResult findPaths(RevWalk rw, int expectedNumCommits)
218 throws MissingObjectException, IOException {
219 BitmapBuilder reuseBitmap = commitBitmapIndex.newBitmapBuilder();
220 List<BitmapCommit> reuse = new ArrayList<BitmapCommit>();
221 for (PackBitmapIndexRemapper.Entry entry : bitmapRemapper) {
222 if ((entry.getFlags() & FLAG_REUSE) != FLAG_REUSE)
223 continue;
224
225 RevObject ro = rw.peel(rw.parseAny(entry));
226 if (ro instanceof RevCommit) {
227 RevCommit rc = (RevCommit) ro;
228 reuse.add(new BitmapCommit(rc, false, entry.getFlags()));
229 rw.markUninteresting(rc);
230
231 EWAHCompressedBitmap bitmap = bitmapRemapper.ofObjectType(
232 bitmapRemapper.getBitmap(rc), Constants.OBJ_COMMIT);
233 writeBitmaps.addBitmap(rc, bitmap, 0);
234 reuseBitmap.add(rc, Constants.OBJ_COMMIT);
235 }
236 }
237 writeBitmaps.clearBitmaps();
238
239
240
241 List<BitmapBuilder> paths = new ArrayList<BitmapBuilder>(want.size());
242 Set<RevCommit> peeledWant = new HashSet<RevCommit>(want.size());
243 for (AnyObjectId objectId : want) {
244 RevObject ro = rw.peel(rw.parseAny(objectId));
245 if (ro instanceof RevCommit && !reuseBitmap.contains(ro)) {
246 RevCommit rc = (RevCommit) ro;
247 peeledWant.add(rc);
248 rw.markStart(rc);
249
250 BitmapBuilder bitmap = commitBitmapIndex.newBitmapBuilder();
251 bitmap.or(reuseBitmap);
252 bitmap.add(rc, Constants.OBJ_COMMIT);
253 paths.add(bitmap);
254 }
255 }
256
257
258
259 RevCommit[] commits = new RevCommit[expectedNumCommits];
260 int pos = commits.length;
261 RevCommit rc;
262 while ((rc = rw.next()) != null) {
263 commits[--pos] = rc;
264 for (BitmapBuilder path : paths) {
265 if (path.contains(rc)) {
266 for (RevCommit c : rc.getParents())
267 path.add(c, Constants.OBJ_COMMIT);
268 }
269 }
270
271 pm.update(1);
272 }
273
274
275 if (!reuse.isEmpty())
276 for (BitmapBuilder bitmap : paths)
277 bitmap.andNot(reuseBitmap);
278
279
280 List<BitmapBuilder> distinctPaths = new ArrayList<BitmapBuilder>(paths.size());
281 while (!paths.isEmpty()) {
282 Collections.sort(paths, BUILDER_BY_CARDINALITY_DSC);
283 BitmapBuilder largest = paths.remove(0);
284 distinctPaths.add(largest);
285
286
287
288 for (int i = paths.size() - 1; i >= 0; i--)
289 paths.get(i).andNot(largest);
290 }
291
292 return new WalkResult(peeledWant, commits, pos, distinctPaths, reuse);
293 }
294
295 private int nextSelectionDistance(int idx, int cardinality) {
296 if (idx > cardinality)
297 throw new IllegalArgumentException();
298 int idxFromStart = cardinality - idx;
299 int mustRegionEnd = 100;
300 if (idxFromStart <= mustRegionEnd)
301 return 0;
302
303
304 int minRegionEnd = 20000;
305 if (idxFromStart <= minRegionEnd)
306 return Math.min(idxFromStart - mustRegionEnd, minCommits);
307
308
309 int next = Math.min(idxFromStart - minRegionEnd, maxCommits);
310 return Math.max(next, minCommits);
311 }
312
313 PackWriterBitmapWalker newBitmapWalker() {
314 return new PackWriterBitmapWalker(
315 new ObjectWalk(reader), bitmapIndex, null);
316 }
317
318 static final class BitmapCommit extends ObjectId {
319 private final boolean reuseWalker;
320 private final int flags;
321
322 private BitmapCommit(
323 AnyObjectId objectId, boolean reuseWalker, int flags) {
324 super(objectId);
325 this.reuseWalker = reuseWalker;
326 this.flags = flags;
327 }
328
329 boolean isReuseWalker() {
330 return reuseWalker;
331 }
332
333 int getFlags() {
334 return flags;
335 }
336 }
337
338 private static final class WalkResult implements Iterable<RevCommit> {
339 private final Set<? extends ObjectId> peeledWant;
340 private final RevCommit[] commitsByOldest;
341 private final int commitStartPos;
342 private final List<BitmapBuilder> paths;
343 private final Iterable<BitmapCommit> reuse;
344
345 private WalkResult(Set<? extends ObjectId> peeledWant,
346 RevCommit[] commitsByOldest, int commitStartPos,
347 List<BitmapBuilder> paths, Iterable<BitmapCommit> reuse) {
348 this.peeledWant = peeledWant;
349 this.commitsByOldest = commitsByOldest;
350 this.commitStartPos = commitStartPos;
351 this.paths = paths;
352 this.reuse = reuse;
353 }
354
355 public Iterator<RevCommit> iterator() {
356 return new Iterator<RevCommit>() {
357 int pos = commitStartPos;
358
359 public boolean hasNext() {
360 return pos < commitsByOldest.length;
361 }
362
363 public RevCommit next() {
364 return commitsByOldest[pos++];
365 }
366
367 public void remove() {
368 throw new UnsupportedOperationException();
369 }
370 };
371 }
372 }
373 }