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