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