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 import static org.eclipse.jgit.revwalk.RevFlag.SEEN;
48
49 import java.io.IOException;
50 import java.util.ArrayList;
51 import java.util.Collection;
52 import java.util.Collections;
53 import java.util.Comparator;
54 import java.util.HashSet;
55 import java.util.Iterator;
56 import java.util.List;
57 import java.util.Set;
58
59 import org.eclipse.jgit.errors.IncorrectObjectTypeException;
60 import org.eclipse.jgit.errors.MissingObjectException;
61 import org.eclipse.jgit.internal.JGitText;
62 import org.eclipse.jgit.internal.storage.file.BitmapIndexImpl;
63 import org.eclipse.jgit.internal.storage.file.BitmapIndexImpl.CompressedBitmap;
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.internal.storage.pack.PackWriterBitmapWalker.AddUnseenToBitmapFilter;
68 import org.eclipse.jgit.lib.AnyObjectId;
69 import org.eclipse.jgit.lib.BitmapIndex.BitmapBuilder;
70 import org.eclipse.jgit.lib.Constants;
71 import org.eclipse.jgit.lib.ObjectId;
72 import org.eclipse.jgit.lib.ObjectReader;
73 import org.eclipse.jgit.lib.ProgressMonitor;
74 import org.eclipse.jgit.revwalk.ObjectWalk;
75 import org.eclipse.jgit.revwalk.RevCommit;
76 import org.eclipse.jgit.revwalk.RevObject;
77 import org.eclipse.jgit.revwalk.RevWalk;
78 import org.eclipse.jgit.revwalk.filter.RevFilter;
79 import org.eclipse.jgit.storage.pack.PackConfig;
80 import org.eclipse.jgit.util.BlockList;
81 import org.eclipse.jgit.util.SystemReader;
82
83 import com.googlecode.javaewah.EWAHCompressedBitmap;
84
85
86
87
88
89 class PackWriterBitmapPreparer {
90
91 private static final int DAY_IN_SECONDS = 24 * 60 * 60;
92
93 private static final Comparator<BitmapBuilderEntry> ORDER_BY_CARDINALITY = new Comparator<BitmapBuilderEntry>() {
94 public int compare(BitmapBuilderEntry a, BitmapBuilderEntry b) {
95 return Integer.signum(a.getBuilder().cardinality()
96 - b.getBuilder().cardinality());
97 }
98 };
99
100 private final ObjectReader reader;
101 private final ProgressMonitor pm;
102 private final Set<? extends ObjectId> want;
103 private final PackBitmapIndexBuilder writeBitmaps;
104 private final BitmapIndexImpl commitBitmapIndex;
105 private final PackBitmapIndexRemapper bitmapRemapper;
106 private final BitmapIndexImpl bitmapIndex;
107
108 private final int contiguousCommitCount;
109 private final int recentCommitCount;
110 private final int recentCommitSpan;
111 private final int distantCommitSpan;
112 private final int excessiveBranchCount;
113 private final long inactiveBranchTimestamp;
114
115 PackWriterBitmapPreparer(ObjectReader reader,
116 PackBitmapIndexBuilder writeBitmaps, ProgressMonitor pm,
117 Set<? extends ObjectId> want, PackConfig config)
118 throws IOException {
119 this.reader = reader;
120 this.writeBitmaps = writeBitmaps;
121 this.pm = pm;
122 this.want = want;
123 this.commitBitmapIndex = new BitmapIndexImpl(writeBitmaps);
124 this.bitmapRemapper = PackBitmapIndexRemapper.newPackBitmapIndex(
125 reader.getBitmapIndex(), writeBitmaps);
126 this.bitmapIndex = new BitmapIndexImpl(bitmapRemapper);
127 this.contiguousCommitCount = config.getBitmapContiguousCommitCount();
128 this.recentCommitCount = config.getBitmapRecentCommitCount();
129 this.recentCommitSpan = config.getBitmapRecentCommitSpan();
130 this.distantCommitSpan = config.getBitmapDistantCommitSpan();
131 this.excessiveBranchCount = config.getBitmapExcessiveBranchCount();
132 long now = SystemReader.getInstance().getCurrentTime();
133 long ageInSeconds = config.getBitmapInactiveBranchAgeInDays()
134 * DAY_IN_SECONDS;
135 this.inactiveBranchTimestamp = (now / 1000) - ageInSeconds;
136 }
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151 Collection<BitmapCommit> selectCommits(int expectedCommitCount)
152 throws IncorrectObjectTypeException, IOException,
153 MissingObjectException {
154
155
156
157
158
159
160
161
162 pm.beginTask(JGitText.get().selectingCommits, ProgressMonitor.UNKNOWN);
163 RevWalk rw = new RevWalk(reader);
164 rw.setRetainBody(false);
165 CommitSelectionHelper selectionHelper = setupTipCommitBitmaps(rw,
166 expectedCommitCount);
167 pm.endTask();
168
169 int totCommits = selectionHelper.getCommitCount();
170 BlockList<BitmapCommit> selections = new BlockList<BitmapCommit>(
171 totCommits / recentCommitSpan + 1);
172 for (BitmapCommit reuse : selectionHelper.reusedCommits) {
173 selections.add(reuse);
174 }
175
176 if (totCommits == 0) {
177 for (AnyObjectId id : selectionHelper.peeledWants) {
178 selections.add(new BitmapCommit(id, false, 0));
179 }
180 return selections;
181 }
182
183 pm.beginTask(JGitText.get().selectingCommits, totCommits);
184 int totalWants = selectionHelper.peeledWants.size();
185
186 for (BitmapBuilderEntry entry : selectionHelper.tipCommitBitmaps) {
187 BitmapBuilder bitmap = entry.getBuilder();
188 int cardinality = bitmap.cardinality();
189
190
191
192
193
194
195
196 List<List<BitmapCommit>> chains =
197 new ArrayList<List<BitmapCommit>>();
198
199
200
201
202
203 boolean isActiveBranch = true;
204 if (totalWants > excessiveBranchCount
205 && !isRecentCommit(entry.getCommit())) {
206 isActiveBranch = false;
207 }
208
209
210
211
212 int index = -1;
213 int nextIn = nextSpan(cardinality);
214 int nextFlg = nextIn == distantCommitSpan
215 ? PackBitmapIndex.FLAG_REUSE : 0;
216
217
218
219 for (RevCommit c : selectionHelper) {
220
221
222 int distanceFromTip = cardinality - index - 1;
223 if (distanceFromTip == 0) {
224 break;
225 }
226
227
228 if (!bitmap.contains(c)) {
229 continue;
230 }
231
232 index++;
233 nextIn--;
234 pm.update(1);
235
236
237 if (selectionHelper.peeledWants.remove(c)) {
238 if (nextIn > 0) {
239 nextFlg = 0;
240 }
241 } else {
242 boolean stillInSpan = nextIn >= 0;
243 boolean isMergeCommit = c.getParentCount() > 1;
244
245
246
247
248 boolean mustPick = (nextIn <= -recentCommitSpan)
249 || (isActiveBranch
250 && (distanceFromTip <= contiguousCommitCount))
251 || (distanceFromTip == 1);
252 if (!mustPick && (stillInSpan || !isMergeCommit)) {
253 continue;
254 }
255 }
256
257
258
259 int flags = nextFlg;
260 nextIn = nextSpan(distanceFromTip);
261 nextFlg = nextIn == distantCommitSpan
262 ? PackBitmapIndex.FLAG_REUSE : 0;
263
264 BitmapBuilder fullBitmap = commitBitmapIndex.newBitmapBuilder();
265 rw.reset();
266 rw.markStart(c);
267 rw.setRevFilter(new AddUnseenToBitmapFilter(
268 selectionHelper.reusedCommitsBitmap, fullBitmap));
269
270 while (rw.next() != null) {
271
272
273 }
274
275
276
277 List<BitmapCommit> longestAncestorChain = null;
278 for (List<BitmapCommit> chain : chains) {
279 BitmapCommit mostRecentCommit = chain.get(chain.size() - 1);
280 if (fullBitmap.contains(mostRecentCommit)) {
281 if (longestAncestorChain == null
282 || longestAncestorChain.size() < chain.size()) {
283 longestAncestorChain = chain;
284 }
285 }
286 }
287
288 if (longestAncestorChain == null) {
289 longestAncestorChain = new ArrayList<BitmapCommit>();
290 chains.add(longestAncestorChain);
291 }
292 longestAncestorChain.add(new BitmapCommit(
293 c, !longestAncestorChain.isEmpty(), flags));
294 writeBitmaps.addBitmap(c, fullBitmap, 0);
295 }
296
297 for (List<BitmapCommit> chain : chains) {
298 selections.addAll(chain);
299 }
300 }
301 writeBitmaps.clearBitmaps();
302
303
304 for (AnyObjectId remainingWant : selectionHelper.peeledWants) {
305 selections.add(new BitmapCommit(remainingWant, false, 0));
306 }
307
308 pm.endTask();
309 return selections;
310 }
311
312 private boolean isRecentCommit(RevCommit revCommit) {
313 return revCommit.getCommitTime() > inactiveBranchTimestamp;
314 }
315
316
317
318
319
320
321
322
323
324
325
326 private static class NotInBitmapFilter extends RevFilter {
327 private final BitmapBuilder bitmap;
328
329 NotInBitmapFilter(BitmapBuilder bitmap) {
330 this.bitmap = bitmap;
331 }
332
333 @Override
334 public final boolean include(RevWalk rw, RevCommit c) {
335 if (!bitmap.contains(c)) {
336 return true;
337 }
338 for (RevCommit p : c.getParents()) {
339 p.add(SEEN);
340 }
341 return false;
342 }
343
344 @Override
345 public final NotInBitmapFilter clone() {
346 throw new UnsupportedOperationException();
347 }
348
349 @Override
350 public final boolean requiresCommitBody() {
351 return false;
352 }
353 }
354
355
356
357
358
359
360
361
362
363
364
365
366
367
368
369
370
371
372
373
374 private CommitSelectionHelper setupTipCommitBitmaps(RevWalk rw,
375 int expectedCommitCount) throws IncorrectObjectTypeException,
376 IOException, MissingObjectException {
377 BitmapBuilder reuse = commitBitmapIndex.newBitmapBuilder();
378 List<BitmapCommit> reuseCommits = new ArrayList<BitmapCommit>();
379 for (PackBitmapIndexRemapper.Entry entry : bitmapRemapper) {
380
381 if ((entry.getFlags() & FLAG_REUSE) != FLAG_REUSE) {
382 continue;
383 }
384 RevObject ro = rw.peel(rw.parseAny(entry));
385 if (!(ro instanceof RevCommit)) {
386 continue;
387 }
388
389 RevCommit rc = (RevCommit) ro;
390 reuseCommits.add(new BitmapCommit(rc, false, entry.getFlags()));
391 if (!reuse.contains(rc)) {
392 EWAHCompressedBitmap bitmap = bitmapRemapper.ofObjectType(
393 bitmapRemapper.getBitmap(rc), Constants.OBJ_COMMIT);
394 reuse.or(new CompressedBitmap(bitmap, commitBitmapIndex));
395 }
396 }
397
398
399
400 List<BitmapBuilderEntry> tipCommitBitmaps = new ArrayList<BitmapBuilderEntry>(
401 want.size());
402 Set<RevCommit> peeledWant = new HashSet<RevCommit>(want.size());
403 for (AnyObjectId objectId : want) {
404 RevObject ro = rw.peel(rw.parseAny(objectId));
405 if (!(ro instanceof RevCommit) || reuse.contains(ro)) {
406 continue;
407 }
408
409 RevCommit rc = (RevCommit) ro;
410 peeledWant.add(rc);
411 rw.markStart(rc);
412
413 BitmapBuilder bitmap = commitBitmapIndex.newBitmapBuilder();
414 bitmap.addObject(rc, Constants.OBJ_COMMIT);
415 tipCommitBitmaps.add(new BitmapBuilderEntry(rc, bitmap));
416 }
417
418
419
420
421 rw.setRevFilter(new NotInBitmapFilter(reuse));
422 RevCommit[] commits = new RevCommit[expectedCommitCount];
423 int pos = commits.length;
424 RevCommit rc;
425 while ((rc = rw.next()) != null && pos > 0) {
426 commits[--pos] = rc;
427 for (BitmapBuilderEntry entry : tipCommitBitmaps) {
428 BitmapBuilder bitmap = entry.getBuilder();
429 if (!bitmap.contains(rc)) {
430 continue;
431 }
432 for (RevCommit c : rc.getParents()) {
433 if (reuse.contains(c)) {
434 continue;
435 }
436 bitmap.addObject(c, Constants.OBJ_COMMIT);
437 }
438 }
439 pm.update(1);
440 }
441
442
443
444
445 List<BitmapBuilderEntry> orderedTipCommitBitmaps = new ArrayList<>(
446 tipCommitBitmaps.size());
447 while (!tipCommitBitmaps.isEmpty()) {
448 BitmapBuilderEntry largest =
449 Collections.max(tipCommitBitmaps, ORDER_BY_CARDINALITY);
450 tipCommitBitmaps.remove(largest);
451 orderedTipCommitBitmaps.add(largest);
452
453
454
455 for (int i = tipCommitBitmaps.size() - 1; i >= 0; i--) {
456 tipCommitBitmaps.get(i).getBuilder()
457 .andNot(largest.getBuilder());
458 }
459 }
460
461 return new CommitSelectionHelper(peeledWant, commits, pos,
462 orderedTipCommitBitmaps, reuse, reuseCommits);
463 }
464
465
466
467
468
469
470
471
472
473
474
475
476
477
478
479
480
481
482
483
484
485
486
487 int nextSpan(int distanceFromTip) {
488 if (distanceFromTip < 0) {
489 throw new IllegalArgumentException();
490 }
491
492
493 if (distanceFromTip <= recentCommitCount) {
494 return recentCommitSpan;
495 }
496
497 int next = Math.min(distanceFromTip - recentCommitCount,
498 distantCommitSpan);
499 return Math.max(next, recentCommitSpan);
500 }
501
502 PackWriterBitmapWalker newBitmapWalker() {
503 return new PackWriterBitmapWalker(
504 new ObjectWalk(reader), bitmapIndex, null);
505 }
506
507
508
509
510 static final class BitmapCommit extends ObjectId {
511 private final boolean reuseWalker;
512 private final int flags;
513
514 BitmapCommit(AnyObjectId objectId, boolean reuseWalker, int flags) {
515 super(objectId);
516 this.reuseWalker = reuseWalker;
517 this.flags = flags;
518 }
519
520 boolean isReuseWalker() {
521 return reuseWalker;
522 }
523
524 int getFlags() {
525 return flags;
526 }
527 }
528
529
530
531
532 private static final class BitmapBuilderEntry {
533 private final RevCommit commit;
534 private final BitmapBuilder builder;
535
536 BitmapBuilderEntry(RevCommit commit, BitmapBuilder builder) {
537 this.commit = commit;
538 this.builder = builder;
539 }
540
541 RevCommit getCommit() {
542 return commit;
543 }
544
545 BitmapBuilder getBuilder() {
546 return builder;
547 }
548 }
549
550
551
552
553
554
555
556
557
558
559
560 private static final class CommitSelectionHelper implements Iterable<RevCommit> {
561 final Set<? extends ObjectId> peeledWants;
562 final List<BitmapBuilderEntry> tipCommitBitmaps;
563
564 final BitmapBuilder reusedCommitsBitmap;
565 final Iterable<BitmapCommit> reusedCommits;
566 final RevCommit[] commitsByOldest;
567 final int commitStartPos;
568
569 CommitSelectionHelper(Set<? extends ObjectId> peeledWant,
570 RevCommit[] commitsByOldest, int commitStartPos,
571 List<BitmapBuilderEntry> bitmapEntries,
572 BitmapBuilder reusedCommitsBitmap,
573 Iterable<BitmapCommit> reuse) {
574 this.peeledWants = peeledWant;
575 this.commitsByOldest = commitsByOldest;
576 this.commitStartPos = commitStartPos;
577 this.tipCommitBitmaps = bitmapEntries;
578 this.reusedCommitsBitmap = reusedCommitsBitmap;
579 this.reusedCommits = reuse;
580 }
581
582 public Iterator<RevCommit> iterator() {
583
584
585 return new Iterator<RevCommit>() {
586 int pos = commitStartPos;
587
588 public boolean hasNext() {
589 return pos < commitsByOldest.length;
590 }
591
592 public RevCommit next() {
593 return commitsByOldest[pos++];
594 }
595
596 public void remove() {
597 throw new UnsupportedOperationException();
598 }
599 };
600 }
601
602 int getCommitCount() {
603 return commitsByOldest.length - commitStartPos;
604 }
605 }
606 }