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.file;
45
46 import static org.eclipse.jgit.internal.storage.pack.PackExt.INDEX;
47 import static org.eclipse.jgit.internal.storage.pack.PackExt.PACK;
48
49 import java.io.BufferedReader;
50 import java.io.File;
51 import java.io.FileInputStream;
52 import java.io.FileNotFoundException;
53 import java.io.FileReader;
54 import java.io.IOException;
55 import java.nio.file.AtomicMoveNotSupportedException;
56 import java.nio.file.Files;
57 import java.nio.file.StandardCopyOption;
58 import java.text.MessageFormat;
59 import java.util.ArrayList;
60 import java.util.Arrays;
61 import java.util.Collection;
62 import java.util.Collections;
63 import java.util.HashMap;
64 import java.util.HashSet;
65 import java.util.List;
66 import java.util.Map;
67 import java.util.Set;
68 import java.util.concurrent.atomic.AtomicReference;
69
70 import org.eclipse.jgit.errors.CorruptObjectException;
71 import org.eclipse.jgit.errors.PackInvalidException;
72 import org.eclipse.jgit.errors.PackMismatchException;
73 import org.eclipse.jgit.internal.JGitText;
74 import org.eclipse.jgit.internal.storage.pack.ObjectToPack;
75 import org.eclipse.jgit.internal.storage.pack.PackExt;
76 import org.eclipse.jgit.internal.storage.pack.PackWriter;
77 import org.eclipse.jgit.lib.AbbreviatedObjectId;
78 import org.eclipse.jgit.lib.AnyObjectId;
79 import org.eclipse.jgit.lib.Config;
80 import org.eclipse.jgit.lib.ConfigConstants;
81 import org.eclipse.jgit.lib.Constants;
82 import org.eclipse.jgit.lib.ObjectDatabase;
83 import org.eclipse.jgit.lib.ObjectId;
84 import org.eclipse.jgit.lib.ObjectLoader;
85 import org.eclipse.jgit.lib.RepositoryCache;
86 import org.eclipse.jgit.lib.RepositoryCache.FileKey;
87 import org.eclipse.jgit.util.FS;
88 import org.eclipse.jgit.util.FileUtils;
89 import org.slf4j.Logger;
90 import org.slf4j.LoggerFactory;
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110 public class ObjectDirectory extends FileObjectDatabase {
111 private final static Logger LOG = LoggerFactory
112 .getLogger(ObjectDirectory.class);
113
114 private static final PackList NO_PACKS = new PackList(
115 FileSnapshot.DIRTY, new PackFile[0]);
116
117
118 private static final int RESOLVE_ABBREV_LIMIT = 256;
119
120 private final Config config;
121
122 private final File objects;
123
124 private final File infoDirectory;
125
126 private final File packDirectory;
127
128 private final File preservedDirectory;
129
130 private final File alternatesFile;
131
132 private final AtomicReference<PackList> packList;
133
134 private final FS fs;
135
136 private final AtomicReference<AlternateHandle[]> alternates;
137
138 private final UnpackedObjectCache unpackedObjectCache;
139
140 private final File shallowFile;
141
142 private FileSnapshot shallowFileSnapshot = FileSnapshot.DIRTY;
143
144 private Set<ObjectId> shallowCommitsIds;
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164 public ObjectDirectory(final Config cfg, final File dir,
165 File[] alternatePaths, FS fs, File shallowFile) throws IOException {
166 config = cfg;
167 objects = dir;
168 infoDirectory = new File(objects, "info");
169 packDirectory = new File(objects, "pack");
170 preservedDirectory = new File(packDirectory, "preserved");
171 alternatesFile = new File(infoDirectory, "alternates");
172 packList = new AtomicReference<>(NO_PACKS);
173 unpackedObjectCache = new UnpackedObjectCache();
174 this.fs = fs;
175 this.shallowFile = shallowFile;
176
177 alternates = new AtomicReference<>();
178 if (alternatePaths != null) {
179 AlternateHandle[] alt;
180
181 alt = new AlternateHandle[alternatePaths.length];
182 for (int i = 0; i < alternatePaths.length; i++)
183 alt[i] = openAlternate(alternatePaths[i]);
184 alternates.set(alt);
185 }
186 }
187
188
189
190
191 @Override
192 public final File getDirectory() {
193 return objects;
194 }
195
196
197
198
199 public final File getPreservedDirectory() {
200 return preservedDirectory;
201 }
202
203 @Override
204 public boolean exists() {
205 return fs.exists(objects);
206 }
207
208 @Override
209 public void create() throws IOException {
210 FileUtils.mkdirs(objects);
211 FileUtils.mkdir(infoDirectory);
212 FileUtils.mkdir(packDirectory);
213 }
214
215 @Override
216 public ObjectDirectoryInserter newInserter() {
217 return new ObjectDirectoryInserter(this, config);
218 }
219
220 @Override
221 public void close() {
222 unpackedObjectCache.clear();
223
224 final PackList packs = packList.get();
225 if (packs != NO_PACKS && packList.compareAndSet(packs, NO_PACKS)) {
226 for (PackFile p : packs.packs)
227 p.close();
228 }
229
230
231 AlternateHandle[] alt = alternates.get();
232 if (alt != null && alternates.compareAndSet(alt, null)) {
233 for(final AlternateHandle od : alt)
234 od.close();
235 }
236 }
237
238
239
240
241
242
243
244
245 @Override
246 public Collection<PackFile> getPacks() {
247 PackList list = packList.get();
248 if (list == NO_PACKS)
249 list = scanPacks(list);
250 PackFile[] packs = list.packs;
251 return Collections.unmodifiableCollection(Arrays.asList(packs));
252 }
253
254
255
256
257
258
259
260
261
262
263
264 @Override
265 public PackFile openPack(final File pack)
266 throws IOException {
267 final String p = pack.getName();
268 if (p.length() != 50 || !p.startsWith("pack-") || !p.endsWith(".pack"))
269 throw new IOException(MessageFormat.format(JGitText.get().notAValidPack, pack));
270
271
272
273
274 int extensions = PACK.getBit() | INDEX.getBit();
275 final String base = p.substring(0, p.length() - 4);
276 for (PackExt ext : PackExt.values()) {
277 if ((extensions & ext.getBit()) == 0) {
278 final String name = base + ext.getExtension();
279 if (new File(pack.getParentFile(), name).exists())
280 extensions |= ext.getBit();
281 }
282 }
283
284 PackFile res = new PackFile(pack, extensions);
285 insertPack(res);
286 return res;
287 }
288
289 @Override
290 public String toString() {
291 return "ObjectDirectory[" + getDirectory() + "]";
292 }
293
294 @Override
295 public boolean has(AnyObjectId objectId) {
296 return unpackedObjectCache.isUnpacked(objectId)
297 || hasPackedInSelfOrAlternate(objectId)
298 || hasLooseInSelfOrAlternate(objectId);
299 }
300
301 private boolean hasPackedInSelfOrAlternate(AnyObjectId objectId) {
302 if (hasPackedObject(objectId))
303 return true;
304 for (AlternateHandle alt : myAlternates()) {
305 if (alt.db.hasPackedInSelfOrAlternate(objectId))
306 return true;
307 }
308 return false;
309 }
310
311 private boolean hasLooseInSelfOrAlternate(AnyObjectId objectId) {
312 if (fileFor(objectId).exists())
313 return true;
314 for (AlternateHandle alt : myAlternates()) {
315 if (alt.db.hasLooseInSelfOrAlternate(objectId))
316 return true;
317 }
318 return false;
319 }
320
321 boolean hasPackedObject(AnyObjectId objectId) {
322 PackList pList;
323 do {
324 pList = packList.get();
325 for (PackFile p : pList.packs) {
326 try {
327 if (p.hasObject(objectId))
328 return true;
329 } catch (IOException e) {
330
331
332
333 removePack(p);
334 }
335 }
336 } while (searchPacksAgain(pList));
337 return false;
338 }
339
340 @Override
341 void resolve(Set<ObjectId> matches, AbbreviatedObjectId id)
342 throws IOException {
343
344
345 int oldSize = matches.size();
346 PackList pList;
347 do {
348 pList = packList.get();
349 for (PackFile p : pList.packs) {
350 try {
351 p.resolve(matches, id, RESOLVE_ABBREV_LIMIT);
352 p.resetTransientErrorCount();
353 } catch (IOException e) {
354 handlePackError(e, p);
355 }
356 if (matches.size() > RESOLVE_ABBREV_LIMIT)
357 return;
358 }
359 } while (matches.size() == oldSize && searchPacksAgain(pList));
360
361 String fanOut = id.name().substring(0, 2);
362 String[] entries = new File(getDirectory(), fanOut).list();
363 if (entries != null) {
364 for (String e : entries) {
365 if (e.length() != Constants.OBJECT_ID_STRING_LENGTH - 2)
366 continue;
367 try {
368 ObjectId entId = ObjectId.fromString(fanOut + e);
369 if (id.prefixCompare(entId) == 0)
370 matches.add(entId);
371 } catch (IllegalArgumentException notId) {
372 continue;
373 }
374 if (matches.size() > RESOLVE_ABBREV_LIMIT)
375 return;
376 }
377 }
378
379 for (AlternateHandle alt : myAlternates()) {
380 alt.db.resolve(matches, id);
381 if (matches.size() > RESOLVE_ABBREV_LIMIT)
382 return;
383 }
384 }
385
386 @Override
387 ObjectLoader openObject(WindowCursor curs, AnyObjectId objectId)
388 throws IOException {
389 if (unpackedObjectCache.isUnpacked(objectId)) {
390 ObjectLoader ldr = openLooseObject(curs, objectId);
391 if (ldr != null)
392 return ldr;
393 }
394 ObjectLoader ldr = openPackedFromSelfOrAlternate(curs, objectId);
395 if (ldr != null)
396 return ldr;
397 return openLooseFromSelfOrAlternate(curs, objectId);
398 }
399
400 private ObjectLoader openPackedFromSelfOrAlternate(WindowCursor curs,
401 AnyObjectId objectId) {
402 ObjectLoader ldr = openPackedObject(curs, objectId);
403 if (ldr != null)
404 return ldr;
405 for (AlternateHandle alt : myAlternates()) {
406 ldr = alt.db.openPackedFromSelfOrAlternate(curs, objectId);
407 if (ldr != null)
408 return ldr;
409 }
410 return null;
411 }
412
413 private ObjectLoader openLooseFromSelfOrAlternate(WindowCursor curs,
414 AnyObjectId objectId) throws IOException {
415 ObjectLoader ldr = openLooseObject(curs, objectId);
416 if (ldr != null)
417 return ldr;
418 for (AlternateHandle alt : myAlternates()) {
419 ldr = alt.db.openLooseFromSelfOrAlternate(curs, objectId);
420 if (ldr != null)
421 return ldr;
422 }
423 return null;
424 }
425
426 ObjectLoader openPackedObject(WindowCursor curs, AnyObjectId objectId) {
427 PackList pList;
428 do {
429 SEARCH: for (;;) {
430 pList = packList.get();
431 for (PackFile p : pList.packs) {
432 try {
433 ObjectLoader ldr = p.get(curs, objectId);
434 p.resetTransientErrorCount();
435 if (ldr != null)
436 return ldr;
437 } catch (PackMismatchException e) {
438
439 if (searchPacksAgain(pList))
440 continue SEARCH;
441 } catch (IOException e) {
442 handlePackError(e, p);
443 }
444 }
445 break SEARCH;
446 }
447 } while (searchPacksAgain(pList));
448 return null;
449 }
450
451 @Override
452 ObjectLoader openLooseObject(WindowCursor curs, AnyObjectId id)
453 throws IOException {
454 File path = fileFor(id);
455 try (FileInputStream in = new FileInputStream(path)) {
456 unpackedObjectCache.add(id);
457 return UnpackedObject.open(in, path, id, curs);
458 } catch (FileNotFoundException noFile) {
459 if (path.exists()) {
460 throw noFile;
461 }
462 unpackedObjectCache.remove(id);
463 return null;
464 }
465 }
466
467 @Override
468 long getObjectSize(WindowCursor curs, AnyObjectId id)
469 throws IOException {
470 if (unpackedObjectCache.isUnpacked(id)) {
471 long len = getLooseObjectSize(curs, id);
472 if (0 <= len)
473 return len;
474 }
475 long len = getPackedSizeFromSelfOrAlternate(curs, id);
476 if (0 <= len)
477 return len;
478 return getLooseSizeFromSelfOrAlternate(curs, id);
479 }
480
481 private long getPackedSizeFromSelfOrAlternate(WindowCursor curs,
482 AnyObjectId id) {
483 long len = getPackedObjectSize(curs, id);
484 if (0 <= len)
485 return len;
486 for (AlternateHandle alt : myAlternates()) {
487 len = alt.db.getPackedSizeFromSelfOrAlternate(curs, id);
488 if (0 <= len)
489 return len;
490 }
491 return -1;
492 }
493
494 private long getLooseSizeFromSelfOrAlternate(WindowCursor curs,
495 AnyObjectId id) throws IOException {
496 long len = getLooseObjectSize(curs, id);
497 if (0 <= len)
498 return len;
499 for (AlternateHandle alt : myAlternates()) {
500 len = alt.db.getLooseSizeFromSelfOrAlternate(curs, id);
501 if (0 <= len)
502 return len;
503 }
504 return -1;
505 }
506
507 private long getPackedObjectSize(WindowCursor curs, AnyObjectId id) {
508 PackList pList;
509 do {
510 SEARCH: for (;;) {
511 pList = packList.get();
512 for (PackFile p : pList.packs) {
513 try {
514 long len = p.getObjectSize(curs, id);
515 p.resetTransientErrorCount();
516 if (0 <= len)
517 return len;
518 } catch (PackMismatchException e) {
519
520 if (searchPacksAgain(pList))
521 continue SEARCH;
522 } catch (IOException e) {
523 handlePackError(e, p);
524 }
525 }
526 break SEARCH;
527 }
528 } while (searchPacksAgain(pList));
529 return -1;
530 }
531
532 private long getLooseObjectSize(WindowCursor curs, AnyObjectId id)
533 throws IOException {
534 File f = fileFor(id);
535 try (FileInputStream in = new FileInputStream(f)) {
536 unpackedObjectCache.add(id);
537 return UnpackedObject.getSize(in, id, curs);
538 } catch (FileNotFoundException noFile) {
539 if (f.exists()) {
540 throw noFile;
541 }
542 unpackedObjectCache.remove(id);
543 return -1;
544 }
545 }
546
547 @Override
548 void selectObjectRepresentation(PackWriter packer, ObjectToPack otp,
549 WindowCursor curs) throws IOException {
550 PackList pList = packList.get();
551 SEARCH: for (;;) {
552 for (final PackFile p : pList.packs) {
553 try {
554 LocalObjectRepresentation rep = p.representation(curs, otp);
555 p.resetTransientErrorCount();
556 if (rep != null)
557 packer.select(otp, rep);
558 } catch (PackMismatchException e) {
559
560
561 pList = scanPacks(pList);
562 continue SEARCH;
563 } catch (IOException e) {
564 handlePackError(e, p);
565 }
566 }
567 break SEARCH;
568 }
569
570 for (AlternateHandle h : myAlternates())
571 h.db.selectObjectRepresentation(packer, otp, curs);
572 }
573
574 private void handlePackError(IOException e, PackFile p) {
575 String warnTmpl = null;
576 int transientErrorCount = 0;
577 String errTmpl = JGitText.get().exceptionWhileReadingPack;
578 if ((e instanceof CorruptObjectException)
579 || (e instanceof PackInvalidException)) {
580 warnTmpl = JGitText.get().corruptPack;
581
582 removePack(p);
583 } else if (e instanceof FileNotFoundException) {
584 if (p.getPackFile().exists()) {
585 errTmpl = JGitText.get().packInaccessible;
586 transientErrorCount = p.incrementTransientErrorCount();
587 } else {
588 warnTmpl = JGitText.get().packWasDeleted;
589 removePack(p);
590 }
591 } else if (FileUtils.isStaleFileHandleInCausalChain(e)) {
592 warnTmpl = JGitText.get().packHandleIsStale;
593 removePack(p);
594 } else {
595 transientErrorCount = p.incrementTransientErrorCount();
596 }
597 if (warnTmpl != null) {
598 if (LOG.isDebugEnabled()) {
599 LOG.debug(MessageFormat.format(warnTmpl,
600 p.getPackFile().getAbsolutePath()), e);
601 } else {
602 LOG.warn(MessageFormat.format(warnTmpl,
603 p.getPackFile().getAbsolutePath()));
604 }
605 } else {
606 if (doLogExponentialBackoff(transientErrorCount)) {
607
608
609 LOG.error(MessageFormat.format(errTmpl,
610 p.getPackFile().getAbsolutePath()),
611 Integer.valueOf(transientErrorCount), e);
612 }
613 }
614 }
615
616
617
618
619
620
621 private boolean doLogExponentialBackoff(int n) {
622 return (n & (n - 1)) == 0;
623 }
624
625 @Override
626 InsertLooseObjectResult insertUnpackedObject(File tmp, ObjectId id,
627 boolean createDuplicate) throws IOException {
628
629
630 if (unpackedObjectCache.isUnpacked(id)) {
631 FileUtils.delete(tmp, FileUtils.RETRY);
632 return InsertLooseObjectResult.EXISTS_LOOSE;
633 }
634 if (!createDuplicate && has(id)) {
635 FileUtils.delete(tmp, FileUtils.RETRY);
636 return InsertLooseObjectResult.EXISTS_PACKED;
637 }
638
639 final File dst = fileFor(id);
640 if (dst.exists()) {
641
642
643
644
645 FileUtils.delete(tmp, FileUtils.RETRY);
646 return InsertLooseObjectResult.EXISTS_LOOSE;
647 }
648 try {
649 Files.move(tmp.toPath(), dst.toPath(),
650 StandardCopyOption.ATOMIC_MOVE);
651 dst.setReadOnly();
652 unpackedObjectCache.add(id);
653 return InsertLooseObjectResult.INSERTED;
654 } catch (AtomicMoveNotSupportedException e) {
655 LOG.error(e.getMessage(), e);
656 } catch (IOException e) {
657
658 }
659
660
661
662
663
664 FileUtils.mkdir(dst.getParentFile(), true);
665 try {
666 Files.move(tmp.toPath(), dst.toPath(),
667 StandardCopyOption.ATOMIC_MOVE);
668 dst.setReadOnly();
669 unpackedObjectCache.add(id);
670 return InsertLooseObjectResult.INSERTED;
671 } catch (AtomicMoveNotSupportedException e) {
672 LOG.error(e.getMessage(), e);
673 } catch (IOException e) {
674 LOG.debug(e.getMessage(), e);
675 }
676
677 if (!createDuplicate && has(id)) {
678 FileUtils.delete(tmp, FileUtils.RETRY);
679 return InsertLooseObjectResult.EXISTS_PACKED;
680 }
681
682
683
684
685
686
687 FileUtils.delete(tmp, FileUtils.RETRY);
688 return InsertLooseObjectResult.FAILURE;
689 }
690
691 private boolean searchPacksAgain(PackList old) {
692
693
694
695
696
697
698 boolean trustFolderStat = config.getBoolean(
699 ConfigConstants.CONFIG_CORE_SECTION,
700 ConfigConstants.CONFIG_KEY_TRUSTFOLDERSTAT, true);
701
702 return ((!trustFolderStat) || old.snapshot.isModified(packDirectory))
703 && old != scanPacks(old);
704 }
705
706 @Override
707 Config getConfig() {
708 return config;
709 }
710
711 @Override
712 FS getFS() {
713 return fs;
714 }
715
716 @Override
717 Set<ObjectId> getShallowCommits() throws IOException {
718 if (shallowFile == null || !shallowFile.isFile())
719 return Collections.emptySet();
720
721 if (shallowFileSnapshot == null
722 || shallowFileSnapshot.isModified(shallowFile)) {
723 shallowCommitsIds = new HashSet<>();
724
725 final BufferedReader reader = open(shallowFile);
726 try {
727 String line;
728 while ((line = reader.readLine()) != null) {
729 try {
730 shallowCommitsIds.add(ObjectId.fromString(line));
731 } catch (IllegalArgumentException ex) {
732 throw new IOException(MessageFormat
733 .format(JGitText.get().badShallowLine, line));
734 }
735 }
736 } finally {
737 reader.close();
738 }
739
740 shallowFileSnapshot = FileSnapshot.save(shallowFile);
741 }
742
743 return shallowCommitsIds;
744 }
745
746 private void insertPack(final PackFile pf) {
747 PackList o, n;
748 do {
749 o = packList.get();
750
751
752
753
754
755 final PackFile[] oldList = o.packs;
756 final String name = pf.getPackFile().getName();
757 for (PackFile p : oldList) {
758 if (PackFile.SORT.compare(pf, p) < 0)
759 break;
760 if (name.equals(p.getPackFile().getName()))
761 return;
762 }
763
764 final PackFile[] newList = new PackFile[1 + oldList.length];
765 newList[0] = pf;
766 System.arraycopy(oldList, 0, newList, 1, oldList.length);
767 n = new PackList(o.snapshot, newList);
768 } while (!packList.compareAndSet(o, n));
769 }
770
771 private void removePack(final PackFile deadPack) {
772 PackList o, n;
773 do {
774 o = packList.get();
775
776 final PackFile[] oldList = o.packs;
777 final int j = indexOf(oldList, deadPack);
778 if (j < 0)
779 break;
780
781 final PackFile[] newList = new PackFile[oldList.length - 1];
782 System.arraycopy(oldList, 0, newList, 0, j);
783 System.arraycopy(oldList, j + 1, newList, j, newList.length - j);
784 n = new PackList(o.snapshot, newList);
785 } while (!packList.compareAndSet(o, n));
786 deadPack.close();
787 }
788
789 private static int indexOf(final PackFile[] list, final PackFile pack) {
790 for (int i = 0; i < list.length; i++) {
791 if (list[i] == pack)
792 return i;
793 }
794 return -1;
795 }
796
797 private PackList scanPacks(final PackList original) {
798 synchronized (packList) {
799 PackList o, n;
800 do {
801 o = packList.get();
802 if (o != original) {
803
804
805
806 return o;
807 }
808 n = scanPacksImpl(o);
809 if (n == o)
810 return n;
811 } while (!packList.compareAndSet(o, n));
812 return n;
813 }
814 }
815
816 private PackList scanPacksImpl(final PackList old) {
817 final Map<String, PackFile> forReuse = reuseMap(old);
818 final FileSnapshot snapshot = FileSnapshot.save(packDirectory);
819 final Set<String> names = listPackDirectory();
820 final List<PackFile> list = new ArrayList<>(names.size() >> 2);
821 boolean foundNew = false;
822 for (final String indexName : names) {
823
824
825 if (indexName.length() != 49 || !indexName.endsWith(".idx"))
826 continue;
827
828 final String base = indexName.substring(0, indexName.length() - 3);
829 int extensions = 0;
830 for (PackExt ext : PackExt.values()) {
831 if (names.contains(base + ext.getExtension()))
832 extensions |= ext.getBit();
833 }
834
835 if ((extensions & PACK.getBit()) == 0) {
836
837
838
839
840 continue;
841 }
842
843 final String packName = base + PACK.getExtension();
844 final PackFile oldPack = forReuse.remove(packName);
845 if (oldPack != null) {
846 list.add(oldPack);
847 continue;
848 }
849
850 final File packFile = new File(packDirectory, packName);
851 list.add(new PackFile(packFile, extensions));
852 foundNew = true;
853 }
854
855
856
857
858
859
860 if (!foundNew && forReuse.isEmpty() && snapshot.equals(old.snapshot)) {
861 old.snapshot.setClean(snapshot);
862 return old;
863 }
864
865 for (final PackFile p : forReuse.values()) {
866 p.close();
867 }
868
869 if (list.isEmpty())
870 return new PackList(snapshot, NO_PACKS.packs);
871
872 final PackFile[] r = list.toArray(new PackFile[list.size()]);
873 Arrays.sort(r, PackFile.SORT);
874 return new PackList(snapshot, r);
875 }
876
877 private static Map<String, PackFile> reuseMap(final PackList old) {
878 final Map<String, PackFile> forReuse = new HashMap<>();
879 for (final PackFile p : old.packs) {
880 if (p.invalid()) {
881
882
883
884 p.close();
885 continue;
886 }
887
888 final PackFile prior = forReuse.put(p.getPackFile().getName(), p);
889 if (prior != null) {
890
891
892
893
894
895
896 forReuse.put(prior.getPackFile().getName(), prior);
897 p.close();
898 }
899 }
900 return forReuse;
901 }
902
903 private Set<String> listPackDirectory() {
904 final String[] nameList = packDirectory.list();
905 if (nameList == null)
906 return Collections.emptySet();
907 final Set<String> nameSet = new HashSet<>(nameList.length << 1);
908 for (final String name : nameList) {
909 if (name.startsWith("pack-"))
910 nameSet.add(name);
911 }
912 return nameSet;
913 }
914
915 AlternateHandle[] myAlternates() {
916 AlternateHandle[] alt = alternates.get();
917 if (alt == null) {
918 synchronized (alternates) {
919 alt = alternates.get();
920 if (alt == null) {
921 try {
922 alt = loadAlternates();
923 } catch (IOException e) {
924 alt = new AlternateHandle[0];
925 }
926 alternates.set(alt);
927 }
928 }
929 }
930 return alt;
931 }
932
933 private AlternateHandle[] loadAlternates() throws IOException {
934 final List<AlternateHandle> l = new ArrayList<>(4);
935 final BufferedReader br = open(alternatesFile);
936 try {
937 String line;
938 while ((line = br.readLine()) != null) {
939 l.add(openAlternate(line));
940 }
941 } finally {
942 br.close();
943 }
944 return l.toArray(new AlternateHandle[l.size()]);
945 }
946
947 private static BufferedReader open(final File f)
948 throws FileNotFoundException {
949 return new BufferedReader(new FileReader(f));
950 }
951
952 private AlternateHandle openAlternate(final String location)
953 throws IOException {
954 final File objdir = fs.resolve(objects, location);
955 return openAlternate(objdir);
956 }
957
958 private AlternateHandle openAlternate(File objdir) throws IOException {
959 final File parent = objdir.getParentFile();
960 if (FileKey.isGitRepository(parent, fs)) {
961 FileKey key = FileKey.exact(parent, fs);
962 FileRepository db = (FileRepository) RepositoryCache.open(key);
963 return new AlternateRepository(db);
964 }
965
966 ObjectDirectory db = new ObjectDirectory(config, objdir, null, fs, null);
967 return new AlternateHandle(db);
968 }
969
970
971
972
973
974
975
976
977 @Override
978 public File fileFor(AnyObjectId objectId) {
979 String n = objectId.name();
980 String d = n.substring(0, 2);
981 String f = n.substring(2);
982 return new File(new File(getDirectory(), d), f);
983 }
984
985 private static final class PackList {
986
987 final FileSnapshot snapshot;
988
989
990 final PackFile[] packs;
991
992 PackList(final FileSnapshot monitor, final PackFile[] packs) {
993 this.snapshot = monitor;
994 this.packs = packs;
995 }
996 }
997
998 static class AlternateHandle {
999 final ObjectDirectory db;
1000
1001 AlternateHandle(ObjectDirectory db) {
1002 this.db = db;
1003 }
1004
1005 void close() {
1006 db.close();
1007 }
1008 }
1009
1010 static class AlternateRepository extends AlternateHandle {
1011 final FileRepository repository;
1012
1013 AlternateRepository(FileRepository r) {
1014 super(r.getObjectDatabase());
1015 repository = r;
1016 }
1017
1018 @Override
1019 void close() {
1020 repository.close();
1021 }
1022 }
1023
1024 @Override
1025 public ObjectDatabase newCachedDatabase() {
1026 return newCachedFileObjectDatabase();
1027 }
1028
1029 CachedObjectDirectory newCachedFileObjectDatabase() {
1030 return new CachedObjectDirectory(this);
1031 }
1032 }