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