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
45 package org.eclipse.jgit.internal.storage.pack;
46
47 import static org.eclipse.jgit.internal.storage.pack.StoredObjectRepresentation.PACK_DELTA;
48 import static org.eclipse.jgit.internal.storage.pack.StoredObjectRepresentation.PACK_WHOLE;
49 import static org.eclipse.jgit.lib.Constants.OBJECT_ID_LENGTH;
50 import static org.eclipse.jgit.lib.Constants.OBJ_BLOB;
51 import static org.eclipse.jgit.lib.Constants.OBJ_COMMIT;
52 import static org.eclipse.jgit.lib.Constants.OBJ_TAG;
53 import static org.eclipse.jgit.lib.Constants.OBJ_TREE;
54
55 import java.io.IOException;
56 import java.io.OutputStream;
57 import java.lang.ref.WeakReference;
58 import java.security.MessageDigest;
59 import java.text.MessageFormat;
60 import java.util.ArrayList;
61 import java.util.Arrays;
62 import java.util.Collection;
63 import java.util.Collections;
64 import java.util.Comparator;
65 import java.util.HashSet;
66 import java.util.Iterator;
67 import java.util.List;
68 import java.util.Map;
69 import java.util.NoSuchElementException;
70 import java.util.Set;
71 import java.util.concurrent.ConcurrentHashMap;
72 import java.util.concurrent.ExecutionException;
73 import java.util.concurrent.Executor;
74 import java.util.concurrent.ExecutorService;
75 import java.util.concurrent.Executors;
76 import java.util.concurrent.Future;
77 import java.util.concurrent.TimeUnit;
78 import java.util.zip.CRC32;
79 import java.util.zip.CheckedOutputStream;
80 import java.util.zip.Deflater;
81 import java.util.zip.DeflaterOutputStream;
82
83 import org.eclipse.jgit.annotations.NonNull;
84 import org.eclipse.jgit.annotations.Nullable;
85 import org.eclipse.jgit.errors.CorruptObjectException;
86 import org.eclipse.jgit.errors.IncorrectObjectTypeException;
87 import org.eclipse.jgit.errors.LargeObjectException;
88 import org.eclipse.jgit.errors.MissingObjectException;
89 import org.eclipse.jgit.errors.StoredObjectRepresentationNotAvailableException;
90 import org.eclipse.jgit.internal.JGitText;
91 import org.eclipse.jgit.internal.storage.file.PackBitmapIndexBuilder;
92 import org.eclipse.jgit.internal.storage.file.PackBitmapIndexWriterV1;
93 import org.eclipse.jgit.internal.storage.file.PackIndexWriter;
94 import org.eclipse.jgit.lib.AnyObjectId;
95 import org.eclipse.jgit.lib.AsyncObjectSizeQueue;
96 import org.eclipse.jgit.lib.BatchingProgressMonitor;
97 import org.eclipse.jgit.lib.BitmapIndex;
98 import org.eclipse.jgit.lib.BitmapIndex.BitmapBuilder;
99 import org.eclipse.jgit.lib.BitmapObject;
100 import org.eclipse.jgit.lib.Constants;
101 import org.eclipse.jgit.lib.NullProgressMonitor;
102 import org.eclipse.jgit.lib.ObjectId;
103 import org.eclipse.jgit.lib.ObjectIdOwnerMap;
104 import org.eclipse.jgit.lib.ObjectIdSet;
105 import org.eclipse.jgit.lib.ObjectLoader;
106 import org.eclipse.jgit.lib.ObjectReader;
107 import org.eclipse.jgit.lib.ProgressMonitor;
108 import org.eclipse.jgit.lib.Repository;
109 import org.eclipse.jgit.lib.ThreadSafeProgressMonitor;
110 import org.eclipse.jgit.revwalk.AsyncRevObjectQueue;
111 import org.eclipse.jgit.revwalk.BitmapWalker;
112 import org.eclipse.jgit.revwalk.DepthWalk;
113 import org.eclipse.jgit.revwalk.ObjectWalk;
114 import org.eclipse.jgit.revwalk.RevCommit;
115 import org.eclipse.jgit.revwalk.RevFlag;
116 import org.eclipse.jgit.revwalk.RevObject;
117 import org.eclipse.jgit.revwalk.RevSort;
118 import org.eclipse.jgit.revwalk.RevTag;
119 import org.eclipse.jgit.revwalk.RevTree;
120 import org.eclipse.jgit.storage.pack.PackConfig;
121 import org.eclipse.jgit.storage.pack.PackStatistics;
122 import org.eclipse.jgit.transport.ObjectCountCallback;
123 import org.eclipse.jgit.transport.WriteAbortedException;
124 import org.eclipse.jgit.util.BlockList;
125 import org.eclipse.jgit.util.TemporaryBuffer;
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166 public class PackWriter implements AutoCloseable {
167 private static final int PACK_VERSION_GENERATED = 2;
168
169
170 public static final Set<ObjectId> NONE = Collections.emptySet();
171
172 private static final Map<WeakReference<PackWriter>, Boolean> instances =
173 new ConcurrentHashMap<>();
174
175 private static final Iterable<PackWriter> instancesIterable = new Iterable<PackWriter>() {
176 @Override
177 public Iterator<PackWriter> iterator() {
178 return new Iterator<PackWriter>() {
179 private final Iterator<WeakReference<PackWriter>> it =
180 instances.keySet().iterator();
181 private PackWriter next;
182
183 @Override
184 public boolean hasNext() {
185 if (next != null)
186 return true;
187 while (it.hasNext()) {
188 WeakReference<PackWriter> ref = it.next();
189 next = ref.get();
190 if (next != null)
191 return true;
192 it.remove();
193 }
194 return false;
195 }
196
197 @Override
198 public PackWriter next() {
199 if (hasNext()) {
200 PackWriter result = next;
201 next = null;
202 return result;
203 }
204 throw new NoSuchElementException();
205 }
206
207 @Override
208 public void remove() {
209 throw new UnsupportedOperationException();
210 }
211 };
212 }
213 };
214
215
216
217
218
219
220 public static Iterable<PackWriter> getInstances() {
221 return instancesIterable;
222 }
223
224 @SuppressWarnings("unchecked")
225 BlockList<ObjectToPack> objectsLists[] = new BlockList[OBJ_TAG + 1];
226 {
227 objectsLists[OBJ_COMMIT] = new BlockList<>();
228 objectsLists[OBJ_TREE] = new BlockList<>();
229 objectsLists[OBJ_BLOB] = new BlockList<>();
230 objectsLists[OBJ_TAG] = new BlockList<>();
231 }
232
233 private ObjectIdOwnerMap<ObjectToPack> objectsMap = new ObjectIdOwnerMap<>();
234
235
236 private List<ObjectToPack> edgeObjects = new BlockList<>();
237
238
239 private BitmapBuilder haveObjects;
240
241 private List<CachedPack> cachedPacks = new ArrayList<>(2);
242
243 private Set<ObjectId> tagTargets = NONE;
244
245 private Set<? extends ObjectId> excludeFromBitmapSelection = NONE;
246
247 private ObjectIdSet[] excludeInPacks;
248
249 private ObjectIdSet excludeInPackLast;
250
251 private Deflater myDeflater;
252
253 private final ObjectReader reader;
254
255
256 private final ObjectReuseAsIs reuseSupport;
257
258 final PackConfig config;
259
260 private final PackStatistics.Accumulator stats;
261
262 private final MutableState state;
263
264 private final WeakReference<PackWriter> selfRef;
265
266 private PackStatistics.ObjectType.Accumulator typeStats;
267
268 private List<ObjectToPack> sortedByName;
269
270 private byte packcsum[];
271
272 private boolean deltaBaseAsOffset;
273
274 private boolean reuseDeltas;
275
276 private boolean reuseDeltaCommits;
277
278 private boolean reuseValidate;
279
280 private boolean thin;
281
282 private boolean useCachedPacks;
283
284 private boolean useBitmaps;
285
286 private boolean ignoreMissingUninteresting = true;
287
288 private boolean pruneCurrentObjectList;
289
290 private boolean shallowPack;
291
292 private boolean canBuildBitmaps;
293
294 private boolean indexDisabled;
295
296 private int depth;
297
298 private Collection<? extends ObjectId> unshallowObjects;
299
300 private PackBitmapIndexBuilder writeBitmaps;
301
302 private CRC32 crc32;
303
304 private ObjectCountCallback callback;
305
306
307
308
309
310
311
312
313
314
315 public PackWriter(final Repository repo) {
316 this(repo, repo.newObjectReader());
317 }
318
319
320
321
322
323
324
325
326
327
328 public PackWriter(final ObjectReader reader) {
329 this(new PackConfig(), reader);
330 }
331
332
333
334
335
336
337
338
339
340
341
342
343 public PackWriter(final Repository repo, final ObjectReader reader) {
344 this(new PackConfig(repo), reader);
345 }
346
347
348
349
350
351
352
353
354
355
356
357
358 public PackWriter(final PackConfig config, final ObjectReader reader) {
359 this(config, reader, null);
360 }
361
362
363
364
365
366
367
368
369
370
371
372
373
374
375 public PackWriter(PackConfig config, final ObjectReader reader,
376 @Nullable PackStatistics.Accumulator statsAccumulator) {
377 this.config = config;
378 this.reader = reader;
379 if (reader instanceof ObjectReuseAsIs)
380 reuseSupport = ((ObjectReuseAsIs) reader);
381 else
382 reuseSupport = null;
383
384 deltaBaseAsOffset = config.isDeltaBaseAsOffset();
385 reuseDeltas = config.isReuseDeltas();
386 reuseValidate = true;
387 stats = statsAccumulator != null ? statsAccumulator
388 : new PackStatistics.Accumulator();
389 state = new MutableState();
390 selfRef = new WeakReference<>(this);
391 instances.put(selfRef, Boolean.TRUE);
392 }
393
394
395
396
397
398
399
400
401
402
403
404 public PackWriter setObjectCountCallback(ObjectCountCallback callback) {
405 this.callback = callback;
406 return this;
407 }
408
409
410
411
412
413
414
415 public void setClientShallowCommits(Set<ObjectId> clientShallowCommits) {
416 stats.clientShallowCommits = Collections
417 .unmodifiableSet(new HashSet<>(clientShallowCommits));
418 }
419
420
421
422
423
424
425
426
427
428
429
430 public boolean isDeltaBaseAsOffset() {
431 return deltaBaseAsOffset;
432 }
433
434
435
436
437
438
439
440
441
442
443
444
445 public void setDeltaBaseAsOffset(boolean deltaBaseAsOffset) {
446 this.deltaBaseAsOffset = deltaBaseAsOffset;
447 }
448
449
450
451
452
453
454
455 public boolean isReuseDeltaCommits() {
456 return reuseDeltaCommits;
457 }
458
459
460
461
462
463
464
465
466 public void setReuseDeltaCommits(boolean reuse) {
467 reuseDeltaCommits = reuse;
468 }
469
470
471
472
473
474
475
476 public boolean isReuseValidatingObjects() {
477 return reuseValidate;
478 }
479
480
481
482
483
484
485
486
487
488
489 public void setReuseValidatingObjects(boolean validate) {
490 reuseValidate = validate;
491 }
492
493
494
495
496
497
498 public boolean isThin() {
499 return thin;
500 }
501
502
503
504
505
506
507
508
509
510
511
512
513 public void setThin(final boolean packthin) {
514 thin = packthin;
515 }
516
517
518
519
520
521
522
523 public boolean isUseCachedPacks() {
524 return useCachedPacks;
525 }
526
527
528
529
530
531
532
533
534
535
536
537
538 public void setUseCachedPacks(boolean useCached) {
539 useCachedPacks = useCached;
540 }
541
542
543
544
545
546
547 public boolean isUseBitmaps() {
548 return useBitmaps;
549 }
550
551
552
553
554
555
556
557 public void setUseBitmaps(boolean useBitmaps) {
558 this.useBitmaps = useBitmaps;
559 }
560
561
562
563
564
565
566
567 public boolean isIndexDisabled() {
568 return indexDisabled || !cachedPacks.isEmpty();
569 }
570
571
572
573
574
575
576
577 public void setIndexDisabled(boolean noIndex) {
578 this.indexDisabled = noIndex;
579 }
580
581
582
583
584
585
586
587
588
589
590
591 public boolean isIgnoreMissingUninteresting() {
592 return ignoreMissingUninteresting;
593 }
594
595
596
597
598
599
600
601
602
603
604 public void setIgnoreMissingUninteresting(final boolean ignore) {
605 ignoreMissingUninteresting = ignore;
606 }
607
608
609
610
611
612
613
614
615
616
617
618
619
620 public void setTagTargets(Set<ObjectId> objects) {
621 tagTargets = objects;
622 }
623
624
625
626
627
628
629
630
631
632
633
634 public void setShallowPack(int depth,
635 Collection<? extends ObjectId> unshallow) {
636 this.shallowPack = true;
637 this.depth = depth;
638 this.unshallowObjects = unshallow;
639 }
640
641
642
643
644
645
646
647
648 public long getObjectCount() throws IOException {
649 if (stats.totalObjects == 0) {
650 long objCnt = 0;
651
652 objCnt += objectsLists[OBJ_COMMIT].size();
653 objCnt += objectsLists[OBJ_TREE].size();
654 objCnt += objectsLists[OBJ_BLOB].size();
655 objCnt += objectsLists[OBJ_TAG].size();
656
657 for (CachedPack pack : cachedPacks)
658 objCnt += pack.getObjectCount();
659 return objCnt;
660 }
661 return stats.totalObjects;
662 }
663
664
665
666
667
668
669
670
671
672
673
674
675 public ObjectIdOwnerMap<ObjectIdOwnerMap.Entry> getObjectSet()
676 throws IOException {
677 if (!cachedPacks.isEmpty())
678 throw new IOException(
679 JGitText.get().cachedPacksPreventsListingObjects);
680
681 if (writeBitmaps != null) {
682 return writeBitmaps.getObjectSet();
683 }
684
685 ObjectIdOwnerMap<ObjectIdOwnerMap.Entry> r = new ObjectIdOwnerMap<>();
686 for (BlockList<ObjectToPack> objList : objectsLists) {
687 if (objList != null) {
688 for (ObjectToPack otp : objList)
689 r.add(new ObjectIdOwnerMap.Entry(otp) {
690
691 });
692 }
693 }
694 return r;
695 }
696
697
698
699
700
701
702
703 public void excludeObjects(ObjectIdSet idx) {
704 if (excludeInPacks == null) {
705 excludeInPacks = new ObjectIdSet[] { idx };
706 excludeInPackLast = idx;
707 } else {
708 int cnt = excludeInPacks.length;
709 ObjectIdSet[] newList = new ObjectIdSet[cnt + 1];
710 System.arraycopy(excludeInPacks, 0, newList, 0, cnt);
711 newList[cnt] = idx;
712 excludeInPacks = newList;
713 }
714 }
715
716
717
718
719
720
721
722
723
724
725
726
727
728
729
730
731
732
733
734
735
736
737
738
739
740
741 public void preparePack(@NonNull Iterator<RevObject> objectsSource)
742 throws IOException {
743 while (objectsSource.hasNext()) {
744 addObject(objectsSource.next());
745 }
746 }
747
748
749
750
751
752
753
754
755
756
757
758
759
760
761
762
763
764
765
766
767
768
769
770
771
772
773 public void preparePack(ProgressMonitor countingMonitor,
774 @NonNull Set<? extends ObjectId> want,
775 @NonNull Set<? extends ObjectId> have) throws IOException {
776 preparePack(countingMonitor, want, have, NONE, NONE);
777 }
778
779
780
781
782
783
784
785
786
787
788
789
790
791
792
793
794
795
796
797
798
799
800
801
802
803
804
805
806
807
808 public void preparePack(ProgressMonitor countingMonitor,
809 @NonNull Set<? extends ObjectId> want,
810 @NonNull Set<? extends ObjectId> have,
811 @NonNull Set<? extends ObjectId> shallow) throws IOException {
812 preparePack(countingMonitor, want, have, shallow, NONE);
813 }
814
815
816
817
818
819
820
821
822
823
824
825
826
827
828
829
830
831
832
833
834
835
836
837
838
839
840
841
842
843
844
845
846
847 public void preparePack(ProgressMonitor countingMonitor,
848 @NonNull Set<? extends ObjectId> want,
849 @NonNull Set<? extends ObjectId> have,
850 @NonNull Set<? extends ObjectId> shallow,
851 @NonNull Set<? extends ObjectId> noBitmaps) throws IOException {
852 try (ObjectWalk ow = getObjectWalk()) {
853 ow.assumeShallow(shallow);
854 preparePack(countingMonitor, ow, want, have, noBitmaps);
855 }
856 }
857
858 private ObjectWalk getObjectWalk() {
859 return shallowPack ? new DepthWalk.ObjectWalk(reader, depth - 1)
860 : new ObjectWalk(reader);
861 }
862
863
864
865
866
867
868
869
870
871
872
873
874
875
876
877
878
879
880
881
882
883
884
885
886
887
888
889
890
891
892
893 public void preparePack(ProgressMonitor countingMonitor,
894 @NonNull ObjectWalk walk,
895 @NonNull Set<? extends ObjectId> interestingObjects,
896 @NonNull Set<? extends ObjectId> uninterestingObjects,
897 @NonNull Set<? extends ObjectId> noBitmaps)
898 throws IOException {
899 if (countingMonitor == null)
900 countingMonitor = NullProgressMonitor.INSTANCE;
901 if (shallowPack && !(walk instanceof DepthWalk.ObjectWalk))
902 throw new IllegalArgumentException(
903 JGitText.get().shallowPacksRequireDepthWalk);
904 findObjectsToPack(countingMonitor, walk, interestingObjects,
905 uninterestingObjects, noBitmaps);
906 }
907
908
909
910
911
912
913
914
915
916
917 public boolean willInclude(final AnyObjectId id) throws IOException {
918 ObjectToPack obj = objectsMap.get(id);
919 return obj != null && !obj.isEdge();
920 }
921
922
923
924
925
926
927
928
929 public ObjectToPack get(AnyObjectId id) {
930 ObjectToPack obj = objectsMap.get(id);
931 return obj != null && !obj.isEdge() ? obj : null;
932 }
933
934
935
936
937
938
939
940 public ObjectId computeName() {
941 final byte[] buf = new byte[OBJECT_ID_LENGTH];
942 final MessageDigest md = Constants.newMessageDigest();
943 for (ObjectToPack otp : sortByName()) {
944 otp.copyRawTo(buf, 0);
945 md.update(buf, 0, OBJECT_ID_LENGTH);
946 }
947 return ObjectId.fromRaw(md.digest());
948 }
949
950
951
952
953
954
955
956
957
958
959 public int getIndexVersion() {
960 int indexVersion = config.getIndexVersion();
961 if (indexVersion <= 0) {
962 for (BlockList<ObjectToPack> objs : objectsLists)
963 indexVersion = Math.max(indexVersion,
964 PackIndexWriter.oldestPossibleFormat(objs));
965 }
966 return indexVersion;
967 }
968
969
970
971
972
973
974
975
976
977
978
979
980
981
982
983
984 public void writeIndex(final OutputStream indexStream) throws IOException {
985 if (isIndexDisabled())
986 throw new IOException(JGitText.get().cachedPacksPreventsIndexCreation);
987
988 long writeStart = System.currentTimeMillis();
989 final PackIndexWriter iw = PackIndexWriter.createVersion(
990 indexStream, getIndexVersion());
991 iw.write(sortByName(), packcsum);
992 stats.timeWriting += System.currentTimeMillis() - writeStart;
993 }
994
995
996
997
998
999
1000
1001
1002
1003
1004
1005
1006 public void writeBitmapIndex(final OutputStream bitmapIndexStream)
1007 throws IOException {
1008 if (writeBitmaps == null)
1009 throw new IOException(JGitText.get().bitmapsMustBePrepared);
1010
1011 long writeStart = System.currentTimeMillis();
1012 final PackBitmapIndexWriterV1 iw = new PackBitmapIndexWriterV1(bitmapIndexStream);
1013 iw.write(writeBitmaps, packcsum);
1014 stats.timeWriting += System.currentTimeMillis() - writeStart;
1015 }
1016
1017 private List<ObjectToPack> sortByName() {
1018 if (sortedByName == null) {
1019 int cnt = 0;
1020 cnt += objectsLists[OBJ_COMMIT].size();
1021 cnt += objectsLists[OBJ_TREE].size();
1022 cnt += objectsLists[OBJ_BLOB].size();
1023 cnt += objectsLists[OBJ_TAG].size();
1024
1025 sortedByName = new BlockList<>(cnt);
1026 sortedByName.addAll(objectsLists[OBJ_COMMIT]);
1027 sortedByName.addAll(objectsLists[OBJ_TREE]);
1028 sortedByName.addAll(objectsLists[OBJ_BLOB]);
1029 sortedByName.addAll(objectsLists[OBJ_TAG]);
1030 Collections.sort(sortedByName);
1031 }
1032 return sortedByName;
1033 }
1034
1035 private void beginPhase(PackingPhase phase, ProgressMonitor monitor,
1036 long cnt) {
1037 state.phase = phase;
1038 String task;
1039 switch (phase) {
1040 case COUNTING:
1041 task = JGitText.get().countingObjects;
1042 break;
1043 case GETTING_SIZES:
1044 task = JGitText.get().searchForSizes;
1045 break;
1046 case FINDING_SOURCES:
1047 task = JGitText.get().searchForReuse;
1048 break;
1049 case COMPRESSING:
1050 task = JGitText.get().compressingObjects;
1051 break;
1052 case WRITING:
1053 task = JGitText.get().writingObjects;
1054 break;
1055 case BUILDING_BITMAPS:
1056 task = JGitText.get().buildingBitmaps;
1057 break;
1058 default:
1059 throw new IllegalArgumentException(
1060 MessageFormat.format(JGitText.get().illegalPackingPhase, phase));
1061 }
1062 monitor.beginTask(task, (int) cnt);
1063 }
1064
1065 private void endPhase(ProgressMonitor monitor) {
1066 monitor.endTask();
1067 }
1068
1069
1070
1071
1072
1073
1074
1075
1076
1077
1078
1079
1080
1081
1082
1083
1084
1085
1086
1087
1088
1089
1090
1091
1092
1093
1094
1095
1096 public void writePack(ProgressMonitor compressMonitor,
1097 ProgressMonitor writeMonitor, OutputStream packStream)
1098 throws IOException {
1099 if (compressMonitor == null)
1100 compressMonitor = NullProgressMonitor.INSTANCE;
1101 if (writeMonitor == null)
1102 writeMonitor = NullProgressMonitor.INSTANCE;
1103
1104 excludeInPacks = null;
1105 excludeInPackLast = null;
1106
1107 boolean needSearchForReuse = reuseSupport != null && (
1108 reuseDeltas
1109 || config.isReuseObjects()
1110 || !cachedPacks.isEmpty());
1111
1112 if (compressMonitor instanceof BatchingProgressMonitor) {
1113 long delay = 1000;
1114 if (needSearchForReuse && config.isDeltaCompress())
1115 delay = 500;
1116 ((BatchingProgressMonitor) compressMonitor).setDelayStart(
1117 delay,
1118 TimeUnit.MILLISECONDS);
1119 }
1120
1121 if (needSearchForReuse)
1122 searchForReuse(compressMonitor);
1123 if (config.isDeltaCompress())
1124 searchForDeltas(compressMonitor);
1125
1126 crc32 = new CRC32();
1127 final PackOutputStream out = new PackOutputStream(
1128 writeMonitor,
1129 isIndexDisabled()
1130 ? packStream
1131 : new CheckedOutputStream(packStream, crc32),
1132 this);
1133
1134 long objCnt = getObjectCount();
1135 stats.totalObjects = objCnt;
1136 if (callback != null)
1137 callback.setObjectCount(objCnt);
1138 beginPhase(PackingPhase.WRITING, writeMonitor, objCnt);
1139 long writeStart = System.currentTimeMillis();
1140 try {
1141 out.writeFileHeader(PACK_VERSION_GENERATED, objCnt);
1142 out.flush();
1143
1144 writeObjects(out);
1145 if (!edgeObjects.isEmpty() || !cachedPacks.isEmpty()) {
1146 for (PackStatistics.ObjectType.Accumulator typeStat : stats.objectTypes) {
1147 if (typeStat == null)
1148 continue;
1149 stats.thinPackBytes += typeStat.bytes;
1150 }
1151 }
1152
1153 stats.reusedPacks = Collections.unmodifiableList(cachedPacks);
1154 for (CachedPack pack : cachedPacks) {
1155 long deltaCnt = pack.getDeltaCount();
1156 stats.reusedObjects += pack.getObjectCount();
1157 stats.reusedDeltas += deltaCnt;
1158 stats.totalDeltas += deltaCnt;
1159 reuseSupport.copyPackAsIs(out, pack);
1160 }
1161 writeChecksum(out);
1162 out.flush();
1163 } finally {
1164 stats.timeWriting = System.currentTimeMillis() - writeStart;
1165 stats.depth = depth;
1166
1167 for (PackStatistics.ObjectType.Accumulator typeStat : stats.objectTypes) {
1168 if (typeStat == null)
1169 continue;
1170 typeStat.cntDeltas += typeStat.reusedDeltas;
1171 stats.reusedObjects += typeStat.reusedObjects;
1172 stats.reusedDeltas += typeStat.reusedDeltas;
1173 stats.totalDeltas += typeStat.cntDeltas;
1174 }
1175 }
1176
1177 stats.totalBytes = out.length();
1178 reader.close();
1179 endPhase(writeMonitor);
1180 }
1181
1182
1183
1184
1185
1186
1187
1188
1189
1190 public PackStatistics getStatistics() {
1191 return new PackStatistics(stats);
1192 }
1193
1194
1195
1196
1197
1198
1199 public State getState() {
1200 return state.snapshot();
1201 }
1202
1203
1204
1205
1206
1207
1208 @Override
1209 public void close() {
1210 reader.close();
1211 if (myDeflater != null) {
1212 myDeflater.end();
1213 myDeflater = null;
1214 }
1215 instances.remove(selfRef);
1216 }
1217
1218 private void searchForReuse(ProgressMonitor monitor) throws IOException {
1219 long cnt = 0;
1220 cnt += objectsLists[OBJ_COMMIT].size();
1221 cnt += objectsLists[OBJ_TREE].size();
1222 cnt += objectsLists[OBJ_BLOB].size();
1223 cnt += objectsLists[OBJ_TAG].size();
1224
1225 long start = System.currentTimeMillis();
1226 beginPhase(PackingPhase.FINDING_SOURCES, monitor, cnt);
1227 if (cnt <= 4096) {
1228
1229 BlockList<ObjectToPack> tmp = new BlockList<>((int) cnt);
1230 tmp.addAll(objectsLists[OBJ_TAG]);
1231 tmp.addAll(objectsLists[OBJ_COMMIT]);
1232 tmp.addAll(objectsLists[OBJ_TREE]);
1233 tmp.addAll(objectsLists[OBJ_BLOB]);
1234 searchForReuse(monitor, tmp);
1235 if (pruneCurrentObjectList) {
1236
1237 pruneEdgesFromObjectList(objectsLists[OBJ_COMMIT]);
1238 pruneEdgesFromObjectList(objectsLists[OBJ_TREE]);
1239 pruneEdgesFromObjectList(objectsLists[OBJ_BLOB]);
1240 pruneEdgesFromObjectList(objectsLists[OBJ_TAG]);
1241 }
1242 } else {
1243 searchForReuse(monitor, objectsLists[OBJ_TAG]);
1244 searchForReuse(monitor, objectsLists[OBJ_COMMIT]);
1245 searchForReuse(monitor, objectsLists[OBJ_TREE]);
1246 searchForReuse(monitor, objectsLists[OBJ_BLOB]);
1247 }
1248 endPhase(monitor);
1249 stats.timeSearchingForReuse = System.currentTimeMillis() - start;
1250
1251 if (config.isReuseDeltas() && config.getCutDeltaChains()) {
1252 cutDeltaChains(objectsLists[OBJ_TREE]);
1253 cutDeltaChains(objectsLists[OBJ_BLOB]);
1254 }
1255 }
1256
1257 private void searchForReuse(ProgressMonitor monitor, List<ObjectToPack> list)
1258 throws IOException, MissingObjectException {
1259 pruneCurrentObjectList = false;
1260 reuseSupport.selectObjectRepresentation(this, monitor, list);
1261 if (pruneCurrentObjectList)
1262 pruneEdgesFromObjectList(list);
1263 }
1264
1265 private void cutDeltaChains(BlockList<ObjectToPack> list)
1266 throws IOException {
1267 int max = config.getMaxDeltaDepth();
1268 for (int idx = list.size() - 1; idx >= 0; idx--) {
1269 int d = 0;
1270 ObjectToPack b = list.get(idx).getDeltaBase();
1271 while (b != null) {
1272 if (d < b.getChainLength())
1273 break;
1274 b.setChainLength(++d);
1275 if (d >= max && b.isDeltaRepresentation()) {
1276 reselectNonDelta(b);
1277 break;
1278 }
1279 b = b.getDeltaBase();
1280 }
1281 }
1282 if (config.isDeltaCompress()) {
1283 for (ObjectToPack otp : list)
1284 otp.clearChainLength();
1285 }
1286 }
1287
1288 private void searchForDeltas(ProgressMonitor monitor)
1289 throws MissingObjectException, IncorrectObjectTypeException,
1290 IOException {
1291
1292
1293
1294
1295 ObjectToPack[] list = new ObjectToPack[
1296 objectsLists[OBJ_TREE].size()
1297 + objectsLists[OBJ_BLOB].size()
1298 + edgeObjects.size()];
1299 int cnt = 0;
1300 cnt = findObjectsNeedingDelta(list, cnt, OBJ_TREE);
1301 cnt = findObjectsNeedingDelta(list, cnt, OBJ_BLOB);
1302 if (cnt == 0)
1303 return;
1304 int nonEdgeCnt = cnt;
1305
1306
1307
1308
1309
1310 for (ObjectToPack eo : edgeObjects) {
1311 eo.setWeight(0);
1312 list[cnt++] = eo;
1313 }
1314
1315
1316
1317
1318
1319
1320
1321
1322 final long sizingStart = System.currentTimeMillis();
1323 beginPhase(PackingPhase.GETTING_SIZES, monitor, cnt);
1324 AsyncObjectSizeQueue<ObjectToPack> sizeQueue = reader.getObjectSize(
1325 Arrays.<ObjectToPack> asList(list).subList(0, cnt), false);
1326 try {
1327 final long limit = Math.min(
1328 config.getBigFileThreshold(),
1329 Integer.MAX_VALUE);
1330 for (;;) {
1331 try {
1332 if (!sizeQueue.next())
1333 break;
1334 } catch (MissingObjectException notFound) {
1335 monitor.update(1);
1336 if (ignoreMissingUninteresting) {
1337 ObjectToPack otp = sizeQueue.getCurrent();
1338 if (otp != null && otp.isEdge()) {
1339 otp.setDoNotDelta();
1340 continue;
1341 }
1342
1343 otp = objectsMap.get(notFound.getObjectId());
1344 if (otp != null && otp.isEdge()) {
1345 otp.setDoNotDelta();
1346 continue;
1347 }
1348 }
1349 throw notFound;
1350 }
1351
1352 ObjectToPack otp = sizeQueue.getCurrent();
1353 if (otp == null)
1354 otp = objectsMap.get(sizeQueue.getObjectId());
1355
1356 long sz = sizeQueue.getSize();
1357 if (DeltaIndex.BLKSZ < sz && sz < limit)
1358 otp.setWeight((int) sz);
1359 else
1360 otp.setDoNotDelta();
1361 monitor.update(1);
1362 }
1363 } finally {
1364 sizeQueue.release();
1365 }
1366 endPhase(monitor);
1367 stats.timeSearchingForSizes = System.currentTimeMillis() - sizingStart;
1368
1369
1370
1371
1372
1373
1374 Arrays.sort(list, 0, cnt, new Comparator<ObjectToPack>() {
1375 @Override
1376 public int compare(ObjectToPack a, ObjectToPack b) {
1377 int cmp = (a.isDoNotDelta() ? 1 : 0)
1378 - (b.isDoNotDelta() ? 1 : 0);
1379 if (cmp != 0)
1380 return cmp;
1381
1382 cmp = a.getType() - b.getType();
1383 if (cmp != 0)
1384 return cmp;
1385
1386 cmp = (a.getPathHash() >>> 1) - (b.getPathHash() >>> 1);
1387 if (cmp != 0)
1388 return cmp;
1389
1390 cmp = (a.getPathHash() & 1) - (b.getPathHash() & 1);
1391 if (cmp != 0)
1392 return cmp;
1393
1394 cmp = (a.isEdge() ? 0 : 1) - (b.isEdge() ? 0 : 1);
1395 if (cmp != 0)
1396 return cmp;
1397
1398 return b.getWeight() - a.getWeight();
1399 }
1400 });
1401
1402
1403
1404 while (0 < cnt && list[cnt - 1].isDoNotDelta()) {
1405 if (!list[cnt - 1].isEdge())
1406 nonEdgeCnt--;
1407 cnt--;
1408 }
1409 if (cnt == 0)
1410 return;
1411
1412 final long searchStart = System.currentTimeMillis();
1413 searchForDeltas(monitor, list, cnt);
1414 stats.deltaSearchNonEdgeObjects = nonEdgeCnt;
1415 stats.timeCompressing = System.currentTimeMillis() - searchStart;
1416
1417 for (int i = 0; i < cnt; i++)
1418 if (!list[i].isEdge() && list[i].isDeltaRepresentation())
1419 stats.deltasFound++;
1420 }
1421
1422 private int findObjectsNeedingDelta(ObjectToPack[] list, int cnt, int type) {
1423 for (ObjectToPack otp : objectsLists[type]) {
1424 if (otp.isDoNotDelta())
1425 continue;
1426 if (otp.isDeltaRepresentation())
1427 continue;
1428 otp.setWeight(0);
1429 list[cnt++] = otp;
1430 }
1431 return cnt;
1432 }
1433
1434 private void reselectNonDelta(ObjectToPack otp) throws IOException {
1435 otp.clearDeltaBase();
1436 otp.clearReuseAsIs();
1437 boolean old = reuseDeltas;
1438 reuseDeltas = false;
1439 reuseSupport.selectObjectRepresentation(this,
1440 NullProgressMonitor.INSTANCE,
1441 Collections.singleton(otp));
1442 reuseDeltas = old;
1443 }
1444
1445 private void searchForDeltas(final ProgressMonitor monitor,
1446 final ObjectToPack[] list, final int cnt)
1447 throws MissingObjectException, IncorrectObjectTypeException,
1448 LargeObjectException, IOException {
1449 int threads = config.getThreads();
1450 if (threads == 0)
1451 threads = Runtime.getRuntime().availableProcessors();
1452 if (threads <= 1 || cnt <= config.getDeltaSearchWindowSize())
1453 singleThreadDeltaSearch(monitor, list, cnt);
1454 else
1455 parallelDeltaSearch(monitor, list, cnt, threads);
1456 }
1457
1458 private void singleThreadDeltaSearch(ProgressMonitor monitor,
1459 ObjectToPack[] list, int cnt) throws IOException {
1460 long totalWeight = 0;
1461 for (int i = 0; i < cnt; i++) {
1462 ObjectToPack o = list[i];
1463 totalWeight += DeltaTask.getAdjustedWeight(o);
1464 }
1465
1466 long bytesPerUnit = 1;
1467 while (DeltaTask.MAX_METER <= (totalWeight / bytesPerUnit))
1468 bytesPerUnit <<= 10;
1469 int cost = (int) (totalWeight / bytesPerUnit);
1470 if (totalWeight % bytesPerUnit != 0)
1471 cost++;
1472
1473 beginPhase(PackingPhase.COMPRESSING, monitor, cost);
1474 new DeltaWindow(config, new DeltaCache(config), reader,
1475 monitor, bytesPerUnit,
1476 list, 0, cnt).search();
1477 endPhase(monitor);
1478 }
1479
1480 private void parallelDeltaSearch(ProgressMonitor monitor,
1481 ObjectToPack[] list, int cnt, int threads) throws IOException {
1482 DeltaCache dc = new ThreadSafeDeltaCache(config);
1483 ThreadSafeProgressMonitor pm = new ThreadSafeProgressMonitor(monitor);
1484 DeltaTask.Block taskBlock = new DeltaTask.Block(threads, config,
1485 reader, dc, pm,
1486 list, 0, cnt);
1487 taskBlock.partitionTasks();
1488 beginPhase(PackingPhase.COMPRESSING, monitor, taskBlock.cost());
1489 pm.startWorkers(taskBlock.tasks.size());
1490
1491 Executor executor = config.getExecutor();
1492 final List<Throwable> errors =
1493 Collections.synchronizedList(new ArrayList<Throwable>(threads));
1494 if (executor instanceof ExecutorService) {
1495
1496 runTasks((ExecutorService) executor, pm, taskBlock, errors);
1497 } else if (executor == null) {
1498
1499
1500 ExecutorService pool = Executors.newFixedThreadPool(threads);
1501 try {
1502 runTasks(pool, pm, taskBlock, errors);
1503 } finally {
1504 pool.shutdown();
1505 for (;;) {
1506 try {
1507 if (pool.awaitTermination(60, TimeUnit.SECONDS))
1508 break;
1509 } catch (InterruptedException e) {
1510 throw new IOException(
1511 JGitText.get().packingCancelledDuringObjectsWriting);
1512 }
1513 }
1514 }
1515 } else {
1516
1517
1518
1519 for (final DeltaTask task : taskBlock.tasks) {
1520 executor.execute(new Runnable() {
1521 @Override
1522 public void run() {
1523 try {
1524 task.call();
1525 } catch (Throwable failure) {
1526 errors.add(failure);
1527 }
1528 }
1529 });
1530 }
1531 try {
1532 pm.waitForCompletion();
1533 } catch (InterruptedException ie) {
1534
1535
1536
1537 throw new IOException(
1538 JGitText.get().packingCancelledDuringObjectsWriting);
1539 }
1540 }
1541
1542
1543
1544
1545 if (!errors.isEmpty()) {
1546 Throwable err = errors.get(0);
1547 if (err instanceof Error)
1548 throw (Error) err;
1549 if (err instanceof RuntimeException)
1550 throw (RuntimeException) err;
1551 if (err instanceof IOException)
1552 throw (IOException) err;
1553
1554 throw new IOException(err.getMessage(), err);
1555 }
1556 endPhase(monitor);
1557 }
1558
1559 private static void runTasks(ExecutorService pool,
1560 ThreadSafeProgressMonitor pm,
1561 DeltaTask.Block tb, List<Throwable> errors) throws IOException {
1562 List<Future<?>> futures = new ArrayList<>(tb.tasks.size());
1563 for (DeltaTask task : tb.tasks)
1564 futures.add(pool.submit(task));
1565
1566 try {
1567 pm.waitForCompletion();
1568 for (Future<?> f : futures) {
1569 try {
1570 f.get();
1571 } catch (ExecutionException failed) {
1572 errors.add(failed.getCause());
1573 }
1574 }
1575 } catch (InterruptedException ie) {
1576 for (Future<?> f : futures)
1577 f.cancel(true);
1578 throw new IOException(
1579 JGitText.get().packingCancelledDuringObjectsWriting);
1580 }
1581 }
1582
1583 private void writeObjects(PackOutputStream out) throws IOException {
1584 writeObjects(out, objectsLists[OBJ_COMMIT]);
1585 writeObjects(out, objectsLists[OBJ_TAG]);
1586 writeObjects(out, objectsLists[OBJ_TREE]);
1587 writeObjects(out, objectsLists[OBJ_BLOB]);
1588 }
1589
1590 private void writeObjects(PackOutputStream out, List<ObjectToPack> list)
1591 throws IOException {
1592 if (list.isEmpty())
1593 return;
1594
1595 typeStats = stats.objectTypes[list.get(0).getType()];
1596 long beginOffset = out.length();
1597
1598 if (reuseSupport != null) {
1599 reuseSupport.writeObjects(out, list);
1600 } else {
1601 for (ObjectToPack otp : list)
1602 out.writeObject(otp);
1603 }
1604
1605 typeStats.bytes += out.length() - beginOffset;
1606 typeStats.cntObjects = list.size();
1607 }
1608
1609 void writeObject(PackOutputStream out, ObjectToPack otp) throws IOException {
1610 if (!otp.isWritten())
1611 writeObjectImpl(out, otp);
1612 }
1613
1614 private void writeObjectImpl(PackOutputStream out, ObjectToPack otp)
1615 throws IOException {
1616 if (otp.wantWrite()) {
1617
1618
1619
1620
1621
1622 reselectNonDelta(otp);
1623 }
1624 otp.markWantWrite();
1625
1626 while (otp.isReuseAsIs()) {
1627 writeBase(out, otp.getDeltaBase());
1628 if (otp.isWritten())
1629 return;
1630
1631 crc32.reset();
1632 otp.setOffset(out.length());
1633 try {
1634 reuseSupport.copyObjectAsIs(out, otp, reuseValidate);
1635 out.endObject();
1636 otp.setCRC((int) crc32.getValue());
1637 typeStats.reusedObjects++;
1638 if (otp.isDeltaRepresentation()) {
1639 typeStats.reusedDeltas++;
1640 typeStats.deltaBytes += out.length() - otp.getOffset();
1641 }
1642 return;
1643 } catch (StoredObjectRepresentationNotAvailableException gone) {
1644 if (otp.getOffset() == out.length()) {
1645 otp.setOffset(0);
1646 otp.clearDeltaBase();
1647 otp.clearReuseAsIs();
1648 reuseSupport.selectObjectRepresentation(this,
1649 NullProgressMonitor.INSTANCE,
1650 Collections.singleton(otp));
1651 continue;
1652 } else {
1653
1654
1655 CorruptObjectException coe;
1656 coe = new CorruptObjectException(otp, "");
1657 coe.initCause(gone);
1658 throw coe;
1659 }
1660 }
1661 }
1662
1663
1664
1665 if (otp.isDeltaRepresentation())
1666 writeDeltaObjectDeflate(out, otp);
1667 else
1668 writeWholeObjectDeflate(out, otp);
1669 out.endObject();
1670 otp.setCRC((int) crc32.getValue());
1671 }
1672
1673 private void writeBase(PackOutputStream out, ObjectToPack base)
1674 throws IOException {
1675 if (base != null && !base.isWritten() && !base.isEdge())
1676 writeObjectImpl(out, base);
1677 }
1678
1679 private void writeWholeObjectDeflate(PackOutputStream out,
1680 final ObjectToPack otp) throws IOException {
1681 final Deflater deflater = deflater();
1682 final ObjectLoader ldr = reader.open(otp, otp.getType());
1683
1684 crc32.reset();
1685 otp.setOffset(out.length());
1686 out.writeHeader(otp, ldr.getSize());
1687
1688 deflater.reset();
1689 DeflaterOutputStream dst = new DeflaterOutputStream(out, deflater);
1690 ldr.copyTo(dst);
1691 dst.finish();
1692 }
1693
1694 private void writeDeltaObjectDeflate(PackOutputStream out,
1695 final ObjectToPack otp) throws IOException {
1696 writeBase(out, otp.getDeltaBase());
1697
1698 crc32.reset();
1699 otp.setOffset(out.length());
1700
1701 DeltaCache.Ref ref = otp.popCachedDelta();
1702 if (ref != null) {
1703 byte[] zbuf = ref.get();
1704 if (zbuf != null) {
1705 out.writeHeader(otp, otp.getCachedSize());
1706 out.write(zbuf);
1707 typeStats.cntDeltas++;
1708 typeStats.deltaBytes += out.length() - otp.getOffset();
1709 return;
1710 }
1711 }
1712
1713 try (TemporaryBuffer.Heap delta = delta(otp)) {
1714 out.writeHeader(otp, delta.length());
1715
1716 Deflater deflater = deflater();
1717 deflater.reset();
1718 DeflaterOutputStream dst = new DeflaterOutputStream(out, deflater);
1719 delta.writeTo(dst, null);
1720 dst.finish();
1721 }
1722 typeStats.cntDeltas++;
1723 typeStats.deltaBytes += out.length() - otp.getOffset();
1724 }
1725
1726 private TemporaryBuffer.Heap delta(final ObjectToPack otp)
1727 throws IOException {
1728 DeltaIndex index = new DeltaIndex(buffer(otp.getDeltaBaseId()));
1729 byte[] res = buffer(otp);
1730
1731
1732
1733
1734
1735 TemporaryBuffer.Heap delta = new TemporaryBuffer.Heap(res.length);
1736 index.encode(delta, res);
1737 return delta;
1738 }
1739
1740 private byte[] buffer(AnyObjectId objId) throws IOException {
1741 return buffer(config, reader, objId);
1742 }
1743
1744 static byte[] buffer(PackConfig config, ObjectReader or, AnyObjectId objId)
1745 throws IOException {
1746
1747
1748
1749
1750
1751 return or.open(objId).getCachedBytes(config.getBigFileThreshold());
1752 }
1753
1754 private Deflater deflater() {
1755 if (myDeflater == null)
1756 myDeflater = new Deflater(config.getCompressionLevel());
1757 return myDeflater;
1758 }
1759
1760 private void writeChecksum(PackOutputStream out) throws IOException {
1761 packcsum = out.getDigest();
1762 out.write(packcsum);
1763 }
1764
1765 private void findObjectsToPack(@NonNull ProgressMonitor countingMonitor,
1766 @NonNull ObjectWalk walker, @NonNull Set<? extends ObjectId> want,
1767 @NonNull Set<? extends ObjectId> have,
1768 @NonNull Set<? extends ObjectId> noBitmaps) throws IOException {
1769 final long countingStart = System.currentTimeMillis();
1770 beginPhase(PackingPhase.COUNTING, countingMonitor, ProgressMonitor.UNKNOWN);
1771
1772 stats.interestingObjects = Collections.unmodifiableSet(new HashSet<ObjectId>(want));
1773 stats.uninterestingObjects = Collections.unmodifiableSet(new HashSet<ObjectId>(have));
1774 excludeFromBitmapSelection = noBitmaps;
1775
1776 canBuildBitmaps = config.isBuildBitmaps()
1777 && !shallowPack
1778 && have.isEmpty()
1779 && (excludeInPacks == null || excludeInPacks.length == 0);
1780 if (!shallowPack && useBitmaps) {
1781 BitmapIndex bitmapIndex = reader.getBitmapIndex();
1782 if (bitmapIndex != null) {
1783 BitmapWalker bitmapWalker = new BitmapWalker(
1784 walker, bitmapIndex, countingMonitor);
1785 findObjectsToPackUsingBitmaps(bitmapWalker, want, have);
1786 endPhase(countingMonitor);
1787 stats.timeCounting = System.currentTimeMillis() - countingStart;
1788 stats.bitmapIndexMisses = bitmapWalker.getCountOfBitmapIndexMisses();
1789 return;
1790 }
1791 }
1792
1793 List<ObjectId> all = new ArrayList<>(want.size() + have.size());
1794 all.addAll(want);
1795 all.addAll(have);
1796
1797 final RevFlag include = walker.newFlag("include");
1798 final RevFlag added = walker.newFlag("added");
1799
1800 walker.carry(include);
1801
1802 int haveEst = have.size();
1803 if (have.isEmpty()) {
1804 walker.sort(RevSort.COMMIT_TIME_DESC);
1805 } else {
1806 walker.sort(RevSort.TOPO);
1807 if (thin)
1808 walker.sort(RevSort.BOUNDARY, true);
1809 }
1810
1811 List<RevObject> wantObjs = new ArrayList<>(want.size());
1812 List<RevObject> haveObjs = new ArrayList<>(haveEst);
1813 List<RevTag> wantTags = new ArrayList<>(want.size());
1814
1815
1816
1817 AsyncRevObjectQueue q = walker.parseAny(all, true);
1818 try {
1819 for (;;) {
1820 try {
1821 RevObject o = q.next();
1822 if (o == null)
1823 break;
1824 if (have.contains(o))
1825 haveObjs.add(o);
1826 if (want.contains(o)) {
1827 o.add(include);
1828 wantObjs.add(o);
1829 if (o instanceof RevTag)
1830 wantTags.add((RevTag) o);
1831 }
1832 } catch (MissingObjectException e) {
1833 if (ignoreMissingUninteresting
1834 && have.contains(e.getObjectId()))
1835 continue;
1836 throw e;
1837 }
1838 }
1839 } finally {
1840 q.release();
1841 }
1842
1843 if (!wantTags.isEmpty()) {
1844 all = new ArrayList<>(wantTags.size());
1845 for (RevTag tag : wantTags)
1846 all.add(tag.getObject());
1847 q = walker.parseAny(all, true);
1848 try {
1849 while (q.next() != null) {
1850
1851 }
1852 } finally {
1853 q.release();
1854 }
1855 }
1856
1857 if (walker instanceof DepthWalk.ObjectWalk) {
1858 DepthWalk.ObjectWalk depthWalk = (DepthWalk.ObjectWalk) walker;
1859 for (RevObject obj : wantObjs) {
1860 depthWalk.markRoot(obj);
1861 }
1862
1863
1864
1865
1866
1867 for (RevObject obj : haveObjs) {
1868 if (obj instanceof RevCommit) {
1869 RevTree t = ((RevCommit) obj).getTree();
1870 depthWalk.markUninteresting(t);
1871 }
1872 }
1873
1874 if (unshallowObjects != null) {
1875 for (ObjectId id : unshallowObjects) {
1876 depthWalk.markUnshallow(walker.parseAny(id));
1877 }
1878 }
1879 } else {
1880 for (RevObject obj : wantObjs)
1881 walker.markStart(obj);
1882 }
1883 for (RevObject obj : haveObjs)
1884 walker.markUninteresting(obj);
1885
1886 final int maxBases = config.getDeltaSearchWindowSize();
1887 Set<RevTree> baseTrees = new HashSet<>();
1888 BlockList<RevCommit> commits = new BlockList<>();
1889 Set<ObjectId> roots = new HashSet<>();
1890 RevCommit c;
1891 while ((c = walker.next()) != null) {
1892 if (exclude(c))
1893 continue;
1894 if (c.has(RevFlag.UNINTERESTING)) {
1895 if (baseTrees.size() <= maxBases)
1896 baseTrees.add(c.getTree());
1897 continue;
1898 }
1899
1900 commits.add(c);
1901 if (c.getParentCount() == 0) {
1902 roots.add(c.copy());
1903 }
1904 countingMonitor.update(1);
1905 }
1906 stats.rootCommits = Collections.unmodifiableSet(roots);
1907
1908 if (shallowPack) {
1909 for (RevCommit cmit : commits) {
1910 addObject(cmit, 0);
1911 }
1912 } else {
1913 int commitCnt = 0;
1914 boolean putTagTargets = false;
1915 for (RevCommit cmit : commits) {
1916 if (!cmit.has(added)) {
1917 cmit.add(added);
1918 addObject(cmit, 0);
1919 commitCnt++;
1920 }
1921
1922 for (int i = 0; i < cmit.getParentCount(); i++) {
1923 RevCommit p = cmit.getParent(i);
1924 if (!p.has(added) && !p.has(RevFlag.UNINTERESTING)
1925 && !exclude(p)) {
1926 p.add(added);
1927 addObject(p, 0);
1928 commitCnt++;
1929 }
1930 }
1931
1932 if (!putTagTargets && 4096 < commitCnt) {
1933 for (ObjectId id : tagTargets) {
1934 RevObject obj = walker.lookupOrNull(id);
1935 if (obj instanceof RevCommit
1936 && obj.has(include)
1937 && !obj.has(RevFlag.UNINTERESTING)
1938 && !obj.has(added)) {
1939 obj.add(added);
1940 addObject(obj, 0);
1941 }
1942 }
1943 putTagTargets = true;
1944 }
1945 }
1946 }
1947 commits = null;
1948
1949 if (thin && !baseTrees.isEmpty()) {
1950 BaseSearch bases = new BaseSearch(countingMonitor, baseTrees,
1951 objectsMap, edgeObjects, reader);
1952 RevObject o;
1953 while ((o = walker.nextObject()) != null) {
1954 if (o.has(RevFlag.UNINTERESTING))
1955 continue;
1956 if (exclude(o))
1957 continue;
1958
1959 int pathHash = walker.getPathHashCode();
1960 byte[] pathBuf = walker.getPathBuffer();
1961 int pathLen = walker.getPathLength();
1962 bases.addBase(o.getType(), pathBuf, pathLen, pathHash);
1963 addObject(o, pathHash);
1964 countingMonitor.update(1);
1965 }
1966 } else {
1967 RevObject o;
1968 while ((o = walker.nextObject()) != null) {
1969 if (o.has(RevFlag.UNINTERESTING))
1970 continue;
1971 if (exclude(o))
1972 continue;
1973 addObject(o, walker.getPathHashCode());
1974 countingMonitor.update(1);
1975 }
1976 }
1977
1978 for (CachedPack pack : cachedPacks)
1979 countingMonitor.update((int) pack.getObjectCount());
1980 endPhase(countingMonitor);
1981 stats.timeCounting = System.currentTimeMillis() - countingStart;
1982 stats.bitmapIndexMisses = -1;
1983 }
1984
1985 private void findObjectsToPackUsingBitmaps(
1986 BitmapWalker bitmapWalker, Set<? extends ObjectId> want,
1987 Set<? extends ObjectId> have)
1988 throws MissingObjectException, IncorrectObjectTypeException,
1989 IOException {
1990 BitmapBuilder haveBitmap = bitmapWalker.findObjects(have, null, true);
1991 BitmapBuilder wantBitmap = bitmapWalker.findObjects(want, haveBitmap,
1992 false);
1993 BitmapBuilder needBitmap = wantBitmap.andNot(haveBitmap);
1994
1995 if (useCachedPacks && reuseSupport != null && !reuseValidate
1996 && (excludeInPacks == null || excludeInPacks.length == 0))
1997 cachedPacks.addAll(
1998 reuseSupport.getCachedPacksAndUpdate(needBitmap));
1999
2000 for (BitmapObject obj : needBitmap) {
2001 ObjectId objectId = obj.getObjectId();
2002 if (exclude(objectId)) {
2003 needBitmap.remove(objectId);
2004 continue;
2005 }
2006 addObject(objectId, obj.getType(), 0);
2007 }
2008
2009 if (thin)
2010 haveObjects = haveBitmap;
2011 }
2012
2013 private static void pruneEdgesFromObjectList(List<ObjectToPack> list) {
2014 final int size = list.size();
2015 int src = 0;
2016 int dst = 0;
2017
2018 for (; src < size; src++) {
2019 ObjectToPack obj = list.get(src);
2020 if (obj.isEdge())
2021 continue;
2022 if (dst != src)
2023 list.set(dst, obj);
2024 dst++;
2025 }
2026
2027 while (dst < list.size())
2028 list.remove(list.size() - 1);
2029 }
2030
2031
2032
2033
2034
2035
2036
2037
2038
2039
2040
2041
2042
2043 public void addObject(final RevObject object)
2044 throws IncorrectObjectTypeException {
2045 if (!exclude(object))
2046 addObject(object, 0);
2047 }
2048
2049 private void addObject(final RevObject object, final int pathHashCode) {
2050 addObject(object, object.getType(), pathHashCode);
2051 }
2052
2053 private void addObject(
2054 final AnyObjectId src, final int type, final int pathHashCode) {
2055 final ObjectToPack otp;
2056 if (reuseSupport != null)
2057 otp = reuseSupport.newObjectToPack(src, type);
2058 else
2059 otp = new ObjectToPack(src, type);
2060 otp.setPathHash(pathHashCode);
2061 objectsLists[type].add(otp);
2062 objectsMap.add(otp);
2063 }
2064
2065 private boolean exclude(AnyObjectId objectId) {
2066 if (excludeInPacks == null)
2067 return false;
2068 if (excludeInPackLast.contains(objectId))
2069 return true;
2070 for (ObjectIdSet idx : excludeInPacks) {
2071 if (idx.contains(objectId)) {
2072 excludeInPackLast = idx;
2073 return true;
2074 }
2075 }
2076 return false;
2077 }
2078
2079
2080
2081
2082
2083
2084
2085
2086
2087
2088
2089
2090
2091 public void select(ObjectToPack otp, StoredObjectRepresentation next) {
2092 int nFmt = next.getFormat();
2093
2094 if (!cachedPacks.isEmpty()) {
2095 if (otp.isEdge())
2096 return;
2097 if ((nFmt == PACK_WHOLE) | (nFmt == PACK_DELTA)) {
2098 for (CachedPack pack : cachedPacks) {
2099 if (pack.hasObject(otp, next)) {
2100 otp.setEdge();
2101 otp.clearDeltaBase();
2102 otp.clearReuseAsIs();
2103 pruneCurrentObjectList = true;
2104 return;
2105 }
2106 }
2107 }
2108 }
2109
2110 if (nFmt == PACK_DELTA && reuseDeltas && reuseDeltaFor(otp)) {
2111 ObjectId baseId = next.getDeltaBase();
2112 ObjectToPack ptr = objectsMap.get(baseId);
2113 if (ptr != null && !ptr.isEdge()) {
2114 otp.setDeltaBase(ptr);
2115 otp.setReuseAsIs();
2116 } else if (thin && have(ptr, baseId)) {
2117 otp.setDeltaBase(baseId);
2118 otp.setReuseAsIs();
2119 } else {
2120 otp.clearDeltaBase();
2121 otp.clearReuseAsIs();
2122 }
2123 } else if (nFmt == PACK_WHOLE && config.isReuseObjects()) {
2124 int nWeight = next.getWeight();
2125 if (otp.isReuseAsIs() && !otp.isDeltaRepresentation()) {
2126
2127
2128
2129 if (otp.getWeight() <= nWeight)
2130 return;
2131 }
2132 otp.clearDeltaBase();
2133 otp.setReuseAsIs();
2134 otp.setWeight(nWeight);
2135 } else {
2136 otp.clearDeltaBase();
2137 otp.clearReuseAsIs();
2138 }
2139
2140 otp.setDeltaAttempted(reuseDeltas & next.wasDeltaAttempted());
2141 otp.select(next);
2142 }
2143
2144 private final boolean have(ObjectToPack ptr, AnyObjectId objectId) {
2145 return (ptr != null && ptr.isEdge())
2146 || (haveObjects != null && haveObjects.contains(objectId));
2147 }
2148
2149
2150
2151
2152
2153
2154
2155
2156
2157
2158
2159
2160
2161
2162
2163
2164
2165
2166
2167
2168 public boolean prepareBitmapIndex(ProgressMonitor pm) throws IOException {
2169 if (!canBuildBitmaps || getObjectCount() > Integer.MAX_VALUE
2170 || !cachedPacks.isEmpty())
2171 return false;
2172
2173 if (pm == null)
2174 pm = NullProgressMonitor.INSTANCE;
2175
2176 int numCommits = objectsLists[OBJ_COMMIT].size();
2177 List<ObjectToPack> byName = sortByName();
2178 sortedByName = null;
2179 objectsLists = null;
2180 objectsMap = null;
2181 writeBitmaps = new PackBitmapIndexBuilder(byName);
2182 byName = null;
2183
2184 PackWriterBitmapPreparer bitmapPreparer = new PackWriterBitmapPreparer(
2185 reader, writeBitmaps, pm, stats.interestingObjects, config);
2186
2187 Collection<PackWriterBitmapPreparer.BitmapCommit> selectedCommits = bitmapPreparer
2188 .selectCommits(numCommits, excludeFromBitmapSelection);
2189
2190 beginPhase(PackingPhase.BUILDING_BITMAPS, pm, selectedCommits.size());
2191
2192 BitmapWalker walker = bitmapPreparer.newBitmapWalker();
2193 AnyObjectId last = null;
2194 for (PackWriterBitmapPreparer.BitmapCommit cmit : selectedCommits) {
2195 if (!cmit.isReuseWalker()) {
2196 walker = bitmapPreparer.newBitmapWalker();
2197 }
2198 BitmapBuilder bitmap = walker.findObjects(
2199 Collections.singleton(cmit), null, false);
2200
2201 if (last != null && cmit.isReuseWalker() && !bitmap.contains(last))
2202 throw new IllegalStateException(MessageFormat.format(
2203 JGitText.get().bitmapMissingObject, cmit.name(),
2204 last.name()));
2205 last = cmit;
2206 writeBitmaps.addBitmap(cmit, bitmap.build(), cmit.getFlags());
2207
2208 pm.update(1);
2209 }
2210
2211 endPhase(pm);
2212 return true;
2213 }
2214
2215 private boolean reuseDeltaFor(ObjectToPack otp) {
2216 int type = otp.getType();
2217 if ((type & 2) != 0)
2218 return true;
2219 if (type == OBJ_COMMIT)
2220 return reuseDeltaCommits;
2221 if (type == OBJ_TAG)
2222 return false;
2223 return true;
2224 }
2225
2226
2227
2228
2229
2230
2231 @Deprecated
2232 public static class Statistics {
2233
2234 public static class ObjectType {
2235
2236 private PackStatistics.ObjectType objectType;
2237
2238
2239
2240
2241
2242
2243
2244
2245
2246 public ObjectType(PackStatistics.ObjectType type) {
2247 objectType = type;
2248 }
2249
2250
2251
2252
2253
2254 public long getObjects() {
2255 return objectType.getObjects();
2256 }
2257
2258
2259
2260
2261
2262 public long getDeltas() {
2263 return objectType.getDeltas();
2264 }
2265
2266
2267
2268
2269
2270
2271 public long getReusedObjects() {
2272 return objectType.getReusedObjects();
2273 }
2274
2275
2276
2277
2278
2279
2280
2281
2282 public long getReusedDeltas() {
2283 return objectType.getReusedDeltas();
2284 }
2285
2286
2287
2288
2289
2290
2291 public long getBytes() {
2292 return objectType.getBytes();
2293 }
2294
2295
2296
2297
2298
2299 public long getDeltaBytes() {
2300 return objectType.getDeltaBytes();
2301 }
2302 }
2303
2304
2305 private PackStatistics statistics;
2306
2307
2308
2309
2310
2311
2312
2313
2314 public Statistics(PackStatistics stats) {
2315 statistics = stats;
2316 }
2317
2318
2319
2320
2321
2322
2323 public Set<ObjectId> getInterestingObjects() {
2324 return statistics.getInterestingObjects();
2325 }
2326
2327
2328
2329
2330
2331
2332 public Set<ObjectId> getUninterestingObjects() {
2333 return statistics.getUninterestingObjects();
2334 }
2335
2336
2337
2338
2339
2340 public Collection<CachedPack> getReusedPacks() {
2341 return statistics.getReusedPacks();
2342 }
2343
2344
2345
2346
2347
2348 public int getDeltaSearchNonEdgeObjects() {
2349 return statistics.getDeltaSearchNonEdgeObjects();
2350 }
2351
2352
2353
2354
2355
2356
2357 public int getDeltasFound() {
2358 return statistics.getDeltasFound();
2359 }
2360
2361
2362
2363
2364
2365 public long getTotalObjects() {
2366 return statistics.getTotalObjects();
2367 }
2368
2369
2370
2371
2372
2373
2374 public long getBitmapIndexMisses() {
2375 return statistics.getBitmapIndexMisses();
2376 }
2377
2378
2379
2380
2381
2382 public long getTotalDeltas() {
2383 return statistics.getTotalDeltas();
2384 }
2385
2386
2387
2388
2389
2390 public long getReusedObjects() {
2391 return statistics.getReusedObjects();
2392 }
2393
2394
2395
2396
2397
2398
2399
2400 public long getReusedDeltas() {
2401 return statistics.getReusedDeltas();
2402 }
2403
2404
2405
2406
2407
2408 public long getTotalBytes() {
2409 return statistics.getTotalBytes();
2410 }
2411
2412
2413
2414
2415
2416
2417
2418
2419
2420 public long getThinPackBytes() {
2421 return statistics.getThinPackBytes();
2422 }
2423
2424
2425
2426
2427
2428
2429 public ObjectType byObjectType(int typeCode) {
2430 return new ObjectType(statistics.byObjectType(typeCode));
2431 }
2432
2433
2434 public boolean isShallow() {
2435 return statistics.isShallow();
2436 }
2437
2438
2439 public int getDepth() {
2440 return statistics.getDepth();
2441 }
2442
2443
2444
2445
2446
2447
2448 public long getTimeCounting() {
2449 return statistics.getTimeCounting();
2450 }
2451
2452
2453
2454
2455
2456
2457 public long getTimeSearchingForReuse() {
2458 return statistics.getTimeSearchingForReuse();
2459 }
2460
2461
2462
2463
2464
2465
2466
2467 public long getTimeSearchingForSizes() {
2468 return statistics.getTimeSearchingForSizes();
2469 }
2470
2471
2472
2473
2474
2475
2476
2477 public long getTimeCompressing() {
2478 return statistics.getTimeCompressing();
2479 }
2480
2481
2482
2483
2484
2485
2486
2487 public long getTimeWriting() {
2488 return statistics.getTimeWriting();
2489 }
2490
2491
2492 public long getTimeTotal() {
2493 return statistics.getTimeTotal();
2494 }
2495
2496
2497
2498
2499
2500 public double getTransferRate() {
2501 return statistics.getTransferRate();
2502 }
2503
2504
2505 public String getMessage() {
2506 return statistics.getMessage();
2507 }
2508 }
2509
2510 private class MutableState {
2511
2512
2513 private static final long OBJECT_TO_PACK_SIZE =
2514 (2 * 8)
2515 + (2 * 8) + (2 * 8)
2516 + (8 + 8)
2517 + 8
2518 + 40
2519 + 8;
2520
2521 private final long totalDeltaSearchBytes;
2522
2523 private volatile PackingPhase phase;
2524
2525 MutableState() {
2526 phase = PackingPhase.COUNTING;
2527 if (config.isDeltaCompress()) {
2528 int threads = config.getThreads();
2529 if (threads <= 0)
2530 threads = Runtime.getRuntime().availableProcessors();
2531 totalDeltaSearchBytes = (threads * config.getDeltaSearchMemoryLimit())
2532 + config.getBigFileThreshold();
2533 } else
2534 totalDeltaSearchBytes = 0;
2535 }
2536
2537 State snapshot() {
2538 long objCnt = 0;
2539 BlockList<ObjectToPack>[] lists = objectsLists;
2540 if (lists != null) {
2541 objCnt += lists[OBJ_COMMIT].size();
2542 objCnt += lists[OBJ_TREE].size();
2543 objCnt += lists[OBJ_BLOB].size();
2544 objCnt += lists[OBJ_TAG].size();
2545
2546 }
2547
2548 long bytesUsed = OBJECT_TO_PACK_SIZE * objCnt;
2549 PackingPhase curr = phase;
2550 if (curr == PackingPhase.COMPRESSING)
2551 bytesUsed += totalDeltaSearchBytes;
2552 return new State(curr, bytesUsed);
2553 }
2554 }
2555
2556
2557 public static enum PackingPhase {
2558
2559 COUNTING,
2560
2561
2562 GETTING_SIZES,
2563
2564
2565 FINDING_SOURCES,
2566
2567
2568 COMPRESSING,
2569
2570
2571 WRITING,
2572
2573
2574 BUILDING_BITMAPS;
2575 }
2576
2577
2578 public class State {
2579 private final PackingPhase phase;
2580
2581 private final long bytesUsed;
2582
2583 State(PackingPhase phase, long bytesUsed) {
2584 this.phase = phase;
2585 this.bytesUsed = bytesUsed;
2586 }
2587
2588
2589 public PackConfig getConfig() {
2590 return config;
2591 }
2592
2593
2594 public PackingPhase getPhase() {
2595 return phase;
2596 }
2597
2598
2599 public long estimateBytesUsed() {
2600 return bytesUsed;
2601 }
2602
2603 @SuppressWarnings("nls")
2604 @Override
2605 public String toString() {
2606 return "PackWriter.State[" + phase + ", memory=" + bytesUsed + "]";
2607 }
2608 }
2609 }