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