summaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
-rw-r--r--org.eclipse.jgit.http.server/META-INF/MANIFEST.MF1
-rw-r--r--org.eclipse.jgit.http.server/src/org/eclipse/jgit/http/server/InfoPacksServlet.java4
-rw-r--r--org.eclipse.jgit.http.server/src/org/eclipse/jgit/http/server/InfoRefsServlet.java49
-rw-r--r--org.eclipse.jgit.http.server/src/org/eclipse/jgit/http/server/IsLocalFilter.java2
-rw-r--r--org.eclipse.jgit.http.server/src/org/eclipse/jgit/http/server/ObjectFileServlet.java2
-rw-r--r--org.eclipse.jgit.http.server/src/org/eclipse/jgit/http/server/ReceivePackServlet.java7
-rw-r--r--org.eclipse.jgit.http.server/src/org/eclipse/jgit/http/server/TextFileServlet.java2
-rw-r--r--org.eclipse.jgit.http.server/src/org/eclipse/jgit/http/server/UploadPackServlet.java14
-rw-r--r--org.eclipse.jgit.http.server/src/org/eclipse/jgit/http/server/resolver/FileResolver.java4
-rw-r--r--org.eclipse.jgit.http.test/META-INF/MANIFEST.MF1
-rw-r--r--org.eclipse.jgit.http.test/tst/org/eclipse/jgit/http/test/AdvertiseErrorTest.java9
-rw-r--r--org.eclipse.jgit.http.test/tst/org/eclipse/jgit/http/test/HookMessageTest.java9
-rw-r--r--org.eclipse.jgit.http.test/tst/org/eclipse/jgit/http/test/HttpClientTests.java17
-rw-r--r--org.eclipse.jgit.http.test/tst/org/eclipse/jgit/http/test/SmartClientSmartServerTest.java15
-rw-r--r--org.eclipse.jgit.http.test/tst/org/eclipse/jgit/http/test/util/HttpTestCase.java6
-rw-r--r--org.eclipse.jgit.iplog/META-INF/MANIFEST.MF1
-rw-r--r--org.eclipse.jgit.iplog/src/org/eclipse/jgit/iplog/IpLogGenerator.java18
-rw-r--r--org.eclipse.jgit.iplog/src/org/eclipse/jgit/iplog/IpLogMeta.java4
-rw-r--r--org.eclipse.jgit.junit/META-INF/MANIFEST.MF2
-rw-r--r--org.eclipse.jgit.junit/src/org/eclipse/jgit/junit/LocalDiskRepositoryTestCase.java17
-rw-r--r--org.eclipse.jgit.junit/src/org/eclipse/jgit/junit/MockSystemReader.java2
-rw-r--r--org.eclipse.jgit.junit/src/org/eclipse/jgit/junit/TestRepository.java183
-rw-r--r--org.eclipse.jgit.pgm/META-INF/MANIFEST.MF2
-rw-r--r--org.eclipse.jgit.pgm/META-INF/services/org.eclipse.jgit.pgm.TextBuiltin1
-rw-r--r--org.eclipse.jgit.pgm/src/org/eclipse/jgit/pgm/Clone.java21
-rw-r--r--org.eclipse.jgit.pgm/src/org/eclipse/jgit/pgm/Glog.java8
-rw-r--r--org.eclipse.jgit.pgm/src/org/eclipse/jgit/pgm/IndexPack.java4
-rw-r--r--org.eclipse.jgit.pgm/src/org/eclipse/jgit/pgm/Init.java4
-rw-r--r--org.eclipse.jgit.pgm/src/org/eclipse/jgit/pgm/Main.java76
-rw-r--r--org.eclipse.jgit.pgm/src/org/eclipse/jgit/pgm/ReceivePack.java18
-rw-r--r--org.eclipse.jgit.pgm/src/org/eclipse/jgit/pgm/Rm.java4
-rw-r--r--org.eclipse.jgit.pgm/src/org/eclipse/jgit/pgm/Tag.java9
-rw-r--r--org.eclipse.jgit.pgm/src/org/eclipse/jgit/pgm/UploadPack.java26
-rw-r--r--org.eclipse.jgit.pgm/src/org/eclipse/jgit/pgm/debug/MakeCacheTree.java2
-rw-r--r--org.eclipse.jgit.pgm/src/org/eclipse/jgit/pgm/debug/ReadDirCache.java3
-rw-r--r--org.eclipse.jgit.pgm/src/org/eclipse/jgit/pgm/debug/RebuildCommitGraph.java3
-rw-r--r--org.eclipse.jgit.pgm/src/org/eclipse/jgit/pgm/debug/ShowCacheTree.java2
-rw-r--r--org.eclipse.jgit.pgm/src/org/eclipse/jgit/pgm/debug/ShowDirCache.java2
-rw-r--r--org.eclipse.jgit.pgm/src/org/eclipse/jgit/pgm/debug/ShowPackDelta.java127
-rw-r--r--org.eclipse.jgit.pgm/src/org/eclipse/jgit/pgm/debug/WriteDirCache.java2
-rw-r--r--org.eclipse.jgit.pgm/src/org/eclipse/jgit/pgm/eclipse/Iplog.java2
-rw-r--r--org.eclipse.jgit.pgm/src/org/eclipse/jgit/pgm/eclipse/Ipzilla.java2
-rw-r--r--org.eclipse.jgit.pgm/src/org/eclipse/jgit/pgm/opt/AbstractTreeIteratorHandler.java6
-rw-r--r--org.eclipse.jgit.test/META-INF/MANIFEST.MF2
-rw-r--r--org.eclipse.jgit.test/exttst/org/eclipse/jgit/lib/T0007_GitIndexTest.java2
-rw-r--r--org.eclipse.jgit.test/tst/org/eclipse/jgit/api/AddCommandTest.java44
-rw-r--r--org.eclipse.jgit.test/tst/org/eclipse/jgit/api/MergeCommandTest.java18
-rw-r--r--org.eclipse.jgit.test/tst/org/eclipse/jgit/diff/SimilarityIndexTest.java16
-rw-r--r--org.eclipse.jgit.test/tst/org/eclipse/jgit/dircache/DirCacheBasicTest.java20
-rw-r--r--org.eclipse.jgit.test/tst/org/eclipse/jgit/dircache/DirCacheBuilderIteratorTest.java2
-rw-r--r--org.eclipse.jgit.test/tst/org/eclipse/jgit/dircache/DirCacheBuilderTest.java20
-rw-r--r--org.eclipse.jgit.test/tst/org/eclipse/jgit/dircache/DirCacheFindTest.java2
-rw-r--r--org.eclipse.jgit.test/tst/org/eclipse/jgit/dircache/DirCacheIteratorTest.java16
-rw-r--r--org.eclipse.jgit.test/tst/org/eclipse/jgit/dircache/DirCacheLargePathTest.java4
-rw-r--r--org.eclipse.jgit.test/tst/org/eclipse/jgit/dircache/DirCacheTreeTest.java14
-rw-r--r--org.eclipse.jgit.test/tst/org/eclipse/jgit/lib/ReadTreeTest.java18
-rw-r--r--org.eclipse.jgit.test/tst/org/eclipse/jgit/lib/ReflogConfigTest.java4
-rw-r--r--org.eclipse.jgit.test/tst/org/eclipse/jgit/lib/RepositoryTestCase.java7
-rw-r--r--org.eclipse.jgit.test/tst/org/eclipse/jgit/lib/SampleDataRepositoryTestCase.java2
-rw-r--r--org.eclipse.jgit.test/tst/org/eclipse/jgit/lib/WorkDirCheckout_ReadTreeTest.java4
-rw-r--r--org.eclipse.jgit.test/tst/org/eclipse/jgit/merge/CherryPickTest.java28
-rw-r--r--org.eclipse.jgit.test/tst/org/eclipse/jgit/merge/SimpleMergeTest.java75
-rw-r--r--org.eclipse.jgit.test/tst/org/eclipse/jgit/revwalk/RevWalkTestCase.java5
-rw-r--r--org.eclipse.jgit.test/tst/org/eclipse/jgit/storage/file/ConcurrentRepackTest.java (renamed from org.eclipse.jgit.test/tst/org/eclipse/jgit/lib/ConcurrentRepackTest.java)26
-rw-r--r--org.eclipse.jgit.test/tst/org/eclipse/jgit/storage/file/PackFileTest.java387
-rw-r--r--org.eclipse.jgit.test/tst/org/eclipse/jgit/storage/file/PackIndexTestCase.java (renamed from org.eclipse.jgit.test/tst/org/eclipse/jgit/lib/PackIndexTestCase.java)5
-rw-r--r--org.eclipse.jgit.test/tst/org/eclipse/jgit/storage/file/PackIndexV1Test.java (renamed from org.eclipse.jgit.test/tst/org/eclipse/jgit/lib/PackIndexV1Test.java)3
-rw-r--r--org.eclipse.jgit.test/tst/org/eclipse/jgit/storage/file/PackIndexV2Test.java (renamed from org.eclipse.jgit.test/tst/org/eclipse/jgit/lib/PackIndexV2Test.java)3
-rw-r--r--org.eclipse.jgit.test/tst/org/eclipse/jgit/storage/file/PackReverseIndexTest.java (renamed from org.eclipse.jgit.test/tst/org/eclipse/jgit/lib/PackReverseIndexTest.java)5
-rw-r--r--org.eclipse.jgit.test/tst/org/eclipse/jgit/storage/file/PackWriterTest.java (renamed from org.eclipse.jgit.test/tst/org/eclipse/jgit/lib/PackWriterTest.java)23
-rw-r--r--org.eclipse.jgit.test/tst/org/eclipse/jgit/storage/file/RefDirectoryTest.java (renamed from org.eclipse.jgit.test/tst/org/eclipse/jgit/lib/RefDirectoryTest.java)6
-rw-r--r--org.eclipse.jgit.test/tst/org/eclipse/jgit/storage/file/RefUpdateTest.java (renamed from org.eclipse.jgit.test/tst/org/eclipse/jgit/lib/RefUpdateTest.java)31
-rw-r--r--org.eclipse.jgit.test/tst/org/eclipse/jgit/storage/file/ReflogReaderTest.java (renamed from org.eclipse.jgit.test/tst/org/eclipse/jgit/lib/ReflogReaderTest.java)7
-rw-r--r--org.eclipse.jgit.test/tst/org/eclipse/jgit/storage/file/RepositorySetupWorkDirTest.java (renamed from org.eclipse.jgit.test/tst/org/eclipse/jgit/lib/RepositorySetupWorkDirTest.java)86
-rw-r--r--org.eclipse.jgit.test/tst/org/eclipse/jgit/storage/file/T0003_Basic.java (renamed from org.eclipse.jgit.test/tst/org/eclipse/jgit/lib/T0003_Basic.java)106
-rw-r--r--org.eclipse.jgit.test/tst/org/eclipse/jgit/storage/file/T0004_PackReader.java (renamed from org.eclipse.jgit.test/tst/org/eclipse/jgit/lib/T0004_PackReader.java)15
-rw-r--r--org.eclipse.jgit.test/tst/org/eclipse/jgit/storage/file/UnpackedObjectTest.java534
-rw-r--r--org.eclipse.jgit.test/tst/org/eclipse/jgit/storage/file/WindowCacheGetTest.java (renamed from org.eclipse.jgit.test/tst/org/eclipse/jgit/lib/WindowCacheGetTest.java)19
-rw-r--r--org.eclipse.jgit.test/tst/org/eclipse/jgit/storage/file/WindowCacheReconfigureTest.java (renamed from org.eclipse.jgit.test/tst/org/eclipse/jgit/lib/WindowCacheReconfigureTest.java)4
-rw-r--r--org.eclipse.jgit.test/tst/org/eclipse/jgit/storage/file/XInputStream.java (renamed from org.eclipse.jgit.test/tst/org/eclipse/jgit/lib/XInputStream.java)2
-rw-r--r--org.eclipse.jgit.test/tst/org/eclipse/jgit/storage/pack/DeltaIndexTest.java228
-rw-r--r--org.eclipse.jgit.test/tst/org/eclipse/jgit/storage/pack/DeltaStreamTest.java273
-rw-r--r--org.eclipse.jgit.test/tst/org/eclipse/jgit/transport/BundleWriterTest.java4
-rw-r--r--org.eclipse.jgit.test/tst/org/eclipse/jgit/transport/IndexPackTest.java2
-rw-r--r--org.eclipse.jgit.test/tst/org/eclipse/jgit/transport/ReceivePackRefFilterTest.java34
-rw-r--r--org.eclipse.jgit.test/tst/org/eclipse/jgit/transport/TransportTest.java4
-rw-r--r--org.eclipse.jgit.test/tst/org/eclipse/jgit/treewalk/AbstractTreeIteratorTest.java4
-rw-r--r--org.eclipse.jgit.test/tst/org/eclipse/jgit/treewalk/EmptyTreeIteratorTest.java7
-rw-r--r--org.eclipse.jgit.test/tst/org/eclipse/jgit/treewalk/FileTreeIteratorTest.java4
-rw-r--r--org.eclipse.jgit.test/tst/org/eclipse/jgit/treewalk/NameConflictTreeWalkTest.java16
-rw-r--r--org.eclipse.jgit.test/tst/org/eclipse/jgit/treewalk/PostOrderTreeWalkTest.java6
-rw-r--r--org.eclipse.jgit.test/tst/org/eclipse/jgit/treewalk/filter/PathSuffixFilterTestCase.java30
-rw-r--r--org.eclipse.jgit/META-INF/MANIFEST.MF3
-rw-r--r--org.eclipse.jgit/resources/org/eclipse/jgit/JGitText.properties4
-rw-r--r--org.eclipse.jgit/src/org/eclipse/jgit/JGitText.java4
-rw-r--r--org.eclipse.jgit/src/org/eclipse/jgit/api/AddCommand.java4
-rw-r--r--org.eclipse.jgit/src/org/eclipse/jgit/api/CommitCommand.java100
-rw-r--r--org.eclipse.jgit/src/org/eclipse/jgit/api/MergeCommand.java68
-rw-r--r--org.eclipse.jgit/src/org/eclipse/jgit/diff/DiffFormatter.java49
-rw-r--r--org.eclipse.jgit/src/org/eclipse/jgit/diff/RenameDetector.java22
-rw-r--r--org.eclipse.jgit/src/org/eclipse/jgit/diff/SimilarityIndex.java52
-rw-r--r--org.eclipse.jgit/src/org/eclipse/jgit/diff/SimilarityRenameDetector.java13
-rw-r--r--org.eclipse.jgit/src/org/eclipse/jgit/dircache/DirCache.java56
-rw-r--r--org.eclipse.jgit/src/org/eclipse/jgit/dircache/DirCacheBuildIterator.java4
-rw-r--r--org.eclipse.jgit/src/org/eclipse/jgit/dircache/DirCacheBuilder.java21
-rw-r--r--org.eclipse.jgit/src/org/eclipse/jgit/dircache/DirCacheIterator.java4
-rw-r--r--org.eclipse.jgit/src/org/eclipse/jgit/dircache/DirCacheTree.java8
-rw-r--r--org.eclipse.jgit/src/org/eclipse/jgit/errors/LargeObjectException.java67
-rw-r--r--org.eclipse.jgit/src/org/eclipse/jgit/errors/NoWorkTreeException.java59
-rw-r--r--org.eclipse.jgit/src/org/eclipse/jgit/errors/StoredObjectRepresentationNotAvailableException.java61
-rw-r--r--org.eclipse.jgit/src/org/eclipse/jgit/events/ConfigChangedEvent.java57
-rw-r--r--org.eclipse.jgit/src/org/eclipse/jgit/events/ConfigChangedListener.java (renamed from org.eclipse.jgit/src/org/eclipse/jgit/lib/RepositoryAdapter.java)25
-rw-r--r--org.eclipse.jgit/src/org/eclipse/jgit/events/IndexChangedEvent.java57
-rw-r--r--org.eclipse.jgit/src/org/eclipse/jgit/events/IndexChangedListener.java (renamed from org.eclipse.jgit/src/org/eclipse/jgit/lib/RepositoryChangedEvent.java)31
-rw-r--r--org.eclipse.jgit/src/org/eclipse/jgit/events/ListenerHandle.java (renamed from org.eclipse.jgit/src/org/eclipse/jgit/lib/IndexChangedEvent.java)40
-rw-r--r--org.eclipse.jgit/src/org/eclipse/jgit/events/ListenerList.java142
-rw-r--r--org.eclipse.jgit/src/org/eclipse/jgit/events/RefsChangedEvent.java57
-rw-r--r--org.eclipse.jgit/src/org/eclipse/jgit/events/RefsChangedListener.java (renamed from org.eclipse.jgit/src/org/eclipse/jgit/lib/RefsChangedEvent.java)26
-rw-r--r--org.eclipse.jgit/src/org/eclipse/jgit/events/RepositoryEvent.java95
-rw-r--r--org.eclipse.jgit/src/org/eclipse/jgit/events/RepositoryListener.java (renamed from org.eclipse.jgit/src/org/eclipse/jgit/lib/RepositoryListener.java)28
-rw-r--r--org.eclipse.jgit/src/org/eclipse/jgit/lib/AlternateRepositoryDatabase.java142
-rw-r--r--org.eclipse.jgit/src/org/eclipse/jgit/lib/AnyObjectId.java4
-rw-r--r--org.eclipse.jgit/src/org/eclipse/jgit/lib/BaseRepositoryBuilder.java616
-rw-r--r--org.eclipse.jgit/src/org/eclipse/jgit/lib/BlobBasedConfig.java13
-rw-r--r--org.eclipse.jgit/src/org/eclipse/jgit/lib/CachedObjectDatabase.java132
-rw-r--r--org.eclipse.jgit/src/org/eclipse/jgit/lib/Commit.java9
-rw-r--r--org.eclipse.jgit/src/org/eclipse/jgit/lib/CoreConfig.java15
-rw-r--r--org.eclipse.jgit/src/org/eclipse/jgit/lib/DeltaPackedObjectLoader.java118
-rw-r--r--org.eclipse.jgit/src/org/eclipse/jgit/lib/FileTreeEntry.java2
-rw-r--r--org.eclipse.jgit/src/org/eclipse/jgit/lib/GitIndex.java24
-rw-r--r--org.eclipse.jgit/src/org/eclipse/jgit/lib/IndexDiff.java2
-rw-r--r--org.eclipse.jgit/src/org/eclipse/jgit/lib/ObjectDatabase.java339
-rw-r--r--org.eclipse.jgit/src/org/eclipse/jgit/lib/ObjectInserter.java395
-rw-r--r--org.eclipse.jgit/src/org/eclipse/jgit/lib/ObjectLoader.java151
-rw-r--r--org.eclipse.jgit/src/org/eclipse/jgit/lib/ObjectReader.java185
-rw-r--r--org.eclipse.jgit/src/org/eclipse/jgit/lib/ObjectStream.java220
-rw-r--r--org.eclipse.jgit/src/org/eclipse/jgit/lib/ObjectWriter.java331
-rw-r--r--org.eclipse.jgit/src/org/eclipse/jgit/lib/PackFile.java526
-rw-r--r--org.eclipse.jgit/src/org/eclipse/jgit/lib/PackWriter.java1040
-rw-r--r--org.eclipse.jgit/src/org/eclipse/jgit/lib/PackedObjectLoader.java188
-rw-r--r--org.eclipse.jgit/src/org/eclipse/jgit/lib/PersonIdent.java2
-rw-r--r--org.eclipse.jgit/src/org/eclipse/jgit/lib/RefUpdate.java14
-rw-r--r--org.eclipse.jgit/src/org/eclipse/jgit/lib/RefWriter.java1
-rw-r--r--org.eclipse.jgit/src/org/eclipse/jgit/lib/Repository.java1025
-rw-r--r--org.eclipse.jgit/src/org/eclipse/jgit/lib/RepositoryBuilder.java74
-rw-r--r--org.eclipse.jgit/src/org/eclipse/jgit/lib/RepositoryCache.java13
-rw-r--r--org.eclipse.jgit/src/org/eclipse/jgit/lib/RepositoryConfig.java135
-rw-r--r--org.eclipse.jgit/src/org/eclipse/jgit/lib/RepositoryState.java8
-rw-r--r--org.eclipse.jgit/src/org/eclipse/jgit/lib/Tag.java11
-rw-r--r--org.eclipse.jgit/src/org/eclipse/jgit/lib/ThreadSafeProgressMonitor.java (renamed from org.eclipse.jgit/src/org/eclipse/jgit/lib/DeltaRefPackedObjectLoader.java)81
-rw-r--r--org.eclipse.jgit/src/org/eclipse/jgit/lib/Tree.java6
-rw-r--r--org.eclipse.jgit/src/org/eclipse/jgit/lib/UnpackedObjectLoader.java223
-rw-r--r--org.eclipse.jgit/src/org/eclipse/jgit/lib/WholePackedObjectLoader.java110
-rw-r--r--org.eclipse.jgit/src/org/eclipse/jgit/merge/Merger.java35
-rw-r--r--org.eclipse.jgit/src/org/eclipse/jgit/merge/StrategySimpleTwoWayInCore.java9
-rw-r--r--org.eclipse.jgit/src/org/eclipse/jgit/revplot/PlotWalk.java23
-rw-r--r--org.eclipse.jgit/src/org/eclipse/jgit/revwalk/MergeBaseGenerator.java2
-rw-r--r--org.eclipse.jgit/src/org/eclipse/jgit/revwalk/ObjectWalk.java37
-rw-r--r--org.eclipse.jgit/src/org/eclipse/jgit/revwalk/PendingGenerator.java4
-rw-r--r--org.eclipse.jgit/src/org/eclipse/jgit/revwalk/RevCommit.java3
-rw-r--r--org.eclipse.jgit/src/org/eclipse/jgit/revwalk/RevObject.java9
-rw-r--r--org.eclipse.jgit/src/org/eclipse/jgit/revwalk/RevTag.java2
-rw-r--r--org.eclipse.jgit/src/org/eclipse/jgit/revwalk/RevWalk.java96
-rw-r--r--org.eclipse.jgit/src/org/eclipse/jgit/revwalk/RewriteTreeFilter.java8
-rw-r--r--org.eclipse.jgit/src/org/eclipse/jgit/storage/file/ByteArrayWindow.java (renamed from org.eclipse.jgit/src/org/eclipse/jgit/lib/ByteArrayWindow.java)45
-rw-r--r--org.eclipse.jgit/src/org/eclipse/jgit/storage/file/ByteBufferWindow.java (renamed from org.eclipse.jgit/src/org/eclipse/jgit/lib/ByteBufferWindow.java)38
-rw-r--r--org.eclipse.jgit/src/org/eclipse/jgit/storage/file/ByteWindow.java (renamed from org.eclipse.jgit/src/org/eclipse/jgit/lib/ByteWindow.java)67
-rw-r--r--org.eclipse.jgit/src/org/eclipse/jgit/storage/file/CachedObjectDirectory.java (renamed from org.eclipse.jgit/src/org/eclipse/jgit/lib/CachedObjectDirectory.java)129
-rw-r--r--org.eclipse.jgit/src/org/eclipse/jgit/storage/file/FileBasedConfig.java (renamed from org.eclipse.jgit/src/org/eclipse/jgit/lib/FileBasedConfig.java)4
-rw-r--r--org.eclipse.jgit/src/org/eclipse/jgit/storage/file/FileObjectDatabase.java271
-rw-r--r--org.eclipse.jgit/src/org/eclipse/jgit/storage/file/FileRepository.java328
-rw-r--r--org.eclipse.jgit/src/org/eclipse/jgit/storage/file/FileRepositoryBuilder.java (renamed from org.eclipse.jgit/src/org/eclipse/jgit/lib/DeltaOfsPackedObjectLoader.java)74
-rw-r--r--org.eclipse.jgit/src/org/eclipse/jgit/storage/file/LargePackedDeltaObject.java230
-rw-r--r--org.eclipse.jgit/src/org/eclipse/jgit/storage/file/LargePackedWholeObject.java130
-rw-r--r--org.eclipse.jgit/src/org/eclipse/jgit/storage/file/LocalObjectRepresentation.java (renamed from org.eclipse.jgit/src/org/eclipse/jgit/lib/PackOutputStream.java)105
-rw-r--r--org.eclipse.jgit/src/org/eclipse/jgit/storage/file/LocalObjectToPack.java78
-rw-r--r--org.eclipse.jgit/src/org/eclipse/jgit/storage/file/LockFile.java (renamed from org.eclipse.jgit/src/org/eclipse/jgit/lib/LockFile.java)4
-rw-r--r--org.eclipse.jgit/src/org/eclipse/jgit/storage/file/ObjectDirectory.java (renamed from org.eclipse.jgit/src/org/eclipse/jgit/lib/ObjectDirectory.java)232
-rw-r--r--org.eclipse.jgit/src/org/eclipse/jgit/storage/file/ObjectDirectoryInserter.java182
-rw-r--r--org.eclipse.jgit/src/org/eclipse/jgit/storage/file/PackFile.java920
-rw-r--r--org.eclipse.jgit/src/org/eclipse/jgit/storage/file/PackIndex.java (renamed from org.eclipse.jgit/src/org/eclipse/jgit/lib/PackIndex.java)5
-rw-r--r--org.eclipse.jgit/src/org/eclipse/jgit/storage/file/PackIndexV1.java (renamed from org.eclipse.jgit/src/org/eclipse/jgit/lib/PackIndexV1.java)5
-rw-r--r--org.eclipse.jgit/src/org/eclipse/jgit/storage/file/PackIndexV2.java (renamed from org.eclipse.jgit/src/org/eclipse/jgit/lib/PackIndexV2.java)5
-rw-r--r--org.eclipse.jgit/src/org/eclipse/jgit/storage/file/PackIndexWriter.java (renamed from org.eclipse.jgit/src/org/eclipse/jgit/lib/PackIndexWriter.java)4
-rw-r--r--org.eclipse.jgit/src/org/eclipse/jgit/storage/file/PackIndexWriterV1.java (renamed from org.eclipse.jgit/src/org/eclipse/jgit/lib/PackIndexWriterV1.java)2
-rw-r--r--org.eclipse.jgit/src/org/eclipse/jgit/storage/file/PackIndexWriterV2.java (renamed from org.eclipse.jgit/src/org/eclipse/jgit/lib/PackIndexWriterV2.java)2
-rw-r--r--org.eclipse.jgit/src/org/eclipse/jgit/storage/file/PackInputStream.java85
-rw-r--r--org.eclipse.jgit/src/org/eclipse/jgit/storage/file/PackLock.java (renamed from org.eclipse.jgit/src/org/eclipse/jgit/lib/PackLock.java)4
-rw-r--r--org.eclipse.jgit/src/org/eclipse/jgit/storage/file/PackReverseIndex.java (renamed from org.eclipse.jgit/src/org/eclipse/jgit/lib/PackReverseIndex.java)5
-rw-r--r--org.eclipse.jgit/src/org/eclipse/jgit/storage/file/RefDirectory.java (renamed from org.eclipse.jgit/src/org/eclipse/jgit/lib/RefDirectory.java)56
-rw-r--r--org.eclipse.jgit/src/org/eclipse/jgit/storage/file/RefDirectoryRename.java (renamed from org.eclipse.jgit/src/org/eclipse/jgit/lib/RefDirectoryRename.java)9
-rw-r--r--org.eclipse.jgit/src/org/eclipse/jgit/storage/file/RefDirectoryUpdate.java (renamed from org.eclipse.jgit/src/org/eclipse/jgit/lib/RefDirectoryUpdate.java)6
-rw-r--r--org.eclipse.jgit/src/org/eclipse/jgit/storage/file/ReflogReader.java (renamed from org.eclipse.jgit/src/org/eclipse/jgit/lib/ReflogReader.java)6
-rw-r--r--org.eclipse.jgit/src/org/eclipse/jgit/storage/file/UnpackedObject.java413
-rw-r--r--org.eclipse.jgit/src/org/eclipse/jgit/storage/file/UnpackedObjectCache.java (renamed from org.eclipse.jgit/src/org/eclipse/jgit/lib/UnpackedObjectCache.java)2
-rw-r--r--org.eclipse.jgit/src/org/eclipse/jgit/storage/file/WindowCache.java (renamed from org.eclipse.jgit/src/org/eclipse/jgit/lib/WindowCache.java)2
-rw-r--r--org.eclipse.jgit/src/org/eclipse/jgit/storage/file/WindowCacheConfig.java (renamed from org.eclipse.jgit/src/org/eclipse/jgit/lib/WindowCacheConfig.java)4
-rw-r--r--org.eclipse.jgit/src/org/eclipse/jgit/storage/file/WindowCursor.java (renamed from org.eclipse.jgit/src/org/eclipse/jgit/lib/WindowCursor.java)141
-rw-r--r--org.eclipse.jgit/src/org/eclipse/jgit/storage/pack/BinaryDelta.java (renamed from org.eclipse.jgit/src/org/eclipse/jgit/lib/BinaryDelta.java)149
-rw-r--r--org.eclipse.jgit/src/org/eclipse/jgit/storage/pack/DeltaCache.java134
-rw-r--r--org.eclipse.jgit/src/org/eclipse/jgit/storage/pack/DeltaEncoder.java294
-rw-r--r--org.eclipse.jgit/src/org/eclipse/jgit/storage/pack/DeltaIndex.java586
-rw-r--r--org.eclipse.jgit/src/org/eclipse/jgit/storage/pack/DeltaIndexScanner.java130
-rw-r--r--org.eclipse.jgit/src/org/eclipse/jgit/storage/pack/DeltaStream.java341
-rw-r--r--org.eclipse.jgit/src/org/eclipse/jgit/storage/pack/DeltaWindow.java511
-rw-r--r--org.eclipse.jgit/src/org/eclipse/jgit/storage/pack/DeltaWindowEntry.java80
-rw-r--r--org.eclipse.jgit/src/org/eclipse/jgit/storage/pack/ObjectReuseAsIs.java136
-rw-r--r--org.eclipse.jgit/src/org/eclipse/jgit/storage/pack/ObjectToPack.java320
-rw-r--r--org.eclipse.jgit/src/org/eclipse/jgit/storage/pack/PackConfig.java93
-rw-r--r--org.eclipse.jgit/src/org/eclipse/jgit/storage/pack/PackOutputStream.java213
-rw-r--r--org.eclipse.jgit/src/org/eclipse/jgit/storage/pack/PackWriter.java1442
-rw-r--r--org.eclipse.jgit/src/org/eclipse/jgit/storage/pack/StoredObjectRepresentation.java90
-rw-r--r--org.eclipse.jgit/src/org/eclipse/jgit/storage/pack/ThreadSafeDeltaCache.java86
-rw-r--r--org.eclipse.jgit/src/org/eclipse/jgit/transport/BasePackFetchConnection.java8
-rw-r--r--org.eclipse.jgit/src/org/eclipse/jgit/transport/BasePackPushConnection.java43
-rw-r--r--org.eclipse.jgit/src/org/eclipse/jgit/transport/BundleFetchConnection.java92
-rw-r--r--org.eclipse.jgit/src/org/eclipse/jgit/transport/BundleWriter.java69
-rw-r--r--org.eclipse.jgit/src/org/eclipse/jgit/transport/FetchConnection.java2
-rw-r--r--org.eclipse.jgit/src/org/eclipse/jgit/transport/FetchProcess.java47
-rw-r--r--org.eclipse.jgit/src/org/eclipse/jgit/transport/IndexPack.java40
-rw-r--r--org.eclipse.jgit/src/org/eclipse/jgit/transport/PushProcess.java52
-rw-r--r--org.eclipse.jgit/src/org/eclipse/jgit/transport/ReceivePack.java9
-rw-r--r--org.eclipse.jgit/src/org/eclipse/jgit/transport/RefAdvertiser.java26
-rw-r--r--org.eclipse.jgit/src/org/eclipse/jgit/transport/RemoteRefUpdate.java2
-rw-r--r--org.eclipse.jgit/src/org/eclipse/jgit/transport/Transport.java2
-rw-r--r--org.eclipse.jgit/src/org/eclipse/jgit/transport/TransportAmazonS3.java45
-rw-r--r--org.eclipse.jgit/src/org/eclipse/jgit/transport/TransportHttp.java2
-rw-r--r--org.eclipse.jgit/src/org/eclipse/jgit/transport/TransportLocal.java5
-rw-r--r--org.eclipse.jgit/src/org/eclipse/jgit/transport/UploadPack.java55
-rw-r--r--org.eclipse.jgit/src/org/eclipse/jgit/transport/WalkFetchConnection.java146
-rw-r--r--org.eclipse.jgit/src/org/eclipse/jgit/transport/WalkPushConnection.java10
-rw-r--r--org.eclipse.jgit/src/org/eclipse/jgit/transport/WalkRemoteObjectDatabase.java2
-rw-r--r--org.eclipse.jgit/src/org/eclipse/jgit/treewalk/AbstractTreeIterator.java42
-rw-r--r--org.eclipse.jgit/src/org/eclipse/jgit/treewalk/CanonicalTreeParser.java78
-rw-r--r--org.eclipse.jgit/src/org/eclipse/jgit/treewalk/EmptyTreeIterator.java4
-rw-r--r--org.eclipse.jgit/src/org/eclipse/jgit/treewalk/FileTreeIterator.java5
-rw-r--r--org.eclipse.jgit/src/org/eclipse/jgit/treewalk/NameConflictTreeWalk.java13
-rw-r--r--org.eclipse.jgit/src/org/eclipse/jgit/treewalk/TreeWalk.java56
-rw-r--r--org.eclipse.jgit/src/org/eclipse/jgit/util/ChangeIdUtil.java11
-rw-r--r--org.eclipse.jgit/src/org/eclipse/jgit/util/LongList.java14
-rw-r--r--org.eclipse.jgit/src/org/eclipse/jgit/util/SystemReader.java2
-rw-r--r--org.eclipse.jgit/src/org/eclipse/jgit/util/TemporaryBuffer.java52
242 files changed, 13969 insertions, 5573 deletions
diff --git a/org.eclipse.jgit.http.server/META-INF/MANIFEST.MF b/org.eclipse.jgit.http.server/META-INF/MANIFEST.MF
index b9aeef5195..edaa1edd19 100644
--- a/org.eclipse.jgit.http.server/META-INF/MANIFEST.MF
+++ b/org.eclipse.jgit.http.server/META-INF/MANIFEST.MF
@@ -17,6 +17,7 @@ Import-Package: javax.servlet;version="[2.5.0,3.0.0)",
org.eclipse.jgit.lib;version="[0.9.0,0.10.0)",
org.eclipse.jgit.nls;version="[0.9.0,0.10.0)",
org.eclipse.jgit.revwalk;version="[0.9.0,0.10.0)",
+ org.eclipse.jgit.storage.file;version="[0.9.0,0.10.0)",
org.eclipse.jgit.transport;version="[0.9.0,0.10.0)",
org.eclipse.jgit.util;version="[0.9.0,0.10.0)",
org.eclipse.jgit.util.io;version="[0.9.0,0.10.0)"
diff --git a/org.eclipse.jgit.http.server/src/org/eclipse/jgit/http/server/InfoPacksServlet.java b/org.eclipse.jgit.http.server/src/org/eclipse/jgit/http/server/InfoPacksServlet.java
index dff1e8252e..d217fe1c35 100644
--- a/org.eclipse.jgit.http.server/src/org/eclipse/jgit/http/server/InfoPacksServlet.java
+++ b/org.eclipse.jgit.http.server/src/org/eclipse/jgit/http/server/InfoPacksServlet.java
@@ -53,8 +53,8 @@ import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import org.eclipse.jgit.lib.ObjectDatabase;
-import org.eclipse.jgit.lib.ObjectDirectory;
-import org.eclipse.jgit.lib.PackFile;
+import org.eclipse.jgit.storage.file.ObjectDirectory;
+import org.eclipse.jgit.storage.file.PackFile;
/** Sends the current list of pack files, sorted most recent first. */
class InfoPacksServlet extends HttpServlet {
diff --git a/org.eclipse.jgit.http.server/src/org/eclipse/jgit/http/server/InfoRefsServlet.java b/org.eclipse.jgit.http.server/src/org/eclipse/jgit/http/server/InfoRefsServlet.java
index f667ce95a7..647919e065 100644
--- a/org.eclipse.jgit.http.server/src/org/eclipse/jgit/http/server/InfoRefsServlet.java
+++ b/org.eclipse.jgit.http.server/src/org/eclipse/jgit/http/server/InfoRefsServlet.java
@@ -74,30 +74,35 @@ class InfoRefsServlet extends HttpServlet {
final Repository db = getRepository(req);
final RevWalk walk = new RevWalk(db);
- final RevFlag ADVERTISED = walk.newFlag("ADVERTISED");
+ try {
+ final RevFlag ADVERTISED = walk.newFlag("ADVERTISED");
- final OutputStreamWriter out = new OutputStreamWriter(
- new SmartOutputStream(req, rsp), Constants.CHARSET);
- final RefAdvertiser adv = new RefAdvertiser() {
- @Override
- protected void writeOne(final CharSequence line) throws IOException {
- // Whoever decided that info/refs should use a different
- // delimiter than the native git:// protocol shouldn't
- // be allowed to design this sort of stuff. :-(
- out.append(line.toString().replace(' ', '\t'));
- }
+ final OutputStreamWriter out = new OutputStreamWriter(
+ new SmartOutputStream(req, rsp), Constants.CHARSET);
+ final RefAdvertiser adv = new RefAdvertiser() {
+ @Override
+ protected void writeOne(final CharSequence line)
+ throws IOException {
+ // Whoever decided that info/refs should use a different
+ // delimiter than the native git:// protocol shouldn't
+ // be allowed to design this sort of stuff. :-(
+ out.append(line.toString().replace(' ', '\t'));
+ }
- @Override
- protected void end() {
- // No end marker required for info/refs format.
- }
- };
- adv.init(walk, ADVERTISED);
- adv.setDerefTags(true);
+ @Override
+ protected void end() {
+ // No end marker required for info/refs format.
+ }
+ };
+ adv.init(walk, ADVERTISED);
+ adv.setDerefTags(true);
- Map<String, Ref> refs = db.getAllRefs();
- refs.remove(Constants.HEAD);
- adv.send(refs);
- out.close();
+ Map<String, Ref> refs = db.getAllRefs();
+ refs.remove(Constants.HEAD);
+ adv.send(refs);
+ out.close();
+ } finally {
+ walk.release();
+ }
}
}
diff --git a/org.eclipse.jgit.http.server/src/org/eclipse/jgit/http/server/IsLocalFilter.java b/org.eclipse.jgit.http.server/src/org/eclipse/jgit/http/server/IsLocalFilter.java
index 34edf82792..019ec90bc4 100644
--- a/org.eclipse.jgit.http.server/src/org/eclipse/jgit/http/server/IsLocalFilter.java
+++ b/org.eclipse.jgit.http.server/src/org/eclipse/jgit/http/server/IsLocalFilter.java
@@ -56,8 +56,8 @@ import javax.servlet.ServletRequest;
import javax.servlet.ServletResponse;
import javax.servlet.http.HttpServletResponse;
-import org.eclipse.jgit.lib.ObjectDirectory;
import org.eclipse.jgit.lib.Repository;
+import org.eclipse.jgit.storage.file.ObjectDirectory;
/**
* Requires the target {@link Repository} to be available via local filesystem.
diff --git a/org.eclipse.jgit.http.server/src/org/eclipse/jgit/http/server/ObjectFileServlet.java b/org.eclipse.jgit.http.server/src/org/eclipse/jgit/http/server/ObjectFileServlet.java
index 5d774a8248..84865121c0 100644
--- a/org.eclipse.jgit.http.server/src/org/eclipse/jgit/http/server/ObjectFileServlet.java
+++ b/org.eclipse.jgit.http.server/src/org/eclipse/jgit/http/server/ObjectFileServlet.java
@@ -60,8 +60,8 @@ import javax.servlet.http.HttpServlet;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
-import org.eclipse.jgit.lib.ObjectDirectory;
import org.eclipse.jgit.lib.Repository;
+import org.eclipse.jgit.storage.file.ObjectDirectory;
/** Sends any object from {@code GIT_DIR/objects/??/0 38}, or any pack file. */
abstract class ObjectFileServlet extends HttpServlet {
diff --git a/org.eclipse.jgit.http.server/src/org/eclipse/jgit/http/server/ReceivePackServlet.java b/org.eclipse.jgit.http.server/src/org/eclipse/jgit/http/server/ReceivePackServlet.java
index 49fd535a71..4bc05c1886 100644
--- a/org.eclipse.jgit.http.server/src/org/eclipse/jgit/http/server/ReceivePackServlet.java
+++ b/org.eclipse.jgit.http.server/src/org/eclipse/jgit/http/server/ReceivePackServlet.java
@@ -83,7 +83,12 @@ class ReceivePackServlet extends HttpServlet {
protected void advertise(HttpServletRequest req, Repository db,
PacketLineOutRefAdvertiser pck) throws IOException,
ServiceNotEnabledException, ServiceNotAuthorizedException {
- receivePackFactory.create(req, db).sendAdvertisedRefs(pck);
+ ReceivePack rp = receivePackFactory.create(req, db);
+ try {
+ rp.sendAdvertisedRefs(pck);
+ } finally {
+ rp.getRevWalk().release();
+ }
}
}
diff --git a/org.eclipse.jgit.http.server/src/org/eclipse/jgit/http/server/TextFileServlet.java b/org.eclipse.jgit.http.server/src/org/eclipse/jgit/http/server/TextFileServlet.java
index 5bf5546cf7..650059bd38 100644
--- a/org.eclipse.jgit.http.server/src/org/eclipse/jgit/http/server/TextFileServlet.java
+++ b/org.eclipse.jgit.http.server/src/org/eclipse/jgit/http/server/TextFileServlet.java
@@ -80,6 +80,8 @@ class TextFileServlet extends HttpServlet {
private byte[] read(final HttpServletRequest req) throws IOException {
final File gitdir = getRepository(req).getDirectory();
+ if (gitdir == null)
+ throw new FileNotFoundException(fileName);
return IO.readFully(new File(gitdir, fileName));
}
}
diff --git a/org.eclipse.jgit.http.server/src/org/eclipse/jgit/http/server/UploadPackServlet.java b/org.eclipse.jgit.http.server/src/org/eclipse/jgit/http/server/UploadPackServlet.java
index 92d41a0caf..602d66a90c 100644
--- a/org.eclipse.jgit.http.server/src/org/eclipse/jgit/http/server/UploadPackServlet.java
+++ b/org.eclipse.jgit.http.server/src/org/eclipse/jgit/http/server/UploadPackServlet.java
@@ -83,7 +83,12 @@ class UploadPackServlet extends HttpServlet {
protected void advertise(HttpServletRequest req, Repository db,
PacketLineOutRefAdvertiser pck) throws IOException,
ServiceNotEnabledException, ServiceNotAuthorizedException {
- uploadPackFactory.create(req, db).sendAdvertisedRefs(pck);
+ UploadPack up = uploadPackFactory.create(req, db);
+ try {
+ up.sendAdvertisedRefs(pck);
+ } finally {
+ up.getRevWalk().release();
+ }
}
}
@@ -107,7 +112,12 @@ class UploadPackServlet extends HttpServlet {
up.setBiDirectionalPipe(false);
rsp.setContentType(RSP_TYPE);
- final SmartOutputStream out = new SmartOutputStream(req, rsp);
+ final SmartOutputStream out = new SmartOutputStream(req, rsp) {
+ @Override
+ public void flush() throws IOException {
+ doFlush();
+ }
+ };
up.upload(getInputStream(req), out, null);
out.close();
diff --git a/org.eclipse.jgit.http.server/src/org/eclipse/jgit/http/server/resolver/FileResolver.java b/org.eclipse.jgit.http.server/src/org/eclipse/jgit/http/server/resolver/FileResolver.java
index cc062dbe88..296725b678 100644
--- a/org.eclipse.jgit.http.server/src/org/eclipse/jgit/http/server/resolver/FileResolver.java
+++ b/org.eclipse.jgit.http.server/src/org/eclipse/jgit/http/server/resolver/FileResolver.java
@@ -138,8 +138,10 @@ public class FileResolver implements RepositoryResolver {
Repository db) throws IOException {
if (isExportAll())
return true;
- else
+ else if (db.getDirectory() != null)
return new File(db.getDirectory(), "git-daemon-export-ok").exists();
+ else
+ return false;
}
private static boolean isUnreasonableName(final String name) {
diff --git a/org.eclipse.jgit.http.test/META-INF/MANIFEST.MF b/org.eclipse.jgit.http.test/META-INF/MANIFEST.MF
index 370bd40353..21dd60817d 100644
--- a/org.eclipse.jgit.http.test/META-INF/MANIFEST.MF
+++ b/org.eclipse.jgit.http.test/META-INF/MANIFEST.MF
@@ -29,5 +29,6 @@ Import-Package: javax.servlet;version="[2.5.0,3.0.0)",
org.eclipse.jgit.junit;version="[0.9.0,0.10.0)",
org.eclipse.jgit.lib;version="[0.9.0,0.10.0)",
org.eclipse.jgit.revwalk;version="[0.9.0,0.10.0)",
+ org.eclipse.jgit.storage.file;version="[0.9.0,0.10.0)",
org.eclipse.jgit.transport;version="[0.9.0,0.10.0)",
org.eclipse.jgit.util;version="[0.9.0,0.10.0)"
diff --git a/org.eclipse.jgit.http.test/tst/org/eclipse/jgit/http/test/AdvertiseErrorTest.java b/org.eclipse.jgit.http.test/tst/org/eclipse/jgit/http/test/AdvertiseErrorTest.java
index 47d7806a1e..db4aa802e2 100644
--- a/org.eclipse.jgit.http.test/tst/org/eclipse/jgit/http/test/AdvertiseErrorTest.java
+++ b/org.eclipse.jgit.http.test/tst/org/eclipse/jgit/http/test/AdvertiseErrorTest.java
@@ -62,23 +62,24 @@ import org.eclipse.jgit.lib.Constants;
import org.eclipse.jgit.lib.NullProgressMonitor;
import org.eclipse.jgit.lib.ObjectId;
import org.eclipse.jgit.lib.Repository;
-import org.eclipse.jgit.lib.RepositoryConfig;
import org.eclipse.jgit.revwalk.RevBlob;
import org.eclipse.jgit.revwalk.RevCommit;
+import org.eclipse.jgit.storage.file.FileBasedConfig;
+import org.eclipse.jgit.storage.file.FileRepository;
import org.eclipse.jgit.transport.ReceivePack;
import org.eclipse.jgit.transport.RemoteRefUpdate;
import org.eclipse.jgit.transport.Transport;
import org.eclipse.jgit.transport.URIish;
public class AdvertiseErrorTest extends HttpTestCase {
- private Repository remoteRepository;
+ private FileRepository remoteRepository;
private URIish remoteURI;
protected void setUp() throws Exception {
super.setUp();
- final TestRepository src = createTestRepository();
+ final TestRepository<FileRepository> src = createTestRepository();
final String srcName = src.getRepository().getDirectory().getName();
ServletContextHandler app = server.addContext("/git");
@@ -114,7 +115,7 @@ public class AdvertiseErrorTest extends HttpTestCase {
remoteRepository = src.getRepository();
remoteURI = toURIish(app, srcName);
- RepositoryConfig cfg = remoteRepository.getConfig();
+ FileBasedConfig cfg = remoteRepository.getConfig();
cfg.setBoolean("http", null, "receivepack", true);
cfg.save();
}
diff --git a/org.eclipse.jgit.http.test/tst/org/eclipse/jgit/http/test/HookMessageTest.java b/org.eclipse.jgit.http.test/tst/org/eclipse/jgit/http/test/HookMessageTest.java
index 224ea05c18..18f8dc928b 100644
--- a/org.eclipse.jgit.http.test/tst/org/eclipse/jgit/http/test/HookMessageTest.java
+++ b/org.eclipse.jgit.http.test/tst/org/eclipse/jgit/http/test/HookMessageTest.java
@@ -64,9 +64,10 @@ import org.eclipse.jgit.lib.Constants;
import org.eclipse.jgit.lib.NullProgressMonitor;
import org.eclipse.jgit.lib.ObjectId;
import org.eclipse.jgit.lib.Repository;
-import org.eclipse.jgit.lib.RepositoryConfig;
import org.eclipse.jgit.revwalk.RevBlob;
import org.eclipse.jgit.revwalk.RevCommit;
+import org.eclipse.jgit.storage.file.FileBasedConfig;
+import org.eclipse.jgit.storage.file.FileRepository;
import org.eclipse.jgit.transport.PreReceiveHook;
import org.eclipse.jgit.transport.PushResult;
import org.eclipse.jgit.transport.ReceiveCommand;
@@ -76,14 +77,14 @@ import org.eclipse.jgit.transport.Transport;
import org.eclipse.jgit.transport.URIish;
public class HookMessageTest extends HttpTestCase {
- private Repository remoteRepository;
+ private FileRepository remoteRepository;
private URIish remoteURI;
protected void setUp() throws Exception {
super.setUp();
- final TestRepository src = createTestRepository();
+ final TestRepository<FileRepository> src = createTestRepository();
final String srcName = src.getRepository().getDirectory().getName();
ServletContextHandler app = server.addContext("/git");
@@ -124,7 +125,7 @@ public class HookMessageTest extends HttpTestCase {
remoteRepository = src.getRepository();
remoteURI = toURIish(app, srcName);
- RepositoryConfig cfg = remoteRepository.getConfig();
+ FileBasedConfig cfg = remoteRepository.getConfig();
cfg.setBoolean("http", null, "receivepack", true);
cfg.save();
}
diff --git a/org.eclipse.jgit.http.test/tst/org/eclipse/jgit/http/test/HttpClientTests.java b/org.eclipse.jgit.http.test/tst/org/eclipse/jgit/http/test/HttpClientTests.java
index 729466df3b..4cc141bb41 100644
--- a/org.eclipse.jgit.http.test/tst/org/eclipse/jgit/http/test/HttpClientTests.java
+++ b/org.eclipse.jgit.http.test/tst/org/eclipse/jgit/http/test/HttpClientTests.java
@@ -66,12 +66,13 @@ import org.eclipse.jgit.lib.Ref;
import org.eclipse.jgit.lib.RefUpdate;
import org.eclipse.jgit.lib.Repository;
import org.eclipse.jgit.revwalk.RevCommit;
+import org.eclipse.jgit.storage.file.FileRepository;
import org.eclipse.jgit.transport.FetchConnection;
import org.eclipse.jgit.transport.Transport;
import org.eclipse.jgit.transport.URIish;
public class HttpClientTests extends HttpTestCase {
- private TestRepository remoteRepository;
+ private TestRepository<FileRepository> remoteRepository;
private URIish dumbAuthNoneURI;
@@ -95,7 +96,7 @@ public class HttpClientTests extends HttpTestCase {
server.setUp();
- final String srcName = nameOf(remoteRepository);
+ final String srcName = nameOf(remoteRepository.getRepository());
dumbAuthNoneURI = toURIish(dNone, srcName);
dumbAuthBasicURI = toURIish(dBasic, srcName);
@@ -119,10 +120,10 @@ public class HttpClientTests extends HttpTestCase {
public Repository open(HttpServletRequest req, String name)
throws RepositoryNotFoundException,
ServiceNotEnabledException {
- if (!name.equals(nameOf(remoteRepository)))
+ final FileRepository db = remoteRepository.getRepository();
+ if (!name.equals(nameOf(db)))
throw new RepositoryNotFoundException(name);
- final Repository db = remoteRepository.getRepository();
db.incrementOpen();
return db;
}
@@ -133,8 +134,8 @@ public class HttpClientTests extends HttpTestCase {
return ctx;
}
- private static String nameOf(final TestRepository db) {
- return db.getRepository().getDirectory().getName();
+ private static String nameOf(final FileRepository db) {
+ return db.getDirectory().getName();
}
public void testRepositoryNotFound_Dumb() throws Exception {
@@ -198,7 +199,7 @@ public class HttpClientTests extends HttpTestCase {
}
public void testListRemote_Dumb_NoHEAD() throws Exception {
- Repository src = remoteRepository.getRepository();
+ FileRepository src = remoteRepository.getRepository();
File headref = new File(src.getDirectory(), Constants.HEAD);
assertTrue("HEAD used to be present", headref.delete());
assertFalse("HEAD is gone", headref.exists());
@@ -306,7 +307,7 @@ public class HttpClientTests extends HttpTestCase {
}
public void testListRemote_Smart_UploadPackDisabled() throws Exception {
- Repository src = remoteRepository.getRepository();
+ FileRepository src = remoteRepository.getRepository();
src.getConfig().setBoolean("http", null, "uploadpack", false);
src.getConfig().save();
diff --git a/org.eclipse.jgit.http.test/tst/org/eclipse/jgit/http/test/SmartClientSmartServerTest.java b/org.eclipse.jgit.http.test/tst/org/eclipse/jgit/http/test/SmartClientSmartServerTest.java
index f7b3bdb203..a7b51c6819 100644
--- a/org.eclipse.jgit.http.test/tst/org/eclipse/jgit/http/test/SmartClientSmartServerTest.java
+++ b/org.eclipse.jgit.http.test/tst/org/eclipse/jgit/http/test/SmartClientSmartServerTest.java
@@ -79,11 +79,12 @@ import org.eclipse.jgit.lib.Constants;
import org.eclipse.jgit.lib.NullProgressMonitor;
import org.eclipse.jgit.lib.ObjectId;
import org.eclipse.jgit.lib.Ref;
-import org.eclipse.jgit.lib.ReflogReader;
import org.eclipse.jgit.lib.Repository;
-import org.eclipse.jgit.lib.RepositoryConfig;
import org.eclipse.jgit.revwalk.RevBlob;
import org.eclipse.jgit.revwalk.RevCommit;
+import org.eclipse.jgit.storage.file.FileBasedConfig;
+import org.eclipse.jgit.storage.file.FileRepository;
+import org.eclipse.jgit.storage.file.ReflogReader;
import org.eclipse.jgit.transport.FetchConnection;
import org.eclipse.jgit.transport.HttpTransport;
import org.eclipse.jgit.transport.RemoteRefUpdate;
@@ -94,7 +95,7 @@ import org.eclipse.jgit.transport.URIish;
public class SmartClientSmartServerTest extends HttpTestCase {
private static final String HDR_TRANSFER_ENCODING = "Transfer-Encoding";
- private Repository remoteRepository;
+ private FileRepository remoteRepository;
private URIish remoteURI;
@@ -107,7 +108,7 @@ public class SmartClientSmartServerTest extends HttpTestCase {
protected void setUp() throws Exception {
super.setUp();
- final TestRepository src = createTestRepository();
+ final TestRepository<FileRepository> src = createTestRepository();
final String srcName = src.getRepository().getDirectory().getName();
ServletContextHandler app = server.addContext("/git");
@@ -489,10 +490,10 @@ public class SmartClientSmartServerTest extends HttpTestCase {
}
public void testPush_ChunkedEncoding() throws Exception {
- final TestRepository src = createTestRepository();
+ final TestRepository<FileRepository> src = createTestRepository();
final RevBlob Q_bin = src.blob(new TestRng("Q").nextBytes(128 * 1024));
final RevCommit Q = src.commit().add("Q", Q_bin).create();
- final Repository db = src.getRepository();
+ final FileRepository db = src.getRepository();
final String dstName = Constants.R_HEADS + "new.branch";
Transport t;
@@ -547,7 +548,7 @@ public class SmartClientSmartServerTest extends HttpTestCase {
}
private void enableReceivePack() throws IOException {
- final RepositoryConfig cfg = remoteRepository.getConfig();
+ final FileBasedConfig cfg = remoteRepository.getConfig();
cfg.setBoolean("http", null, "receivepack", true);
cfg.save();
}
diff --git a/org.eclipse.jgit.http.test/tst/org/eclipse/jgit/http/test/util/HttpTestCase.java b/org.eclipse.jgit.http.test/tst/org/eclipse/jgit/http/test/util/HttpTestCase.java
index e259757615..313b6ad90b 100644
--- a/org.eclipse.jgit.http.test/tst/org/eclipse/jgit/http/test/util/HttpTestCase.java
+++ b/org.eclipse.jgit.http.test/tst/org/eclipse/jgit/http/test/util/HttpTestCase.java
@@ -61,6 +61,7 @@ import org.eclipse.jgit.lib.ObjectId;
import org.eclipse.jgit.lib.Repository;
import org.eclipse.jgit.revwalk.RevCommit;
import org.eclipse.jgit.revwalk.RevObject;
+import org.eclipse.jgit.storage.file.FileRepository;
import org.eclipse.jgit.transport.RefSpec;
import org.eclipse.jgit.transport.RemoteRefUpdate;
import org.eclipse.jgit.transport.URIish;
@@ -82,8 +83,9 @@ public abstract class HttpTestCase extends LocalDiskRepositoryTestCase {
super.tearDown();
}
- protected TestRepository createTestRepository() throws Exception {
- return new TestRepository(createBareRepository());
+ protected TestRepository<FileRepository> createTestRepository()
+ throws IOException {
+ return new TestRepository<FileRepository>(createBareRepository());
}
protected URIish toURIish(String path) throws URISyntaxException {
diff --git a/org.eclipse.jgit.iplog/META-INF/MANIFEST.MF b/org.eclipse.jgit.iplog/META-INF/MANIFEST.MF
index 4ff6144004..31a19b5ca0 100644
--- a/org.eclipse.jgit.iplog/META-INF/MANIFEST.MF
+++ b/org.eclipse.jgit.iplog/META-INF/MANIFEST.MF
@@ -15,6 +15,7 @@ Import-Package: org.eclipse.jgit.diff;version="[0.9.0,0.10.0)",
org.eclipse.jgit.revplot;version="[0.9.0,0.10.0)",
org.eclipse.jgit.revwalk;version="[0.9.0,0.10.0)",
org.eclipse.jgit.revwalk.filter;version="[0.9.0,0.10.0)",
+ org.eclipse.jgit.storage.file;version="[0.9.0,0.10.0)",
org.eclipse.jgit.transport;version="[0.9.0,0.10.0)",
org.eclipse.jgit.treewalk;version="[0.9.0,0.10.0)",
org.eclipse.jgit.treewalk.filter;version="[0.9.0,0.10.0)",
diff --git a/org.eclipse.jgit.iplog/src/org/eclipse/jgit/iplog/IpLogGenerator.java b/org.eclipse.jgit.iplog/src/org/eclipse/jgit/iplog/IpLogGenerator.java
index f64c329847..433d4338df 100644
--- a/org.eclipse.jgit.iplog/src/org/eclipse/jgit/iplog/IpLogGenerator.java
+++ b/org.eclipse.jgit.iplog/src/org/eclipse/jgit/iplog/IpLogGenerator.java
@@ -78,15 +78,13 @@ import org.eclipse.jgit.diff.EditList;
import org.eclipse.jgit.diff.MyersDiff;
import org.eclipse.jgit.diff.RawText;
import org.eclipse.jgit.errors.ConfigInvalidException;
-import org.eclipse.jgit.errors.MissingObjectException;
import org.eclipse.jgit.iplog.Committer.ActiveRange;
import org.eclipse.jgit.lib.BlobBasedConfig;
import org.eclipse.jgit.lib.Constants;
import org.eclipse.jgit.lib.MutableObjectId;
-import org.eclipse.jgit.lib.ObjectLoader;
+import org.eclipse.jgit.lib.ObjectReader;
import org.eclipse.jgit.lib.PersonIdent;
import org.eclipse.jgit.lib.Repository;
-import org.eclipse.jgit.lib.WindowCursor;
import org.eclipse.jgit.revwalk.RevCommit;
import org.eclipse.jgit.revwalk.RevTree;
import org.eclipse.jgit.revwalk.RevWalk;
@@ -144,7 +142,7 @@ public class IpLogGenerator {
private NameConflictTreeWalk tw;
- private final WindowCursor curs = new WindowCursor();
+ private ObjectReader curs;
private final MutableObjectId idbuf = new MutableObjectId();
@@ -184,8 +182,9 @@ public class IpLogGenerator {
throws IOException, ConfigInvalidException {
try {
db = repo;
- rw = new RevWalk(db);
- tw = new NameConflictTreeWalk(db);
+ curs = db.newObjectReader();
+ rw = new RevWalk(curs);
+ tw = new NameConflictTreeWalk(curs);
RevCommit c = rw.parseCommit(startCommit);
@@ -194,7 +193,7 @@ public class IpLogGenerator {
scanProjectCommits(meta.getProjects().get(0), c);
commits.add(c);
} finally {
- WindowCursor.release(curs);
+ curs.release();
db = null;
rw = null;
tw = null;
@@ -417,10 +416,7 @@ public class IpLogGenerator {
private byte[] openBlob(int side) throws IOException {
tw.getObjectId(idbuf, side);
- ObjectLoader ldr = db.openObject(curs, idbuf);
- if (ldr == null)
- throw new MissingObjectException(idbuf.copy(), Constants.OBJ_BLOB);
- return ldr.getCachedBytes();
+ return curs.open(idbuf, Constants.OBJ_BLOB).getCachedBytes();
}
/**
diff --git a/org.eclipse.jgit.iplog/src/org/eclipse/jgit/iplog/IpLogMeta.java b/org.eclipse.jgit.iplog/src/org/eclipse/jgit/iplog/IpLogMeta.java
index 89695bdb8d..2799a4a30b 100644
--- a/org.eclipse.jgit.iplog/src/org/eclipse/jgit/iplog/IpLogMeta.java
+++ b/org.eclipse.jgit.iplog/src/org/eclipse/jgit/iplog/IpLogMeta.java
@@ -58,9 +58,9 @@ import java.util.Set;
import org.eclipse.jgit.errors.ConfigInvalidException;
import org.eclipse.jgit.lib.Config;
import org.eclipse.jgit.lib.Constants;
-import org.eclipse.jgit.lib.FileBasedConfig;
-import org.eclipse.jgit.lib.LockFile;
import org.eclipse.jgit.lib.ObjectId;
+import org.eclipse.jgit.storage.file.FileBasedConfig;
+import org.eclipse.jgit.storage.file.LockFile;
/**
* Manages the {@code .eclipse_iplog} file in a project.
diff --git a/org.eclipse.jgit.junit/META-INF/MANIFEST.MF b/org.eclipse.jgit.junit/META-INF/MANIFEST.MF
index 051079b78e..7f4dcfddd6 100644
--- a/org.eclipse.jgit.junit/META-INF/MANIFEST.MF
+++ b/org.eclipse.jgit.junit/META-INF/MANIFEST.MF
@@ -18,6 +18,8 @@ Import-Package: junit.framework;version="[3.8.2,4.0.0)",
org.eclipse.jgit.revplot;version="[0.9.0,0.10.0)",
org.eclipse.jgit.revwalk;version="[0.9.0,0.10.0)",
org.eclipse.jgit.revwalk.filter;version="[0.9.0,0.10.0)",
+ org.eclipse.jgit.storage.file;version="[0.9.0,0.10.0)",
+ org.eclipse.jgit.storage.pack;version="[0.9.0,0.10.0)",
org.eclipse.jgit.transport;version="[0.9.0,0.10.0)",
org.eclipse.jgit.treewalk;version="[0.9.0,0.10.0)",
org.eclipse.jgit.treewalk.filter;version="[0.9.0,0.10.0)",
diff --git a/org.eclipse.jgit.junit/src/org/eclipse/jgit/junit/LocalDiskRepositoryTestCase.java b/org.eclipse.jgit.junit/src/org/eclipse/jgit/junit/LocalDiskRepositoryTestCase.java
index 001deb262b..2b82d82d74 100644
--- a/org.eclipse.jgit.junit/src/org/eclipse/jgit/junit/LocalDiskRepositoryTestCase.java
+++ b/org.eclipse.jgit.junit/src/org/eclipse/jgit/junit/LocalDiskRepositoryTestCase.java
@@ -62,12 +62,13 @@ import junit.framework.TestCase;
import org.eclipse.jgit.lib.AnyObjectId;
import org.eclipse.jgit.lib.Constants;
-import org.eclipse.jgit.lib.FileBasedConfig;
import org.eclipse.jgit.lib.PersonIdent;
import org.eclipse.jgit.lib.Repository;
import org.eclipse.jgit.lib.RepositoryCache;
-import org.eclipse.jgit.lib.WindowCache;
-import org.eclipse.jgit.lib.WindowCacheConfig;
+import org.eclipse.jgit.storage.file.FileBasedConfig;
+import org.eclipse.jgit.storage.file.FileRepository;
+import org.eclipse.jgit.storage.file.WindowCache;
+import org.eclipse.jgit.storage.file.WindowCacheConfig;
import org.eclipse.jgit.util.IO;
import org.eclipse.jgit.util.SystemReader;
@@ -259,7 +260,7 @@ public abstract class LocalDiskRepositoryTestCase extends TestCase {
* @throws IOException
* the repository could not be created in the temporary area
*/
- protected Repository createBareRepository() throws IOException {
+ protected FileRepository createBareRepository() throws IOException {
return createRepository(true /* bare */);
}
@@ -270,7 +271,7 @@ public abstract class LocalDiskRepositoryTestCase extends TestCase {
* @throws IOException
* the repository could not be created in the temporary area
*/
- protected Repository createWorkRepository() throws IOException {
+ protected FileRepository createWorkRepository() throws IOException {
return createRepository(false /* not bare */);
}
@@ -284,11 +285,11 @@ public abstract class LocalDiskRepositoryTestCase extends TestCase {
* @throws IOException
* the repository could not be created in the temporary area
*/
- private Repository createRepository(boolean bare) throws IOException {
+ private FileRepository createRepository(boolean bare) throws IOException {
String uniqueId = System.currentTimeMillis() + "_" + (testCount++);
String gitdirName = "test" + uniqueId + (bare ? "" : "/") + Constants.DOT_GIT;
File gitdir = new File(trash, gitdirName).getCanonicalFile();
- Repository db = new Repository(gitdir);
+ FileRepository db = new FileRepository(gitdir);
assertFalse(gitdir.exists());
db.create();
@@ -323,7 +324,7 @@ public abstract class LocalDiskRepositoryTestCase extends TestCase {
putPersonIdent(env, "AUTHOR", author);
putPersonIdent(env, "COMMITTER", committer);
- final File cwd = db.getWorkDir();
+ final File cwd = db.getWorkTree();
final Process p = Runtime.getRuntime().exec(argv, toEnvArray(env), cwd);
p.getOutputStream().close();
p.getErrorStream().close();
diff --git a/org.eclipse.jgit.junit/src/org/eclipse/jgit/junit/MockSystemReader.java b/org.eclipse.jgit.junit/src/org/eclipse/jgit/junit/MockSystemReader.java
index c502fb6344..eb08417bc8 100644
--- a/org.eclipse.jgit.junit/src/org/eclipse/jgit/junit/MockSystemReader.java
+++ b/org.eclipse.jgit.junit/src/org/eclipse/jgit/junit/MockSystemReader.java
@@ -52,7 +52,7 @@ import java.util.TimeZone;
import org.eclipse.jgit.errors.ConfigInvalidException;
import org.eclipse.jgit.lib.Constants;
-import org.eclipse.jgit.lib.FileBasedConfig;
+import org.eclipse.jgit.storage.file.FileBasedConfig;
import org.eclipse.jgit.util.FS;
import org.eclipse.jgit.util.SystemReader;
diff --git a/org.eclipse.jgit.junit/src/org/eclipse/jgit/junit/TestRepository.java b/org.eclipse.jgit.junit/src/org/eclipse/jgit/junit/TestRepository.java
index 59504aa780..3c58271257 100644
--- a/org.eclipse.jgit.junit/src/org/eclipse/jgit/junit/TestRepository.java
+++ b/org.eclipse.jgit.junit/src/org/eclipse/jgit/junit/TestRepository.java
@@ -73,22 +73,16 @@ import org.eclipse.jgit.lib.AnyObjectId;
import org.eclipse.jgit.lib.Commit;
import org.eclipse.jgit.lib.Constants;
import org.eclipse.jgit.lib.FileMode;
-import org.eclipse.jgit.lib.LockFile;
import org.eclipse.jgit.lib.NullProgressMonitor;
import org.eclipse.jgit.lib.ObjectChecker;
-import org.eclipse.jgit.lib.ObjectDatabase;
-import org.eclipse.jgit.lib.ObjectDirectory;
import org.eclipse.jgit.lib.ObjectId;
-import org.eclipse.jgit.lib.ObjectWriter;
-import org.eclipse.jgit.lib.PackFile;
-import org.eclipse.jgit.lib.PackWriter;
+import org.eclipse.jgit.lib.ObjectInserter;
import org.eclipse.jgit.lib.PersonIdent;
import org.eclipse.jgit.lib.Ref;
import org.eclipse.jgit.lib.RefUpdate;
import org.eclipse.jgit.lib.RefWriter;
import org.eclipse.jgit.lib.Repository;
import org.eclipse.jgit.lib.Tag;
-import org.eclipse.jgit.lib.PackIndex.MutableEntry;
import org.eclipse.jgit.revwalk.ObjectWalk;
import org.eclipse.jgit.revwalk.RevBlob;
import org.eclipse.jgit.revwalk.RevCommit;
@@ -96,11 +90,22 @@ import org.eclipse.jgit.revwalk.RevObject;
import org.eclipse.jgit.revwalk.RevTag;
import org.eclipse.jgit.revwalk.RevTree;
import org.eclipse.jgit.revwalk.RevWalk;
+import org.eclipse.jgit.storage.file.FileRepository;
+import org.eclipse.jgit.storage.file.LockFile;
+import org.eclipse.jgit.storage.file.ObjectDirectory;
+import org.eclipse.jgit.storage.file.PackFile;
+import org.eclipse.jgit.storage.file.PackIndex.MutableEntry;
+import org.eclipse.jgit.storage.pack.PackWriter;
import org.eclipse.jgit.treewalk.TreeWalk;
import org.eclipse.jgit.treewalk.filter.PathFilterGroup;
-/** Wrapper to make creating test data easier. */
-public class TestRepository {
+/**
+ * Wrapper to make creating test data easier.
+ *
+ * @param <R>
+ * type of Repository the test data is stored on.
+ */
+public class TestRepository<R extends Repository> {
private static final PersonIdent author;
private static final PersonIdent committer;
@@ -119,11 +124,11 @@ public class TestRepository {
committer = new PersonIdent(cn, ce, now, tz);
}
- private final Repository db;
+ private final R db;
private final RevWalk pool;
- private final ObjectWriter writer;
+ private final ObjectInserter inserter;
private long now;
@@ -132,9 +137,9 @@ public class TestRepository {
*
* @param db
* the test repository to write into.
- * @throws Exception
+ * @throws IOException
*/
- public TestRepository(Repository db) throws Exception {
+ public TestRepository(R db) throws IOException {
this(db, new RevWalk(db));
}
@@ -145,17 +150,17 @@ public class TestRepository {
* the test repository to write into.
* @param rw
* the RevObject pool to use for object lookup.
- * @throws Exception
+ * @throws IOException
*/
- public TestRepository(Repository db, RevWalk rw) throws Exception {
+ public TestRepository(R db, RevWalk rw) throws IOException {
this.db = db;
this.pool = rw;
- this.writer = new ObjectWriter(db);
+ this.inserter = db.newObjectInserter();
this.now = 1236977987000L;
}
/** @return the repository this helper class operates against. */
- public Repository getRepository() {
+ public R getRepository() {
return db;
}
@@ -200,7 +205,14 @@ public class TestRepository {
* @throws Exception
*/
public RevBlob blob(final byte[] content) throws Exception {
- return pool.lookupBlob(writer.writeBlob(content));
+ ObjectId id;
+ try {
+ id = inserter.insert(Constants.OBJ_BLOB, content);
+ inserter.flush();
+ } finally {
+ inserter.release();
+ }
+ return pool.lookupBlob(id);
}
/**
@@ -236,7 +248,14 @@ public class TestRepository {
for (final DirCacheEntry e : entries)
b.add(e);
b.finish();
- return pool.lookupTree(dc.writeTree(writer));
+ ObjectId root;
+ try {
+ root = dc.writeTree(inserter);
+ inserter.flush();
+ } finally {
+ inserter.release();
+ }
+ return pool.lookupTree(root);
}
/**
@@ -253,7 +272,7 @@ public class TestRepository {
*/
public RevObject get(final RevTree tree, final String path)
throws AssertionFailedError, Exception {
- final TreeWalk tw = new TreeWalk(db);
+ final TreeWalk tw = new TreeWalk(pool.getObjectReader());
tw.setFilter(PathFilterGroup.createFromStrings(Collections
.singleton(path)));
tw.reset(tree);
@@ -346,7 +365,14 @@ public class TestRepository {
c.setAuthor(new PersonIdent(author, new Date(now)));
c.setCommitter(new PersonIdent(committer, new Date(now)));
c.setMessage("");
- return pool.lookupCommit(writer.writeCommit(c));
+ ObjectId id;
+ try {
+ id = inserter.insert(Constants.OBJ_COMMIT, inserter.format(c));
+ inserter.flush();
+ } finally {
+ inserter.release();
+ }
+ return pool.lookupCommit(id);
}
/** @return a new commit builder. */
@@ -377,7 +403,14 @@ public class TestRepository {
t.setTag(name);
t.setTagger(new PersonIdent(committer, new Date(now)));
t.setMessage("");
- return (RevTag) pool.lookupAny(writer.writeTag(t), Constants.OBJ_TAG);
+ ObjectId id;
+ try {
+ id = inserter.insert(Constants.OBJ_TAG, inserter.format(t));
+ inserter.flush();
+ } finally {
+ inserter.release();
+ }
+ return (RevTag) pool.lookupAny(id, Constants.OBJ_TAG);
}
/**
@@ -443,25 +476,27 @@ public class TestRepository {
* @throws Exception
*/
public void updateServerInfo() throws Exception {
- final ObjectDatabase odb = db.getObjectDatabase();
- if (odb instanceof ObjectDirectory) {
- RefWriter rw = new RefWriter(db.getAllRefs().values()) {
+ if (db instanceof FileRepository) {
+ final FileRepository fr = (FileRepository) db;
+ RefWriter rw = new RefWriter(fr.getAllRefs().values()) {
@Override
protected void writeFile(final String name, final byte[] bin)
throws IOException {
- TestRepository.this.writeFile(name, bin);
+ File path = new File(fr.getDirectory(), name);
+ TestRepository.this.writeFile(path, bin);
}
};
rw.writePackedRefs();
rw.writeInfoRefs();
final StringBuilder w = new StringBuilder();
- for (PackFile p : ((ObjectDirectory) odb).getPacks()) {
+ for (PackFile p : fr.getObjectDatabase().getPacks()) {
w.append("P ");
w.append(p.getPackFile().getName());
w.append('\n');
}
- writeFile("objects/info/packs", Constants.encodeASCII(w.toString()));
+ writeFile(new File(new File(fr.getObjectDatabase().getDirectory(),
+ "info"), "packs"), Constants.encodeASCII(w.toString()));
}
}
@@ -528,7 +563,7 @@ public class TestRepository {
if (o == null)
break;
- final byte[] bin = db.openObject(o).getCachedBytes();
+ final byte[] bin = db.open(o, o.getType()).getCachedBytes();
oc.checkCommit(bin);
assertHash(o, bin);
}
@@ -538,7 +573,7 @@ public class TestRepository {
if (o == null)
break;
- final byte[] bin = db.openObject(o).getCachedBytes();
+ final byte[] bin = db.open(o, o.getType()).getCachedBytes();
oc.check(o.getType(), bin);
assertHash(o, bin);
}
@@ -563,38 +598,46 @@ public class TestRepository {
* @throws Exception
*/
public void packAndPrune() throws Exception {
- final ObjectDirectory odb = (ObjectDirectory) db.getObjectDatabase();
- final PackWriter pw = new PackWriter(db, NullProgressMonitor.INSTANCE);
-
- Set<ObjectId> all = new HashSet<ObjectId>();
- for (Ref r : db.getAllRefs().values())
- all.add(r.getObjectId());
- pw.preparePack(all, Collections.<ObjectId> emptySet());
-
- final ObjectId name = pw.computeName();
- OutputStream out;
-
- final File pack = nameFor(odb, name, ".pack");
- out = new BufferedOutputStream(new FileOutputStream(pack));
- try {
- pw.writePack(out);
- } finally {
- out.close();
- }
- pack.setReadOnly();
+ if (db.getObjectDatabase() instanceof ObjectDirectory) {
+ ObjectDirectory odb = (ObjectDirectory) db.getObjectDatabase();
+ NullProgressMonitor m = NullProgressMonitor.INSTANCE;
+
+ final File pack, idx;
+ PackWriter pw = new PackWriter(db);
+ try {
+ Set<ObjectId> all = new HashSet<ObjectId>();
+ for (Ref r : db.getAllRefs().values())
+ all.add(r.getObjectId());
+ pw.preparePack(m, all, Collections.<ObjectId> emptySet());
+
+ final ObjectId name = pw.computeName();
+ OutputStream out;
+
+ pack = nameFor(odb, name, ".pack");
+ out = new BufferedOutputStream(new FileOutputStream(pack));
+ try {
+ pw.writePack(m, m, out);
+ } finally {
+ out.close();
+ }
+ pack.setReadOnly();
+
+ idx = nameFor(odb, name, ".idx");
+ out = new BufferedOutputStream(new FileOutputStream(idx));
+ try {
+ pw.writeIndex(out);
+ } finally {
+ out.close();
+ }
+ idx.setReadOnly();
+ } finally {
+ pw.release();
+ }
- final File idx = nameFor(odb, name, ".idx");
- out = new BufferedOutputStream(new FileOutputStream(idx));
- try {
- pw.writeIndex(out);
- } finally {
- out.close();
+ odb.openPack(pack, idx);
+ updateServerInfo();
+ prunePacked(odb);
}
- idx.setReadOnly();
-
- odb.openPack(pack, idx);
- updateServerInfo();
- prunePacked(odb);
}
private void prunePacked(ObjectDirectory odb) {
@@ -609,9 +652,8 @@ public class TestRepository {
return new File(packdir, "pack-" + name.name() + t);
}
- private void writeFile(final String name, final byte[] bin)
- throws IOException, ObjectWritingException {
- final File p = new File(db.getDirectory(), name);
+ private void writeFile(final File p, final byte[] bin) throws IOException,
+ ObjectWritingException {
final LockFile lck = new LockFile(p);
if (!lck.lock())
throw new ObjectWritingException("Can't write " + p);
@@ -711,7 +753,8 @@ public class TestRepository {
if (parents.isEmpty()) {
DirCacheBuilder b = tree.builder();
parseBody(p);
- b.addTree(new byte[0], DirCacheEntry.STAGE_0, db, p.getTree());
+ b.addTree(new byte[0], DirCacheEntry.STAGE_0, pool
+ .getObjectReader(), p.getTree());
b.finish();
}
parents.add(p);
@@ -769,13 +812,21 @@ public class TestRepository {
TestRepository.this.tick(tick);
final Commit c = new Commit(db);
- c.setTreeId(pool.lookupTree(tree.writeTree(writer)));
c.setParentIds(parents.toArray(new RevCommit[parents.size()]));
c.setAuthor(new PersonIdent(author, new Date(now)));
c.setCommitter(new PersonIdent(committer, new Date(now)));
c.setMessage(message);
- self = pool.lookupCommit(writer.writeCommit(c));
+ ObjectId commitId;
+ try {
+ c.setTreeId(tree.writeTree(inserter));
+ commitId = inserter.insert(Constants.OBJ_COMMIT, inserter
+ .format(c));
+ inserter.flush();
+ } finally {
+ inserter.release();
+ }
+ self = pool.lookupCommit(commitId);
if (branch != null)
branch.update(self);
diff --git a/org.eclipse.jgit.pgm/META-INF/MANIFEST.MF b/org.eclipse.jgit.pgm/META-INF/MANIFEST.MF
index 8628896780..d6eac232e1 100644
--- a/org.eclipse.jgit.pgm/META-INF/MANIFEST.MF
+++ b/org.eclipse.jgit.pgm/META-INF/MANIFEST.MF
@@ -17,6 +17,8 @@ Import-Package: org.eclipse.jgit.api;version="[0.9.0,0.10.0)",
org.eclipse.jgit.revplot;version="[0.9.0,0.10.0)",
org.eclipse.jgit.revwalk;version="[0.9.0,0.10.0)",
org.eclipse.jgit.revwalk.filter;version="[0.9.0,0.10.0)",
+ org.eclipse.jgit.storage.file;version="[0.9.0,0.10.0)",
+ org.eclipse.jgit.storage.pack;version="[0.9.0,0.10.0)",
org.eclipse.jgit.transport;version="[0.9.0,0.10.0)",
org.eclipse.jgit.treewalk;version="[0.9.0,0.10.0)",
org.eclipse.jgit.treewalk.filter;version="[0.9.0,0.10.0)",
diff --git a/org.eclipse.jgit.pgm/META-INF/services/org.eclipse.jgit.pgm.TextBuiltin b/org.eclipse.jgit.pgm/META-INF/services/org.eclipse.jgit.pgm.TextBuiltin
index 0d4a140e62..075cadef79 100644
--- a/org.eclipse.jgit.pgm/META-INF/services/org.eclipse.jgit.pgm.TextBuiltin
+++ b/org.eclipse.jgit.pgm/META-INF/services/org.eclipse.jgit.pgm.TextBuiltin
@@ -30,6 +30,7 @@ org.eclipse.jgit.pgm.debug.RebuildCommitGraph
org.eclipse.jgit.pgm.debug.ShowCacheTree
org.eclipse.jgit.pgm.debug.ShowCommands
org.eclipse.jgit.pgm.debug.ShowDirCache
+org.eclipse.jgit.pgm.debug.ShowPackDelta
org.eclipse.jgit.pgm.debug.WriteDirCache
org.eclipse.jgit.pgm.eclipse.Iplog
diff --git a/org.eclipse.jgit.pgm/src/org/eclipse/jgit/pgm/Clone.java b/org.eclipse.jgit.pgm/src/org/eclipse/jgit/pgm/Clone.java
index b0f51ec58a..22302bba81 100644
--- a/org.eclipse.jgit.pgm/src/org/eclipse/jgit/pgm/Clone.java
+++ b/org.eclipse.jgit.pgm/src/org/eclipse/jgit/pgm/Clone.java
@@ -61,10 +61,10 @@ import org.eclipse.jgit.lib.GitIndex;
import org.eclipse.jgit.lib.Ref;
import org.eclipse.jgit.lib.RefComparator;
import org.eclipse.jgit.lib.RefUpdate;
-import org.eclipse.jgit.lib.Repository;
import org.eclipse.jgit.lib.TextProgressMonitor;
import org.eclipse.jgit.lib.Tree;
import org.eclipse.jgit.lib.WorkDirCheckout;
+import org.eclipse.jgit.storage.file.FileRepository;
import org.eclipse.jgit.transport.FetchResult;
import org.eclipse.jgit.transport.RefSpec;
import org.eclipse.jgit.transport.RemoteConfig;
@@ -82,6 +82,8 @@ class Clone extends AbstractFetchCommand {
@Argument(index = 1, metaVar = "metaVar_directory")
private String localName;
+ private FileRepository dst;
+
@Override
protected final boolean requiresRepository() {
return false;
@@ -103,10 +105,11 @@ class Clone extends AbstractFetchCommand {
if (gitdir == null)
gitdir = new File(localName, Constants.DOT_GIT);
- db = new Repository(gitdir);
- db.create();
- db.getConfig().setBoolean("core", null, "bare", false);
- db.getConfig().save();
+ dst = new FileRepository(gitdir);
+ dst.create();
+ dst.getConfig().setBoolean("core", null, "bare", false);
+ dst.getConfig().save();
+ db = dst;
out.format(CLIText.get().initializedEmptyGitRepositoryIn, gitdir.getAbsolutePath());
out.println();
@@ -120,13 +123,13 @@ class Clone extends AbstractFetchCommand {
private void saveRemote(final URIish uri) throws URISyntaxException,
IOException {
- final RemoteConfig rc = new RemoteConfig(db.getConfig(), remoteName);
+ final RemoteConfig rc = new RemoteConfig(dst.getConfig(), remoteName);
rc.addURI(uri);
rc.addFetchRefSpec(new RefSpec().setForceUpdate(true)
.setSourceDestination(Constants.R_HEADS + "*",
Constants.R_REMOTES + remoteName + "/*"));
- rc.update(db.getConfig());
- db.getConfig().save();
+ rc.update(dst.getConfig());
+ dst.getConfig().save();
}
private FetchResult runFetch() throws NotSupportedException,
@@ -180,7 +183,7 @@ class Clone extends AbstractFetchCommand {
final Tree tree = commit.getTree();
final WorkDirCheckout co;
- co = new WorkDirCheckout(db, db.getWorkDir(), index, tree);
+ co = new WorkDirCheckout(db, db.getWorkTree(), index, tree);
co.checkout();
index.write();
}
diff --git a/org.eclipse.jgit.pgm/src/org/eclipse/jgit/pgm/Glog.java b/org.eclipse.jgit.pgm/src/org/eclipse/jgit/pgm/Glog.java
index 3dfd8ff62d..ae11f67317 100644
--- a/org.eclipse.jgit.pgm/src/org/eclipse/jgit/pgm/Glog.java
+++ b/org.eclipse.jgit.pgm/src/org/eclipse/jgit/pgm/Glog.java
@@ -125,10 +125,12 @@ class Glog extends RevWalkTextBuiltin {
}
private String repoName() {
- final File f = db.getDirectory();
- String n = f.getName();
+ final File gitDir = db.getDirectory();
+ if (gitDir == null)
+ return db.toString();
+ String n = gitDir.getName();
if (Constants.DOT_GIT.equals(n))
- n = f.getParentFile().getName();
+ n = gitDir.getParentFile().getName();
return n;
}
}
diff --git a/org.eclipse.jgit.pgm/src/org/eclipse/jgit/pgm/IndexPack.java b/org.eclipse.jgit.pgm/src/org/eclipse/jgit/pgm/IndexPack.java
index 35fd2a5971..640c8ef348 100644
--- a/org.eclipse.jgit.pgm/src/org/eclipse/jgit/pgm/IndexPack.java
+++ b/org.eclipse.jgit.pgm/src/org/eclipse/jgit/pgm/IndexPack.java
@@ -49,6 +49,7 @@ import java.io.File;
import org.kohsuke.args4j.Argument;
import org.kohsuke.args4j.Option;
+import org.eclipse.jgit.lib.CoreConfig;
import org.eclipse.jgit.lib.TextProgressMonitor;
class IndexPack extends TextBuiltin {
@@ -64,7 +65,8 @@ class IndexPack extends TextBuiltin {
@Override
protected void run() throws Exception {
if (indexVersion == -1)
- indexVersion = db.getConfig().getCore().getPackIndexVersion();
+ indexVersion = db.getConfig().get(CoreConfig.KEY)
+ .getPackIndexVersion();
final BufferedInputStream in;
final org.eclipse.jgit.transport.IndexPack ip;
in = new BufferedInputStream(System.in);
diff --git a/org.eclipse.jgit.pgm/src/org/eclipse/jgit/pgm/Init.java b/org.eclipse.jgit.pgm/src/org/eclipse/jgit/pgm/Init.java
index d8c7bdfb4a..c5a696a57f 100644
--- a/org.eclipse.jgit.pgm/src/org/eclipse/jgit/pgm/Init.java
+++ b/org.eclipse.jgit.pgm/src/org/eclipse/jgit/pgm/Init.java
@@ -50,7 +50,7 @@ import java.text.MessageFormat;
import org.kohsuke.args4j.Option;
import org.eclipse.jgit.lib.Constants;
-import org.eclipse.jgit.lib.Repository;
+import org.eclipse.jgit.storage.file.FileRepository;
@Command(common = true, usage = "usage_CreateAnEmptyGitRepository")
class Init extends TextBuiltin {
@@ -66,7 +66,7 @@ class Init extends TextBuiltin {
protected void run() throws Exception {
if (gitdir == null)
gitdir = new File(bare ? "." : Constants.DOT_GIT);
- db = new Repository(gitdir);
+ db = new FileRepository(gitdir);
db.create(bare);
out.println(MessageFormat.format(CLIText.get().initializedEmptyGitRepositoryIn, gitdir.getAbsolutePath()));
}
diff --git a/org.eclipse.jgit.pgm/src/org/eclipse/jgit/pgm/Main.java b/org.eclipse.jgit.pgm/src/org/eclipse/jgit/pgm/Main.java
index 306ac816d8..ab11062cc2 100644
--- a/org.eclipse.jgit.pgm/src/org/eclipse/jgit/pgm/Main.java
+++ b/org.eclipse.jgit.pgm/src/org/eclipse/jgit/pgm/Main.java
@@ -51,20 +51,15 @@ import java.net.MalformedURLException;
import java.net.URL;
import java.text.MessageFormat;
import java.util.ArrayList;
-import java.util.Arrays;
-import java.util.HashSet;
import java.util.List;
-import java.util.Set;
import org.eclipse.jgit.awtui.AwtAuthenticator;
import org.eclipse.jgit.awtui.AwtSshSessionFactory;
import org.eclipse.jgit.errors.TransportException;
-import org.eclipse.jgit.lib.Constants;
-import org.eclipse.jgit.lib.Repository;
+import org.eclipse.jgit.lib.RepositoryBuilder;
import org.eclipse.jgit.pgm.opt.CmdLineParser;
import org.eclipse.jgit.pgm.opt.SubcommandHandler;
import org.eclipse.jgit.util.CachedAuthenticator;
-import org.eclipse.jgit.util.SystemReader;
import org.kohsuke.args4j.Argument;
import org.kohsuke.args4j.CmdLineException;
import org.kohsuke.args4j.ExampleMode;
@@ -168,51 +163,17 @@ public class Main {
final TextBuiltin cmd = subcommand;
if (cmd.requiresRepository()) {
- if (gitdir == null) {
- String gitDirEnv = SystemReader.getInstance().getenv(Constants.GIT_DIR_KEY);
- if (gitDirEnv != null)
- gitdir = new File(gitDirEnv);
- }
- if (gitdir == null)
- gitdir = findGitDir();
-
- File gitworktree;
- String gitWorkTreeEnv = SystemReader.getInstance().getenv(Constants.GIT_WORK_TREE_KEY);
- if (gitWorkTreeEnv != null)
- gitworktree = new File(gitWorkTreeEnv);
- else
- gitworktree = null;
-
- File indexfile;
- String indexFileEnv = SystemReader.getInstance().getenv(Constants.GIT_INDEX_KEY);
- if (indexFileEnv != null)
- indexfile = new File(indexFileEnv);
- else
- indexfile = null;
-
- File objectdir;
- String objectDirEnv = SystemReader.getInstance().getenv(Constants.GIT_OBJECT_DIRECTORY_KEY);
- if (objectDirEnv != null)
- objectdir = new File(objectDirEnv);
- else
- objectdir = null;
-
- File[] altobjectdirs;
- String altObjectDirEnv = SystemReader.getInstance().getenv(Constants.GIT_ALTERNATE_OBJECT_DIRECTORIES_KEY);
- if (altObjectDirEnv != null) {
- String[] parserdAltObjectDirEnv = altObjectDirEnv.split(File.pathSeparator);
- altobjectdirs = new File[parserdAltObjectDirEnv.length];
- for (int i = 0; i < parserdAltObjectDirEnv.length; i++)
- altobjectdirs[i] = new File(parserdAltObjectDirEnv[i]);
- } else
- altobjectdirs = null;
-
- if (gitdir == null || !gitdir.isDirectory()) {
+ RepositoryBuilder rb = new RepositoryBuilder() //
+ .setGitDir(gitdir) //
+ .readEnvironment() //
+ .findGitDir();
+ if (rb.getGitDir() == null) {
writer.println(CLIText.get().cantFindGitDirectory);
writer.flush();
System.exit(1);
}
- cmd.init(new Repository(gitdir, gitworktree, objectdir, altobjectdirs, indexfile), gitdir);
+
+ cmd.init(rb.build(), null);
} else {
cmd.init(null, gitdir);
}
@@ -224,27 +185,6 @@ public class Main {
}
}
- private static File findGitDir() {
- Set<String> ceilingDirectories = new HashSet<String>();
- String ceilingDirectoriesVar = SystemReader.getInstance().getenv(
- Constants.GIT_CEILING_DIRECTORIES_KEY);
- if (ceilingDirectoriesVar != null) {
- ceilingDirectories.addAll(Arrays.asList(ceilingDirectoriesVar
- .split(File.pathSeparator)));
- }
- File current = new File("").getAbsoluteFile();
- while (current != null) {
- final File gitDir = new File(current, Constants.DOT_GIT);
- if (gitDir.isDirectory())
- return gitDir;
- current = current.getParentFile();
- if (current != null
- && ceilingDirectories.contains(current.getPath()))
- break;
- }
- return null;
- }
-
private static boolean installConsole() {
try {
install("org.eclipse.jgit.console.ConsoleAuthenticator");
diff --git a/org.eclipse.jgit.pgm/src/org/eclipse/jgit/pgm/ReceivePack.java b/org.eclipse.jgit.pgm/src/org/eclipse/jgit/pgm/ReceivePack.java
index 09a9f2b580..7a27617220 100644
--- a/org.eclipse.jgit.pgm/src/org/eclipse/jgit/pgm/ReceivePack.java
+++ b/org.eclipse.jgit.pgm/src/org/eclipse/jgit/pgm/ReceivePack.java
@@ -47,9 +47,10 @@ package org.eclipse.jgit.pgm;
import java.io.File;
import java.text.MessageFormat;
+import org.eclipse.jgit.errors.RepositoryNotFoundException;
+import org.eclipse.jgit.lib.RepositoryCache.FileKey;
+import org.eclipse.jgit.util.FS;
import org.kohsuke.args4j.Argument;
-import org.eclipse.jgit.lib.Constants;
-import org.eclipse.jgit.lib.Repository;
@Command(common = false, usage = "usage_ServerSideBackendForJgitPush")
class ReceivePack extends TextBuiltin {
@@ -65,11 +66,14 @@ class ReceivePack extends TextBuiltin {
protected void run() throws Exception {
final org.eclipse.jgit.transport.ReceivePack rp;
- if (new File(dstGitdir, Constants.DOT_GIT).isDirectory())
- dstGitdir = new File(dstGitdir, Constants.DOT_GIT);
- db = new Repository(dstGitdir);
- if (!db.getObjectsDirectory().isDirectory())
- throw die(MessageFormat.format(CLIText.get().notAGitRepository, dstGitdir.getPath()));
+ try {
+ FileKey key = FileKey.lenient(dstGitdir, FS.DETECTED);
+ db = key.open(true /* must exist */);
+ } catch (RepositoryNotFoundException notFound) {
+ throw die(MessageFormat.format(CLIText.get().notAGitRepository,
+ dstGitdir.getPath()));
+ }
+
rp = new org.eclipse.jgit.transport.ReceivePack(db);
rp.receive(System.in, System.out, System.err);
}
diff --git a/org.eclipse.jgit.pgm/src/org/eclipse/jgit/pgm/Rm.java b/org.eclipse.jgit.pgm/src/org/eclipse/jgit/pgm/Rm.java
index 1b8711dc9d..9f577ff05e 100644
--- a/org.eclipse.jgit.pgm/src/org/eclipse/jgit/pgm/Rm.java
+++ b/org.eclipse.jgit.pgm/src/org/eclipse/jgit/pgm/Rm.java
@@ -67,9 +67,9 @@ class Rm extends TextBuiltin {
@Override
protected void run() throws Exception {
- root = db.getWorkDir();
+ root = db.getWorkTree();
- final DirCache dirc = DirCache.lock(db);
+ final DirCache dirc = db.lockDirCache();
final DirCacheBuilder edit = dirc.builder();
final TreeWalk walk = new TreeWalk(db);
diff --git a/org.eclipse.jgit.pgm/src/org/eclipse/jgit/pgm/Tag.java b/org.eclipse.jgit.pgm/src/org/eclipse/jgit/pgm/Tag.java
index 63d26eacae..c798950a2e 100644
--- a/org.eclipse.jgit.pgm/src/org/eclipse/jgit/pgm/Tag.java
+++ b/org.eclipse.jgit.pgm/src/org/eclipse/jgit/pgm/Tag.java
@@ -49,13 +49,12 @@ package org.eclipse.jgit.pgm;
import java.text.MessageFormat;
-import org.kohsuke.args4j.Argument;
-import org.kohsuke.args4j.Option;
-import org.eclipse.jgit.errors.MissingObjectException;
import org.eclipse.jgit.lib.Constants;
import org.eclipse.jgit.lib.ObjectId;
import org.eclipse.jgit.lib.ObjectLoader;
import org.eclipse.jgit.lib.PersonIdent;
+import org.kohsuke.args4j.Argument;
+import org.kohsuke.args4j.Option;
@Command(common = true, usage = "usage_CreateATag")
class Tag extends TextBuiltin {
@@ -86,9 +85,7 @@ class Tag extends TextBuiltin {
, tagName.substring(Constants.R_TAGS.length())));
}
- final ObjectLoader ldr = db.openObject(object);
- if (ldr == null)
- throw new MissingObjectException(object, "any");
+ final ObjectLoader ldr = db.open(object);
org.eclipse.jgit.lib.Tag tag = new org.eclipse.jgit.lib.Tag(db);
tag.setObjId(object);
diff --git a/org.eclipse.jgit.pgm/src/org/eclipse/jgit/pgm/UploadPack.java b/org.eclipse.jgit.pgm/src/org/eclipse/jgit/pgm/UploadPack.java
index 52d2488f70..d4e2bcec76 100644
--- a/org.eclipse.jgit.pgm/src/org/eclipse/jgit/pgm/UploadPack.java
+++ b/org.eclipse.jgit.pgm/src/org/eclipse/jgit/pgm/UploadPack.java
@@ -47,10 +47,11 @@ package org.eclipse.jgit.pgm;
import java.io.File;
import java.text.MessageFormat;
+import org.eclipse.jgit.errors.RepositoryNotFoundException;
+import org.eclipse.jgit.lib.RepositoryCache.FileKey;
+import org.eclipse.jgit.util.FS;
import org.kohsuke.args4j.Argument;
import org.kohsuke.args4j.Option;
-import org.eclipse.jgit.lib.Constants;
-import org.eclipse.jgit.lib.Repository;
@Command(common = false, usage = "usage_ServerSideBackendForJgitFetch")
class UploadPack extends TextBuiltin {
@@ -67,16 +68,19 @@ class UploadPack extends TextBuiltin {
@Override
protected void run() throws Exception {
- final org.eclipse.jgit.transport.UploadPack rp;
+ final org.eclipse.jgit.transport.UploadPack up;
- if (new File(srcGitdir, Constants.DOT_GIT).isDirectory())
- srcGitdir = new File(srcGitdir, Constants.DOT_GIT);
- db = new Repository(srcGitdir);
- if (!db.getObjectsDirectory().isDirectory())
- throw die(MessageFormat.format(CLIText.get().notAGitRepository, srcGitdir.getPath()));
- rp = new org.eclipse.jgit.transport.UploadPack(db);
+ try {
+ FileKey key = FileKey.lenient(srcGitdir, FS.DETECTED);
+ db = key.open(true /* must exist */);
+ } catch (RepositoryNotFoundException notFound) {
+ throw die(MessageFormat.format(CLIText.get().notAGitRepository,
+ srcGitdir.getPath()));
+ }
+
+ up = new org.eclipse.jgit.transport.UploadPack(db);
if (0 <= timeout)
- rp.setTimeout(timeout);
- rp.upload(System.in, System.out, System.err);
+ up.setTimeout(timeout);
+ up.upload(System.in, System.out, System.err);
}
}
diff --git a/org.eclipse.jgit.pgm/src/org/eclipse/jgit/pgm/debug/MakeCacheTree.java b/org.eclipse.jgit.pgm/src/org/eclipse/jgit/pgm/debug/MakeCacheTree.java
index d772ffe23f..709b45a17b 100644
--- a/org.eclipse.jgit.pgm/src/org/eclipse/jgit/pgm/debug/MakeCacheTree.java
+++ b/org.eclipse.jgit.pgm/src/org/eclipse/jgit/pgm/debug/MakeCacheTree.java
@@ -54,7 +54,7 @@ import org.eclipse.jgit.pgm.TextBuiltin;
class MakeCacheTree extends TextBuiltin {
@Override
protected void run() throws Exception {
- final DirCache cache = DirCache.read(db);
+ final DirCache cache = db.readDirCache();
final DirCacheTree tree = cache.getCacheTree(true);
show(tree);
}
diff --git a/org.eclipse.jgit.pgm/src/org/eclipse/jgit/pgm/debug/ReadDirCache.java b/org.eclipse.jgit.pgm/src/org/eclipse/jgit/pgm/debug/ReadDirCache.java
index 2a1079b313..0ca050880e 100644
--- a/org.eclipse.jgit.pgm/src/org/eclipse/jgit/pgm/debug/ReadDirCache.java
+++ b/org.eclipse.jgit.pgm/src/org/eclipse/jgit/pgm/debug/ReadDirCache.java
@@ -46,7 +46,6 @@ package org.eclipse.jgit.pgm.debug;
import java.text.MessageFormat;
-import org.eclipse.jgit.dircache.DirCache;
import org.eclipse.jgit.pgm.CLIText;
import org.eclipse.jgit.pgm.TextBuiltin;
@@ -56,7 +55,7 @@ class ReadDirCache extends TextBuiltin {
final int cnt = 100;
final long start = System.currentTimeMillis();
for (int i = 0; i < cnt; i++)
- DirCache.read(db);
+ db.readDirCache();
final long end = System.currentTimeMillis();
out.print(" ");
out.println(MessageFormat.format(CLIText.get().averageMSPerRead, (end - start) / cnt));
diff --git a/org.eclipse.jgit.pgm/src/org/eclipse/jgit/pgm/debug/RebuildCommitGraph.java b/org.eclipse.jgit.pgm/src/org/eclipse/jgit/pgm/debug/RebuildCommitGraph.java
index 1681dbc96e..5b75c1b5c5 100644
--- a/org.eclipse.jgit.pgm/src/org/eclipse/jgit/pgm/debug/RebuildCommitGraph.java
+++ b/org.eclipse.jgit.pgm/src/org/eclipse/jgit/pgm/debug/RebuildCommitGraph.java
@@ -62,7 +62,6 @@ import org.eclipse.jgit.errors.MissingObjectException;
import org.eclipse.jgit.errors.ObjectWritingException;
import org.eclipse.jgit.lib.Commit;
import org.eclipse.jgit.lib.Constants;
-import org.eclipse.jgit.lib.LockFile;
import org.eclipse.jgit.lib.ObjectId;
import org.eclipse.jgit.lib.ObjectIdRef;
import org.eclipse.jgit.lib.ObjectWriter;
@@ -76,6 +75,7 @@ import org.eclipse.jgit.lib.Tree;
import org.eclipse.jgit.pgm.CLIText;
import org.eclipse.jgit.pgm.TextBuiltin;
import org.eclipse.jgit.revwalk.RevWalk;
+import org.eclipse.jgit.storage.file.LockFile;
/**
* Recreates a repository from another one's commit graph.
@@ -297,6 +297,7 @@ class RebuildCommitGraph extends TextBuiltin {
name, id));
}
} finally {
+ rw.release();
br.close();
}
return refs;
diff --git a/org.eclipse.jgit.pgm/src/org/eclipse/jgit/pgm/debug/ShowCacheTree.java b/org.eclipse.jgit.pgm/src/org/eclipse/jgit/pgm/debug/ShowCacheTree.java
index 09796edb30..c49aefbf2f 100644
--- a/org.eclipse.jgit.pgm/src/org/eclipse/jgit/pgm/debug/ShowCacheTree.java
+++ b/org.eclipse.jgit.pgm/src/org/eclipse/jgit/pgm/debug/ShowCacheTree.java
@@ -54,7 +54,7 @@ import org.eclipse.jgit.pgm.TextBuiltin;
class ShowCacheTree extends TextBuiltin {
@Override
protected void run() throws Exception {
- final DirCache cache = DirCache.read(db);
+ final DirCache cache = db.readDirCache();
final DirCacheTree tree = cache.getCacheTree(false);
if (tree == null)
throw die(CLIText.get().noTREESectionInIndex);
diff --git a/org.eclipse.jgit.pgm/src/org/eclipse/jgit/pgm/debug/ShowDirCache.java b/org.eclipse.jgit.pgm/src/org/eclipse/jgit/pgm/debug/ShowDirCache.java
index 854596c97b..a94d37ff87 100644
--- a/org.eclipse.jgit.pgm/src/org/eclipse/jgit/pgm/debug/ShowDirCache.java
+++ b/org.eclipse.jgit.pgm/src/org/eclipse/jgit/pgm/debug/ShowDirCache.java
@@ -59,7 +59,7 @@ class ShowDirCache extends TextBuiltin {
final SimpleDateFormat fmt;
fmt = new SimpleDateFormat("yyyyMMdd,HHmmss.SSS");
- final DirCache cache = DirCache.read(db);
+ final DirCache cache = db.readDirCache();
for (int i = 0; i < cache.getEntryCount(); i++) {
final DirCacheEntry ent = cache.getEntry(i);
final FileMode mode = FileMode.fromBits(ent.getRawMode());
diff --git a/org.eclipse.jgit.pgm/src/org/eclipse/jgit/pgm/debug/ShowPackDelta.java b/org.eclipse.jgit.pgm/src/org/eclipse/jgit/pgm/debug/ShowPackDelta.java
new file mode 100644
index 0000000000..1718ef30f2
--- /dev/null
+++ b/org.eclipse.jgit.pgm/src/org/eclipse/jgit/pgm/debug/ShowPackDelta.java
@@ -0,0 +1,127 @@
+/*
+ * Copyright (C) 2010, Google Inc.
+ * and other copyright owners as documented in the project's IP log.
+ *
+ * This program and the accompanying materials are made available
+ * under the terms of the Eclipse Distribution License v1.0 which
+ * accompanies this distribution, is reproduced below, and is
+ * available at http://www.eclipse.org/org/documents/edl-v10.php
+ *
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or
+ * without modification, are permitted provided that the following
+ * conditions are met:
+ *
+ * - Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ *
+ * - Redistributions in binary form must reproduce the above
+ * copyright notice, this list of conditions and the following
+ * disclaimer in the documentation and/or other materials provided
+ * with the distribution.
+ *
+ * - Neither the name of the Eclipse Foundation, Inc. nor the
+ * names of its contributors may be used to endorse or promote
+ * products derived from this software without specific prior
+ * written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND
+ * CONTRIBUTORS "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES,
+ * INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
+ * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR
+ * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
+ * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT
+ * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
+ * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER
+ * CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT,
+ * STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
+ * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF
+ * ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ */
+
+package org.eclipse.jgit.pgm.debug;
+
+import java.io.ByteArrayInputStream;
+import java.io.ByteArrayOutputStream;
+import java.io.IOException;
+import java.util.zip.InflaterInputStream;
+
+import org.eclipse.jgit.errors.MissingObjectException;
+import org.eclipse.jgit.errors.StoredObjectRepresentationNotAvailableException;
+import org.eclipse.jgit.lib.NullProgressMonitor;
+import org.eclipse.jgit.lib.ObjectId;
+import org.eclipse.jgit.lib.ObjectReader;
+import org.eclipse.jgit.pgm.TextBuiltin;
+import org.eclipse.jgit.revwalk.RevObject;
+import org.eclipse.jgit.revwalk.RevWalk;
+import org.eclipse.jgit.storage.pack.BinaryDelta;
+import org.eclipse.jgit.storage.pack.ObjectReuseAsIs;
+import org.eclipse.jgit.storage.pack.ObjectToPack;
+import org.eclipse.jgit.storage.pack.PackOutputStream;
+import org.eclipse.jgit.storage.pack.PackWriter;
+import org.eclipse.jgit.storage.pack.StoredObjectRepresentation;
+import org.eclipse.jgit.util.TemporaryBuffer;
+import org.kohsuke.args4j.Argument;
+
+class ShowPackDelta extends TextBuiltin {
+ @Argument(index = 0)
+ private ObjectId objectId;
+
+ @Override
+ protected void run() throws Exception {
+ ObjectReader reader = db.newObjectReader();
+ RevObject obj = new RevWalk(reader).parseAny(objectId);
+ byte[] delta = getDelta(reader, obj);
+
+ // We're crossing our fingers that this will be a delta. Double
+ // check the size field in the header, it should match.
+ //
+ long size = reader.getObjectSize(obj, obj.getType());
+ try {
+ if (BinaryDelta.getResultSize(delta) != size)
+ throw die("Object " + obj.name() + " is not a delta");
+ } catch (ArrayIndexOutOfBoundsException bad) {
+ throw die("Object " + obj.name() + " is not a delta");
+ }
+
+ out.println(BinaryDelta.format(delta));
+ }
+
+ private byte[] getDelta(ObjectReader reader, RevObject obj)
+ throws IOException, MissingObjectException,
+ StoredObjectRepresentationNotAvailableException {
+ ObjectReuseAsIs asis = (ObjectReuseAsIs) reader;
+ ObjectToPack target = asis.newObjectToPack(obj);
+
+ PackWriter pw = new PackWriter(reader) {
+ @Override
+ public void select(ObjectToPack otp, StoredObjectRepresentation next) {
+ otp.select(next);
+ }
+ };
+
+ ByteArrayOutputStream buf = new ByteArrayOutputStream();
+ asis.selectObjectRepresentation(pw, target);
+ asis.copyObjectAsIs(new PackOutputStream(NullProgressMonitor.INSTANCE,
+ buf, pw), target);
+
+ // At this point the object header has no delta information,
+ // because it was output as though it were a whole object.
+ // Skip over the header and inflate.
+ //
+ byte[] bufArray = buf.toByteArray();
+ int ptr = 0;
+ while ((bufArray[ptr] & 0x80) != 0)
+ ptr++;
+ ptr++;
+
+ TemporaryBuffer.Heap raw = new TemporaryBuffer.Heap(bufArray.length);
+ InflaterInputStream inf = new InflaterInputStream(
+ new ByteArrayInputStream(bufArray, ptr, bufArray.length));
+ raw.copy(inf);
+ inf.close();
+ return raw.toByteArray();
+ }
+}
diff --git a/org.eclipse.jgit.pgm/src/org/eclipse/jgit/pgm/debug/WriteDirCache.java b/org.eclipse.jgit.pgm/src/org/eclipse/jgit/pgm/debug/WriteDirCache.java
index cee5966a03..142dbeecc8 100644
--- a/org.eclipse.jgit.pgm/src/org/eclipse/jgit/pgm/debug/WriteDirCache.java
+++ b/org.eclipse.jgit.pgm/src/org/eclipse/jgit/pgm/debug/WriteDirCache.java
@@ -51,7 +51,7 @@ import org.eclipse.jgit.pgm.TextBuiltin;
class WriteDirCache extends TextBuiltin {
@Override
protected void run() throws Exception {
- final DirCache cache = DirCache.read(db);
+ final DirCache cache = db.readDirCache();
if (!cache.lock())
throw die(CLIText.get().failedToLockIndex);
cache.read();
diff --git a/org.eclipse.jgit.pgm/src/org/eclipse/jgit/pgm/eclipse/Iplog.java b/org.eclipse.jgit.pgm/src/org/eclipse/jgit/pgm/eclipse/Iplog.java
index e13bb1f136..a99e0abca2 100644
--- a/org.eclipse.jgit.pgm/src/org/eclipse/jgit/pgm/eclipse/Iplog.java
+++ b/org.eclipse.jgit.pgm/src/org/eclipse/jgit/pgm/eclipse/Iplog.java
@@ -51,7 +51,6 @@ import java.text.MessageFormat;
import org.eclipse.jgit.iplog.IpLogGenerator;
import org.eclipse.jgit.iplog.SimpleCookieManager;
import org.eclipse.jgit.lib.Constants;
-import org.eclipse.jgit.lib.LockFile;
import org.eclipse.jgit.lib.ObjectId;
import org.eclipse.jgit.pgm.CLIText;
import org.eclipse.jgit.pgm.Command;
@@ -59,6 +58,7 @@ import org.eclipse.jgit.pgm.TextBuiltin;
import org.eclipse.jgit.revwalk.RevObject;
import org.eclipse.jgit.revwalk.RevTag;
import org.eclipse.jgit.revwalk.RevWalk;
+import org.eclipse.jgit.storage.file.LockFile;
import org.kohsuke.args4j.Argument;
import org.kohsuke.args4j.Option;
diff --git a/org.eclipse.jgit.pgm/src/org/eclipse/jgit/pgm/eclipse/Ipzilla.java b/org.eclipse.jgit.pgm/src/org/eclipse/jgit/pgm/eclipse/Ipzilla.java
index 4f0e338e8d..b563f07910 100644
--- a/org.eclipse.jgit.pgm/src/org/eclipse/jgit/pgm/eclipse/Ipzilla.java
+++ b/org.eclipse.jgit.pgm/src/org/eclipse/jgit/pgm/eclipse/Ipzilla.java
@@ -91,7 +91,7 @@ class Ipzilla extends TextBuiltin {
}
if (output == null)
- output = new File(db.getWorkDir(), IpLogMeta.IPLOG_CONFIG_FILE);
+ output = new File(db.getWorkTree(), IpLogMeta.IPLOG_CONFIG_FILE);
IpLogMeta meta = new IpLogMeta();
meta.syncCQs(output, ipzilla, username, password);
diff --git a/org.eclipse.jgit.pgm/src/org/eclipse/jgit/pgm/opt/AbstractTreeIteratorHandler.java b/org.eclipse.jgit.pgm/src/org/eclipse/jgit/pgm/opt/AbstractTreeIteratorHandler.java
index 2043ac2090..01981600dd 100644
--- a/org.eclipse.jgit.pgm/src/org/eclipse/jgit/pgm/opt/AbstractTreeIteratorHandler.java
+++ b/org.eclipse.jgit.pgm/src/org/eclipse/jgit/pgm/opt/AbstractTreeIteratorHandler.java
@@ -59,7 +59,7 @@ import org.eclipse.jgit.dircache.DirCacheIterator;
import org.eclipse.jgit.errors.IncorrectObjectTypeException;
import org.eclipse.jgit.errors.MissingObjectException;
import org.eclipse.jgit.lib.ObjectId;
-import org.eclipse.jgit.lib.WindowCursor;
+import org.eclipse.jgit.lib.ObjectReader;
import org.eclipse.jgit.pgm.CLIText;
import org.eclipse.jgit.treewalk.AbstractTreeIterator;
import org.eclipse.jgit.treewalk.CanonicalTreeParser;
@@ -121,9 +121,9 @@ public class AbstractTreeIteratorHandler extends
throw new CmdLineException(MessageFormat.format(CLIText.get().notATree, name));
final CanonicalTreeParser p = new CanonicalTreeParser();
- final WindowCursor curs = new WindowCursor();
+ final ObjectReader curs = clp.getRepository().newObjectReader();
try {
- p.reset(clp.getRepository(), clp.getRevWalk().parseTree(id), curs);
+ p.reset(curs, clp.getRevWalk().parseTree(id));
} catch (MissingObjectException e) {
throw new CmdLineException(MessageFormat.format(CLIText.get().notATree, name));
} catch (IncorrectObjectTypeException e) {
diff --git a/org.eclipse.jgit.test/META-INF/MANIFEST.MF b/org.eclipse.jgit.test/META-INF/MANIFEST.MF
index 3aaa8a45e8..d6128afc97 100644
--- a/org.eclipse.jgit.test/META-INF/MANIFEST.MF
+++ b/org.eclipse.jgit.test/META-INF/MANIFEST.MF
@@ -29,6 +29,8 @@ Import-Package: junit.framework;version="[3.8.2,4.0.0)",
org.eclipse.jgit.revplot;version="[0.9.0,0.10.0)",
org.eclipse.jgit.revwalk;version="[0.9.0,0.10.0)",
org.eclipse.jgit.revwalk.filter;version="[0.9.0,0.10.0)",
+ org.eclipse.jgit.storage.file;version="[0.9.0,0.10.0)",
+ org.eclipse.jgit.storage.pack;version="[0.9.0,0.10.0)",
org.eclipse.jgit.transport;version="[0.9.0,0.10.0)",
org.eclipse.jgit.treewalk;version="[0.9.0,0.10.0)",
org.eclipse.jgit.treewalk.filter;version="[0.9.0,0.10.0)",
diff --git a/org.eclipse.jgit.test/exttst/org/eclipse/jgit/lib/T0007_GitIndexTest.java b/org.eclipse.jgit.test/exttst/org/eclipse/jgit/lib/T0007_GitIndexTest.java
index dd3b51efc7..c5591b9bfd 100644
--- a/org.eclipse.jgit.test/exttst/org/eclipse/jgit/lib/T0007_GitIndexTest.java
+++ b/org.eclipse.jgit.test/exttst/org/eclipse/jgit/lib/T0007_GitIndexTest.java
@@ -116,7 +116,7 @@ public class T0007_GitIndexTest extends LocalDiskRepositoryTestCase {
protected void setUp() throws Exception {
super.setUp();
db = createWorkRepository();
- trash = db.getWorkDir();
+ trash = db.getWorkTree();
}
public void testCreateEmptyIndex() throws Exception {
diff --git a/org.eclipse.jgit.test/tst/org/eclipse/jgit/api/AddCommandTest.java b/org.eclipse.jgit.test/tst/org/eclipse/jgit/api/AddCommandTest.java
index ab011807cb..9e195b4974 100644
--- a/org.eclipse.jgit.test/tst/org/eclipse/jgit/api/AddCommandTest.java
+++ b/org.eclipse.jgit.test/tst/org/eclipse/jgit/api/AddCommandTest.java
@@ -78,7 +78,7 @@ public class AddCommandTest extends RepositoryTestCase {
}
public void testAddExistingSingleFile() throws IOException, NoFilepatternException {
- File file = new File(db.getWorkDir(), "a.txt");
+ File file = new File(db.getWorkTree(), "a.txt");
file.createNewFile();
PrintWriter writer = new PrintWriter(file);
writer.print("content");
@@ -98,8 +98,8 @@ public class AddCommandTest extends RepositoryTestCase {
}
public void testAddExistingSingleFileInSubDir() throws IOException, NoFilepatternException {
- new File(db.getWorkDir(), "sub").mkdir();
- File file = new File(db.getWorkDir(), "sub/a.txt");
+ new File(db.getWorkTree(), "sub").mkdir();
+ File file = new File(db.getWorkTree(), "sub/a.txt");
file.createNewFile();
PrintWriter writer = new PrintWriter(file);
writer.print("content");
@@ -119,7 +119,7 @@ public class AddCommandTest extends RepositoryTestCase {
}
public void testAddExistingSingleFileTwice() throws IOException, NoFilepatternException {
- File file = new File(db.getWorkDir(), "a.txt");
+ File file = new File(db.getWorkTree(), "a.txt");
file.createNewFile();
PrintWriter writer = new PrintWriter(file);
writer.print("content");
@@ -143,7 +143,7 @@ public class AddCommandTest extends RepositoryTestCase {
}
public void testAddExistingSingleFileTwiceWithCommit() throws Exception {
- File file = new File(db.getWorkDir(), "a.txt");
+ File file = new File(db.getWorkTree(), "a.txt");
file.createNewFile();
PrintWriter writer = new PrintWriter(file);
writer.print("content");
@@ -169,7 +169,7 @@ public class AddCommandTest extends RepositoryTestCase {
}
public void testAddRemovedFile() throws Exception {
- File file = new File(db.getWorkDir(), "a.txt");
+ File file = new File(db.getWorkTree(), "a.txt");
file.createNewFile();
PrintWriter writer = new PrintWriter(file);
writer.print("content");
@@ -191,7 +191,7 @@ public class AddCommandTest extends RepositoryTestCase {
}
public void testAddRemovedCommittedFile() throws Exception {
- File file = new File(db.getWorkDir(), "a.txt");
+ File file = new File(db.getWorkTree(), "a.txt");
file.createNewFile();
PrintWriter writer = new PrintWriter(file);
writer.print("content");
@@ -217,20 +217,20 @@ public class AddCommandTest extends RepositoryTestCase {
public void testAddWithConflicts() throws Exception {
// prepare conflict
- File file = new File(db.getWorkDir(), "a.txt");
+ File file = new File(db.getWorkTree(), "a.txt");
file.createNewFile();
PrintWriter writer = new PrintWriter(file);
writer.print("content");
writer.close();
- File file2 = new File(db.getWorkDir(), "b.txt");
+ File file2 = new File(db.getWorkTree(), "b.txt");
file2.createNewFile();
writer = new PrintWriter(file2);
writer.print("content b");
writer.close();
ObjectWriter ow = new ObjectWriter(db);
- DirCache dc = DirCache.lock(db);
+ DirCache dc = db.lockDirCache();
DirCacheBuilder builder = dc.builder();
addEntryToBuilder("b.txt", file2, ow, builder, 0);
@@ -264,13 +264,13 @@ public class AddCommandTest extends RepositoryTestCase {
}
public void testAddTwoFiles() throws Exception {
- File file = new File(db.getWorkDir(), "a.txt");
+ File file = new File(db.getWorkTree(), "a.txt");
file.createNewFile();
PrintWriter writer = new PrintWriter(file);
writer.print("content");
writer.close();
- File file2 = new File(db.getWorkDir(), "b.txt");
+ File file2 = new File(db.getWorkTree(), "b.txt");
file2.createNewFile();
writer = new PrintWriter(file2);
writer.print("content b");
@@ -287,14 +287,14 @@ public class AddCommandTest extends RepositoryTestCase {
}
public void testAddFolder() throws Exception {
- new File(db.getWorkDir(), "sub").mkdir();
- File file = new File(db.getWorkDir(), "sub/a.txt");
+ new File(db.getWorkTree(), "sub").mkdir();
+ File file = new File(db.getWorkTree(), "sub/a.txt");
file.createNewFile();
PrintWriter writer = new PrintWriter(file);
writer.print("content");
writer.close();
- File file2 = new File(db.getWorkDir(), "sub/b.txt");
+ File file2 = new File(db.getWorkTree(), "sub/b.txt");
file2.createNewFile();
writer = new PrintWriter(file2);
writer.print("content b");
@@ -311,20 +311,20 @@ public class AddCommandTest extends RepositoryTestCase {
}
public void testAddIgnoredFile() throws Exception {
- new File(db.getWorkDir(), "sub").mkdir();
- File file = new File(db.getWorkDir(), "sub/a.txt");
+ new File(db.getWorkTree(), "sub").mkdir();
+ File file = new File(db.getWorkTree(), "sub/a.txt");
file.createNewFile();
PrintWriter writer = new PrintWriter(file);
writer.print("content");
writer.close();
- File ignoreFile = new File(db.getWorkDir(), ".gitignore");
+ File ignoreFile = new File(db.getWorkTree(), ".gitignore");
ignoreFile.createNewFile();
writer = new PrintWriter(ignoreFile);
writer.print("sub/b.txt");
writer.close();
- File file2 = new File(db.getWorkDir(), "sub/b.txt");
+ File file2 = new File(db.getWorkTree(), "sub/b.txt");
file2.createNewFile();
writer = new PrintWriter(file2);
writer.print("content b");
@@ -339,14 +339,14 @@ public class AddCommandTest extends RepositoryTestCase {
}
public void testAddWholeRepo() throws Exception {
- new File(db.getWorkDir(), "sub").mkdir();
- File file = new File(db.getWorkDir(), "sub/a.txt");
+ new File(db.getWorkTree(), "sub").mkdir();
+ File file = new File(db.getWorkTree(), "sub/a.txt");
file.createNewFile();
PrintWriter writer = new PrintWriter(file);
writer.print("content");
writer.close();
- File file2 = new File(db.getWorkDir(), "sub/b.txt");
+ File file2 = new File(db.getWorkTree(), "sub/b.txt");
file2.createNewFile();
writer = new PrintWriter(file2);
writer.print("content b");
diff --git a/org.eclipse.jgit.test/tst/org/eclipse/jgit/api/MergeCommandTest.java b/org.eclipse.jgit.test/tst/org/eclipse/jgit/api/MergeCommandTest.java
index c965c67664..773d2f0556 100644
--- a/org.eclipse.jgit.test/tst/org/eclipse/jgit/api/MergeCommandTest.java
+++ b/org.eclipse.jgit.test/tst/org/eclipse/jgit/api/MergeCommandTest.java
@@ -100,20 +100,20 @@ public class MergeCommandTest extends RepositoryTestCase {
addNewFileToIndex("file1");
RevCommit first = git.commit().setMessage("initial commit").call();
- assertTrue(new File(db.getWorkDir(), "file1").exists());
+ assertTrue(new File(db.getWorkTree(), "file1").exists());
createBranch(first, "refs/heads/branch1");
addNewFileToIndex("file2");
RevCommit second = git.commit().setMessage("second commit").call();
- assertTrue(new File(db.getWorkDir(), "file2").exists());
+ assertTrue(new File(db.getWorkTree(), "file2").exists());
checkoutBranch("refs/heads/branch1");
- assertFalse(new File(db.getWorkDir(), "file2").exists());
+ assertFalse(new File(db.getWorkTree(), "file2").exists());
MergeResult result = git.merge().include(db.getRef(Constants.MASTER)).call();
- assertTrue(new File(db.getWorkDir(), "file1").exists());
- assertTrue(new File(db.getWorkDir(), "file2").exists());
+ assertTrue(new File(db.getWorkTree(), "file1").exists());
+ assertTrue(new File(db.getWorkTree(), "file2").exists());
assertEquals(MergeResult.MergeStatus.FAST_FORWARD, result.getMergeStatus());
assertEquals(second, result.getNewHead());
}
@@ -132,8 +132,8 @@ public class MergeCommandTest extends RepositoryTestCase {
git.commit().setMessage("third commit").call();
checkoutBranch("refs/heads/branch1");
- assertFalse(new File(db.getWorkDir(), "file2").exists());
- assertFalse(new File(db.getWorkDir(), "file3").exists());
+ assertFalse(new File(db.getWorkTree(), "file2").exists());
+ assertFalse(new File(db.getWorkTree(), "file3").exists());
MergeCommand merge = git.merge();
merge.include(second.getId());
@@ -152,7 +152,7 @@ public class MergeCommandTest extends RepositoryTestCase {
}
private void checkoutBranch(String branchName) throws Exception {
- File workDir = db.getWorkDir();
+ File workDir = db.getWorkTree();
if (workDir != null) {
WorkDirCheckout workDirCheckout = new WorkDirCheckout(db,
workDir, db.mapCommit(Constants.HEAD).getTree(),
@@ -176,7 +176,7 @@ public class MergeCommandTest extends RepositoryTestCase {
File writeTrashFile = writeTrashFile(filename, filename);
GitIndex index = db.getIndex();
- Entry entry = index.add(db.getWorkDir(), writeTrashFile);
+ Entry entry = index.add(db.getWorkTree(), writeTrashFile);
entry.update(writeTrashFile);
index.write();
}
diff --git a/org.eclipse.jgit.test/tst/org/eclipse/jgit/diff/SimilarityIndexTest.java b/org.eclipse.jgit.test/tst/org/eclipse/jgit/diff/SimilarityIndexTest.java
index d6915eb872..7e42e53586 100644
--- a/org.eclipse.jgit.test/tst/org/eclipse/jgit/diff/SimilarityIndexTest.java
+++ b/org.eclipse.jgit.test/tst/org/eclipse/jgit/diff/SimilarityIndexTest.java
@@ -43,12 +43,15 @@
package org.eclipse.jgit.diff;
+import java.io.ByteArrayInputStream;
+import java.io.IOException;
+
import junit.framework.TestCase;
import org.eclipse.jgit.lib.Constants;
public class SimilarityIndexTest extends TestCase {
- public void testIndexing() {
+ public void testIndexingSmallObject() {
SimilarityIndex si = hash("" //
+ "A\n" //
+ "B\n" //
@@ -67,6 +70,17 @@ public class SimilarityIndexTest extends TestCase {
assertEquals(2, si.count(si.findIndex(key_D)));
}
+ public void testIndexingLargeObject() throws IOException {
+ byte[] in = ("" //
+ + "A\n" //
+ + "B\n" //
+ + "B\n" //
+ + "B\n").getBytes("UTF-8");
+ SimilarityIndex si = new SimilarityIndex();
+ si.hash(new ByteArrayInputStream(in), in.length);
+ assertEquals(2, si.size());
+ }
+
public void testCommonScore_SameFiles() {
String text = "" //
+ "A\n" //
diff --git a/org.eclipse.jgit.test/tst/org/eclipse/jgit/dircache/DirCacheBasicTest.java b/org.eclipse.jgit.test/tst/org/eclipse/jgit/dircache/DirCacheBasicTest.java
index f4692b168d..c3ac952a11 100644
--- a/org.eclipse.jgit.test/tst/org/eclipse/jgit/dircache/DirCacheBasicTest.java
+++ b/org.eclipse.jgit.test/tst/org/eclipse/jgit/dircache/DirCacheBasicTest.java
@@ -54,7 +54,7 @@ public class DirCacheBasicTest extends RepositoryTestCase {
final File idx = new File(db.getDirectory(), "index");
assertFalse(idx.exists());
- final DirCache dc = DirCache.read(db);
+ final DirCache dc = db.readDirCache();
assertNotNull(dc);
assertEquals(0, dc.getEntryCount());
}
@@ -74,7 +74,7 @@ public class DirCacheBasicTest extends RepositoryTestCase {
assertFalse(idx.exists());
assertFalse(lck.exists());
- final DirCache dc = DirCache.lock(db);
+ final DirCache dc = db.lockDirCache();
assertNotNull(dc);
assertFalse(idx.exists());
assertTrue(lck.exists());
@@ -108,7 +108,7 @@ public class DirCacheBasicTest extends RepositoryTestCase {
assertFalse(idx.exists());
assertFalse(lck.exists());
- final DirCache dc = DirCache.lock(db);
+ final DirCache dc = db.lockDirCache();
assertEquals(0, lck.length());
dc.write();
assertEquals(12 + 20, lck.length());
@@ -124,7 +124,7 @@ public class DirCacheBasicTest extends RepositoryTestCase {
assertFalse(idx.exists());
assertFalse(lck.exists());
- final DirCache dc = DirCache.lock(db);
+ final DirCache dc = db.lockDirCache();
assertEquals(0, lck.length());
dc.write();
assertEquals(12 + 20, lck.length());
@@ -141,13 +141,13 @@ public class DirCacheBasicTest extends RepositoryTestCase {
assertFalse(idx.exists());
assertFalse(lck.exists());
{
- final DirCache dc = DirCache.lock(db);
+ final DirCache dc = db.lockDirCache();
dc.write();
assertTrue(dc.commit());
assertTrue(idx.exists());
}
{
- final DirCache dc = DirCache.read(db);
+ final DirCache dc = db.readDirCache();
assertEquals(0, dc.getEntryCount());
}
}
@@ -158,13 +158,13 @@ public class DirCacheBasicTest extends RepositoryTestCase {
assertFalse(idx.exists());
assertFalse(lck.exists());
{
- final DirCache dc = DirCache.lock(db);
+ final DirCache dc = db.lockDirCache();
dc.write();
assertTrue(dc.commit());
assertTrue(idx.exists());
}
{
- final DirCache dc = DirCache.lock(db);
+ final DirCache dc = db.lockDirCache();
assertEquals(0, dc.getEntryCount());
assertTrue(idx.exists());
assertTrue(lck.exists());
@@ -173,7 +173,7 @@ public class DirCacheBasicTest extends RepositoryTestCase {
}
public void testBuildThenClear() throws Exception {
- final DirCache dc = DirCache.read(db);
+ final DirCache dc = db.readDirCache();
final String[] paths = { "a.", "a.b", "a/b", "a0b" };
final DirCacheEntry[] ents = new DirCacheEntry[paths.length];
@@ -195,7 +195,7 @@ public class DirCacheBasicTest extends RepositoryTestCase {
}
public void testDetectUnmergedPaths() throws Exception {
- final DirCache dc = DirCache.read(db);
+ final DirCache dc = db.readDirCache();
final DirCacheEntry[] ents = new DirCacheEntry[3];
ents[0] = new DirCacheEntry("a", 1);
diff --git a/org.eclipse.jgit.test/tst/org/eclipse/jgit/dircache/DirCacheBuilderIteratorTest.java b/org.eclipse.jgit.test/tst/org/eclipse/jgit/dircache/DirCacheBuilderIteratorTest.java
index 03bb7f5e83..a09f8e86c4 100644
--- a/org.eclipse.jgit.test/tst/org/eclipse/jgit/dircache/DirCacheBuilderIteratorTest.java
+++ b/org.eclipse.jgit.test/tst/org/eclipse/jgit/dircache/DirCacheBuilderIteratorTest.java
@@ -52,7 +52,7 @@ import org.eclipse.jgit.treewalk.filter.PathFilterGroup;
public class DirCacheBuilderIteratorTest extends RepositoryTestCase {
public void testPathFilterGroup_DoesNotSkipTail() throws Exception {
- final DirCache dc = DirCache.read(db);
+ final DirCache dc = db.readDirCache();
final FileMode mode = FileMode.REGULAR_FILE;
final String[] paths = { "a.", "a/b", "a/c", "a/d", "a0b" };
diff --git a/org.eclipse.jgit.test/tst/org/eclipse/jgit/dircache/DirCacheBuilderTest.java b/org.eclipse.jgit.test/tst/org/eclipse/jgit/dircache/DirCacheBuilderTest.java
index e919e41f4d..81ffab9148 100644
--- a/org.eclipse.jgit.test/tst/org/eclipse/jgit/dircache/DirCacheBuilderTest.java
+++ b/org.eclipse.jgit.test/tst/org/eclipse/jgit/dircache/DirCacheBuilderTest.java
@@ -52,7 +52,7 @@ import org.eclipse.jgit.lib.RepositoryTestCase;
public class DirCacheBuilderTest extends RepositoryTestCase {
public void testBuildEmpty() throws Exception {
{
- final DirCache dc = DirCache.lock(db);
+ final DirCache dc = db.lockDirCache();
final DirCacheBuilder b = dc.builder();
assertNotNull(b);
b.finish();
@@ -60,7 +60,7 @@ public class DirCacheBuilderTest extends RepositoryTestCase {
assertTrue(dc.commit());
}
{
- final DirCache dc = DirCache.read(db);
+ final DirCache dc = db.readDirCache();
assertEquals(0, dc.getEntryCount());
}
}
@@ -86,7 +86,7 @@ public class DirCacheBuilderTest extends RepositoryTestCase {
final int length = 1342;
final DirCacheEntry entOrig;
{
- final DirCache dc = DirCache.lock(db);
+ final DirCache dc = db.lockDirCache();
final DirCacheBuilder b = dc.builder();
assertNotNull(b);
@@ -113,7 +113,7 @@ public class DirCacheBuilderTest extends RepositoryTestCase {
assertTrue(dc.commit());
}
{
- final DirCache dc = DirCache.read(db);
+ final DirCache dc = db.readDirCache();
assertEquals(1, dc.getEntryCount());
final DirCacheEntry entRead = dc.getEntry(0);
@@ -135,7 +135,7 @@ public class DirCacheBuilderTest extends RepositoryTestCase {
final int length = 1342;
final DirCacheEntry entOrig;
{
- final DirCache dc = DirCache.lock(db);
+ final DirCache dc = db.lockDirCache();
final DirCacheBuilder b = dc.builder();
assertNotNull(b);
@@ -160,7 +160,7 @@ public class DirCacheBuilderTest extends RepositoryTestCase {
assertFalse(new File(db.getDirectory(), "index.lock").exists());
}
{
- final DirCache dc = DirCache.read(db);
+ final DirCache dc = db.readDirCache();
assertEquals(1, dc.getEntryCount());
final DirCacheEntry entRead = dc.getEntry(0);
@@ -177,7 +177,7 @@ public class DirCacheBuilderTest extends RepositoryTestCase {
public void testFindSingleFile() throws Exception {
final String path = "a-file-path";
- final DirCache dc = DirCache.read(db);
+ final DirCache dc = db.readDirCache();
final DirCacheBuilder b = dc.builder();
assertNotNull(b);
@@ -202,7 +202,7 @@ public class DirCacheBuilderTest extends RepositoryTestCase {
}
public void testAdd_InGitSortOrder() throws Exception {
- final DirCache dc = DirCache.read(db);
+ final DirCache dc = db.readDirCache();
final String[] paths = { "a.", "a.b", "a/b", "a0b" };
final DirCacheEntry[] ents = new DirCacheEntry[paths.length];
@@ -226,7 +226,7 @@ public class DirCacheBuilderTest extends RepositoryTestCase {
}
public void testAdd_ReverseGitSortOrder() throws Exception {
- final DirCache dc = DirCache.read(db);
+ final DirCache dc = db.readDirCache();
final String[] paths = { "a.", "a.b", "a/b", "a0b" };
final DirCacheEntry[] ents = new DirCacheEntry[paths.length];
@@ -250,7 +250,7 @@ public class DirCacheBuilderTest extends RepositoryTestCase {
}
public void testBuilderClear() throws Exception {
- final DirCache dc = DirCache.read(db);
+ final DirCache dc = db.readDirCache();
final String[] paths = { "a.", "a.b", "a/b", "a0b" };
final DirCacheEntry[] ents = new DirCacheEntry[paths.length];
diff --git a/org.eclipse.jgit.test/tst/org/eclipse/jgit/dircache/DirCacheFindTest.java b/org.eclipse.jgit.test/tst/org/eclipse/jgit/dircache/DirCacheFindTest.java
index d5a632c48c..5533fe358a 100644
--- a/org.eclipse.jgit.test/tst/org/eclipse/jgit/dircache/DirCacheFindTest.java
+++ b/org.eclipse.jgit.test/tst/org/eclipse/jgit/dircache/DirCacheFindTest.java
@@ -48,7 +48,7 @@ import org.eclipse.jgit.lib.RepositoryTestCase;
public class DirCacheFindTest extends RepositoryTestCase {
public void testEntriesWithin() throws Exception {
- final DirCache dc = DirCache.read(db);
+ final DirCache dc = db.readDirCache();
final String[] paths = { "a.", "a/b", "a/c", "a/d", "a0b" };
final DirCacheEntry[] ents = new DirCacheEntry[paths.length];
diff --git a/org.eclipse.jgit.test/tst/org/eclipse/jgit/dircache/DirCacheIteratorTest.java b/org.eclipse.jgit.test/tst/org/eclipse/jgit/dircache/DirCacheIteratorTest.java
index efea117388..24e3c34ddf 100644
--- a/org.eclipse.jgit.test/tst/org/eclipse/jgit/dircache/DirCacheIteratorTest.java
+++ b/org.eclipse.jgit.test/tst/org/eclipse/jgit/dircache/DirCacheIteratorTest.java
@@ -52,7 +52,7 @@ import org.eclipse.jgit.treewalk.filter.PathFilterGroup;
public class DirCacheIteratorTest extends RepositoryTestCase {
public void testEmptyTree_NoTreeWalk() throws Exception {
- final DirCache dc = DirCache.read(db);
+ final DirCache dc = db.readDirCache();
assertEquals(0, dc.getEntryCount());
final DirCacheIterator i = new DirCacheIterator(dc);
@@ -60,7 +60,7 @@ public class DirCacheIteratorTest extends RepositoryTestCase {
}
public void testEmptyTree_WithTreeWalk() throws Exception {
- final DirCache dc = DirCache.read(db);
+ final DirCache dc = db.readDirCache();
assertEquals(0, dc.getEntryCount());
final TreeWalk tw = new TreeWalk(db);
@@ -70,7 +70,7 @@ public class DirCacheIteratorTest extends RepositoryTestCase {
}
public void testNoSubtree_NoTreeWalk() throws Exception {
- final DirCache dc = DirCache.read(db);
+ final DirCache dc = db.readDirCache();
final String[] paths = { "a.", "a0b" };
final DirCacheEntry[] ents = new DirCacheEntry[paths.length];
@@ -95,7 +95,7 @@ public class DirCacheIteratorTest extends RepositoryTestCase {
}
public void testNoSubtree_WithTreeWalk() throws Exception {
- final DirCache dc = DirCache.read(db);
+ final DirCache dc = db.readDirCache();
final String[] paths = { "a.", "a0b" };
final FileMode[] modes = { FileMode.EXECUTABLE_FILE, FileMode.GITLINK };
@@ -128,7 +128,7 @@ public class DirCacheIteratorTest extends RepositoryTestCase {
}
public void testSingleSubtree_NoRecursion() throws Exception {
- final DirCache dc = DirCache.read(db);
+ final DirCache dc = db.readDirCache();
final String[] paths = { "a.", "a/b", "a/c", "a/d", "a0b" };
final DirCacheEntry[] ents = new DirCacheEntry[paths.length];
@@ -172,7 +172,7 @@ public class DirCacheIteratorTest extends RepositoryTestCase {
}
public void testSingleSubtree_Recursive() throws Exception {
- final DirCache dc = DirCache.read(db);
+ final DirCache dc = db.readDirCache();
final FileMode mode = FileMode.REGULAR_FILE;
final String[] paths = { "a.", "a/b", "a/c", "a/d", "a0b" };
@@ -207,7 +207,7 @@ public class DirCacheIteratorTest extends RepositoryTestCase {
}
public void testTwoLevelSubtree_Recursive() throws Exception {
- final DirCache dc = DirCache.read(db);
+ final DirCache dc = db.readDirCache();
final FileMode mode = FileMode.REGULAR_FILE;
final String[] paths = { "a.", "a/b", "a/c/e", "a/c/f", "a/d", "a0b" };
@@ -241,7 +241,7 @@ public class DirCacheIteratorTest extends RepositoryTestCase {
}
public void testTwoLevelSubtree_FilterPath() throws Exception {
- final DirCache dc = DirCache.read(db);
+ final DirCache dc = db.readDirCache();
final FileMode mode = FileMode.REGULAR_FILE;
final String[] paths = { "a.", "a/b", "a/c/e", "a/c/f", "a/d", "a0b" };
diff --git a/org.eclipse.jgit.test/tst/org/eclipse/jgit/dircache/DirCacheLargePathTest.java b/org.eclipse.jgit.test/tst/org/eclipse/jgit/dircache/DirCacheLargePathTest.java
index 0926ab9899..a6d7e39eb5 100644
--- a/org.eclipse.jgit.test/tst/org/eclipse/jgit/dircache/DirCacheLargePathTest.java
+++ b/org.eclipse.jgit.test/tst/org/eclipse/jgit/dircache/DirCacheLargePathTest.java
@@ -85,7 +85,7 @@ public class DirCacheLargePathTest extends RepositoryTestCase {
assertEquals(shortPath, shortEnt.getPathString());
{
- final DirCache dc1 = DirCache.lock(db);
+ final DirCache dc1 = db.lockDirCache();
{
final DirCacheBuilder b = dc1.builder();
b.add(longEnt);
@@ -97,7 +97,7 @@ public class DirCacheLargePathTest extends RepositoryTestCase {
assertSame(shortEnt, dc1.getEntry(1));
}
{
- final DirCache dc2 = DirCache.read(db);
+ final DirCache dc2 = db.readDirCache();
assertEquals(2, dc2.getEntryCount());
assertNotSame(longEnt, dc2.getEntry(0));
diff --git a/org.eclipse.jgit.test/tst/org/eclipse/jgit/dircache/DirCacheTreeTest.java b/org.eclipse.jgit.test/tst/org/eclipse/jgit/dircache/DirCacheTreeTest.java
index 8345c5d83d..dfca2fb298 100644
--- a/org.eclipse.jgit.test/tst/org/eclipse/jgit/dircache/DirCacheTreeTest.java
+++ b/org.eclipse.jgit.test/tst/org/eclipse/jgit/dircache/DirCacheTreeTest.java
@@ -51,12 +51,12 @@ import org.eclipse.jgit.lib.RepositoryTestCase;
public class DirCacheTreeTest extends RepositoryTestCase {
public void testEmptyCache_NoCacheTree() throws Exception {
- final DirCache dc = DirCache.read(db);
+ final DirCache dc = db.readDirCache();
assertNull(dc.getCacheTree(false));
}
public void testEmptyCache_CreateEmptyCacheTree() throws Exception {
- final DirCache dc = DirCache.read(db);
+ final DirCache dc = db.readDirCache();
final DirCacheTree tree = dc.getCacheTree(true);
assertNotNull(tree);
assertSame(tree, dc.getCacheTree(false));
@@ -69,7 +69,7 @@ public class DirCacheTreeTest extends RepositoryTestCase {
}
public void testEmptyCache_Clear_NoCacheTree() throws Exception {
- final DirCache dc = DirCache.read(db);
+ final DirCache dc = db.readDirCache();
final DirCacheTree tree = dc.getCacheTree(true);
assertNotNull(tree);
dc.clear();
@@ -78,7 +78,7 @@ public class DirCacheTreeTest extends RepositoryTestCase {
}
public void testSingleSubtree() throws Exception {
- final DirCache dc = DirCache.read(db);
+ final DirCache dc = db.readDirCache();
final String[] paths = { "a.", "a/b", "a/c", "a/d", "a0b" };
final DirCacheEntry[] ents = new DirCacheEntry[paths.length];
@@ -115,7 +115,7 @@ public class DirCacheTreeTest extends RepositoryTestCase {
}
public void testTwoLevelSubtree() throws Exception {
- final DirCache dc = DirCache.read(db);
+ final DirCache dc = db.readDirCache();
final String[] paths = { "a.", "a/b", "a/c/e", "a/c/f", "a/d", "a0b" };
final DirCacheEntry[] ents = new DirCacheEntry[paths.length];
@@ -172,7 +172,7 @@ public class DirCacheTreeTest extends RepositoryTestCase {
* @throws IOException
*/
public void testWriteReadTree() throws CorruptObjectException, IOException {
- final DirCache dc = DirCache.lock(db);
+ final DirCache dc = db.lockDirCache();
final String A = String.format("a%2000s", "a");
final String B = String.format("b%2000s", "b");
@@ -188,7 +188,7 @@ public class DirCacheTreeTest extends RepositoryTestCase {
b.add(ents[i]);
b.commit();
- DirCache read = DirCache.read(db);
+ DirCache read = db.readDirCache();
assertEquals(paths.length, read.getEntryCount());
assertEquals(1, read.getCacheTree(true).getChildCount());
diff --git a/org.eclipse.jgit.test/tst/org/eclipse/jgit/lib/ReadTreeTest.java b/org.eclipse.jgit.test/tst/org/eclipse/jgit/lib/ReadTreeTest.java
index 02bfdc1df5..2fa45c8211 100644
--- a/org.eclipse.jgit.test/tst/org/eclipse/jgit/lib/ReadTreeTest.java
+++ b/org.eclipse.jgit.test/tst/org/eclipse/jgit/lib/ReadTreeTest.java
@@ -45,11 +45,9 @@
*/
package org.eclipse.jgit.lib;
-import java.io.ByteArrayInputStream;
import java.io.File;
import java.io.FileInputStream;
import java.io.IOException;
-import java.io.InputStream;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.HashMap;
@@ -131,13 +129,15 @@ public abstract class ReadTreeTest extends RepositoryTestCase {
}
ObjectId genSha1(String data) {
- InputStream is = new ByteArrayInputStream(data.getBytes());
- ObjectWriter objectWriter = new ObjectWriter(db);
+ ObjectInserter w = db.newObjectInserter();
try {
- return objectWriter.writeObject(Constants.OBJ_BLOB, data
- .getBytes().length, is, true);
+ ObjectId id = w.insert(Constants.OBJ_BLOB, data.getBytes());
+ w.flush();
+ return id;
} catch (IOException e) {
fail(e.toString());
+ } finally {
+ w.release();
}
return null;
}
@@ -623,7 +623,7 @@ public abstract class ReadTreeTest extends RepositoryTestCase {
expectedValue = i.get(path);
assertNotNull("found unexpected file for path "
+ path + " in workdir", expectedValue);
- File file = new File(db.getWorkDir(), path);
+ File file = new File(db.getWorkTree(), path);
assertTrue(file.exists());
if (file.isFile()) {
FileInputStream is = new FileInputStream(file);
@@ -661,8 +661,8 @@ public abstract class ReadTreeTest extends RepositoryTestCase {
assertTrue("unexpected content for path " + path
+ " in index. Expected: <" + expectedValue + ">",
Arrays.equals(
- db.openBlob(theIndex.getMembers()[j].getObjectId())
- .getBytes(), i.get(path).getBytes()));
+ db.open(theIndex.getMembers()[j].getObjectId())
+ .getCachedBytes(), i.get(path).getBytes()));
}
}
diff --git a/org.eclipse.jgit.test/tst/org/eclipse/jgit/lib/ReflogConfigTest.java b/org.eclipse.jgit.test/tst/org/eclipse/jgit/lib/ReflogConfigTest.java
index 88bcf76710..d78892b89d 100644
--- a/org.eclipse.jgit.test/tst/org/eclipse/jgit/lib/ReflogConfigTest.java
+++ b/org.eclipse.jgit.test/tst/org/eclipse/jgit/lib/ReflogConfigTest.java
@@ -70,7 +70,7 @@ public class ReflogConfigTest extends RepositoryTestCase {
// set the logAllRefUpdates parameter to true and check it
db.getConfig().setBoolean("core", null, "logallrefupdates", true);
- assertTrue(db.getConfig().getCore().isLogAllRefUpdates());
+ assertTrue(db.getConfig().get(CoreConfig.KEY).isLogAllRefUpdates());
// do one commit and check that reflog size is increased to 1
addFileToTree(t, "i-am-another-file", "and this is other data in me\n");
@@ -83,7 +83,7 @@ public class ReflogConfigTest extends RepositoryTestCase {
// set the logAllRefUpdates parameter to false and check it
db.getConfig().setBoolean("core", null, "logallrefupdates", false);
- assertFalse(db.getConfig().getCore().isLogAllRefUpdates());
+ assertFalse(db.getConfig().get(CoreConfig.KEY).isLogAllRefUpdates());
// do one commit and check that reflog size is 2
addFileToTree(t, "i-am-anotheranother-file",
diff --git a/org.eclipse.jgit.test/tst/org/eclipse/jgit/lib/RepositoryTestCase.java b/org.eclipse.jgit.test/tst/org/eclipse/jgit/lib/RepositoryTestCase.java
index c5c6d998bb..e78f8512a2 100644
--- a/org.eclipse.jgit.test/tst/org/eclipse/jgit/lib/RepositoryTestCase.java
+++ b/org.eclipse.jgit.test/tst/org/eclipse/jgit/lib/RepositoryTestCase.java
@@ -54,6 +54,7 @@ import java.io.InputStreamReader;
import java.io.Reader;
import org.eclipse.jgit.junit.LocalDiskRepositoryTestCase;
+import org.eclipse.jgit.storage.file.FileRepository;
/**
* Base class for most JGit unit tests.
@@ -83,7 +84,7 @@ public abstract class RepositoryTestCase extends LocalDiskRepositoryTestCase {
protected File writeTrashFile(final String name, final String data)
throws IOException {
- File path = new File(db.getWorkDir(), name);
+ File path = new File(db.getWorkTree(), name);
write(path, data);
return path;
}
@@ -102,7 +103,7 @@ public abstract class RepositoryTestCase extends LocalDiskRepositoryTestCase {
}
/** Test repository, initialized for this test case. */
- protected Repository db;
+ protected FileRepository db;
/** Working directory of {@link #db}. */
protected File trash;
@@ -111,6 +112,6 @@ public abstract class RepositoryTestCase extends LocalDiskRepositoryTestCase {
protected void setUp() throws Exception {
super.setUp();
db = createWorkRepository();
- trash = db.getWorkDir();
+ trash = db.getWorkTree();
}
}
diff --git a/org.eclipse.jgit.test/tst/org/eclipse/jgit/lib/SampleDataRepositoryTestCase.java b/org.eclipse.jgit.test/tst/org/eclipse/jgit/lib/SampleDataRepositoryTestCase.java
index 10c005679b..7bc9bb22e7 100644
--- a/org.eclipse.jgit.test/tst/org/eclipse/jgit/lib/SampleDataRepositoryTestCase.java
+++ b/org.eclipse.jgit.test/tst/org/eclipse/jgit/lib/SampleDataRepositoryTestCase.java
@@ -65,7 +65,7 @@ public abstract class SampleDataRepositoryTestCase extends RepositoryTestCase {
"pack-e6d07037cbcf13376308a0a995d1fa48f8f76aaa",
"pack-3280af9c07ee18a87705ef50b0cc4cd20266cf12"
};
- final File packDir = new File(db.getObjectsDirectory(), "pack");
+ final File packDir = new File(db.getObjectDatabase().getDirectory(), "pack");
for (String n : packs) {
copyFile(JGitTestUtil.getTestResourceFile(n + ".pack"), new File(packDir, n + ".pack"));
copyFile(JGitTestUtil.getTestResourceFile(n + ".idx"), new File(packDir, n + ".idx"));
diff --git a/org.eclipse.jgit.test/tst/org/eclipse/jgit/lib/WorkDirCheckout_ReadTreeTest.java b/org.eclipse.jgit.test/tst/org/eclipse/jgit/lib/WorkDirCheckout_ReadTreeTest.java
index ecaac5846e..7b5c3cdc57 100644
--- a/org.eclipse.jgit.test/tst/org/eclipse/jgit/lib/WorkDirCheckout_ReadTreeTest.java
+++ b/org.eclipse.jgit.test/tst/org/eclipse/jgit/lib/WorkDirCheckout_ReadTreeTest.java
@@ -52,12 +52,12 @@ import java.util.HashMap;
public class WorkDirCheckout_ReadTreeTest extends ReadTreeTest {
private WorkDirCheckout wdc;
public void prescanTwoTrees(Tree head, Tree merge) throws IllegalStateException, IOException {
- wdc = new WorkDirCheckout(db, db.getWorkDir(), head, db.getIndex(), merge);
+ wdc = new WorkDirCheckout(db, db.getWorkTree(), head, db.getIndex(), merge);
wdc.prescanTwoTrees();
}
public void checkout() throws IOException {
- wdc = new WorkDirCheckout(db, db.getWorkDir(), theHead, db.getIndex(), theMerge);
+ wdc = new WorkDirCheckout(db, db.getWorkTree(), theHead, db.getIndex(), theMerge);
wdc.checkout();
}
diff --git a/org.eclipse.jgit.test/tst/org/eclipse/jgit/merge/CherryPickTest.java b/org.eclipse.jgit.test/tst/org/eclipse/jgit/merge/CherryPickTest.java
index 42e653be37..1cd1261636 100644
--- a/org.eclipse.jgit.test/tst/org/eclipse/jgit/merge/CherryPickTest.java
+++ b/org.eclipse.jgit.test/tst/org/eclipse/jgit/merge/CherryPickTest.java
@@ -44,7 +44,8 @@
package org.eclipse.jgit.merge;
-import java.io.ByteArrayInputStream;
+import static org.eclipse.jgit.lib.Constants.OBJ_BLOB;
+import static org.eclipse.jgit.lib.Constants.OBJ_COMMIT;
import org.eclipse.jgit.dircache.DirCache;
import org.eclipse.jgit.dircache.DirCacheBuilder;
@@ -53,7 +54,7 @@ import org.eclipse.jgit.lib.Commit;
import org.eclipse.jgit.lib.Constants;
import org.eclipse.jgit.lib.FileMode;
import org.eclipse.jgit.lib.ObjectId;
-import org.eclipse.jgit.lib.ObjectWriter;
+import org.eclipse.jgit.lib.ObjectInserter;
import org.eclipse.jgit.lib.PersonIdent;
import org.eclipse.jgit.lib.RepositoryTestCase;
import org.eclipse.jgit.treewalk.TreeWalk;
@@ -66,10 +67,10 @@ public class CherryPickTest extends RepositoryTestCase {
// Cherry-pick "T" onto "O". This shouldn't introduce "p-fail", which
// was created by "P", nor should it modify "a", which was done by "P".
//
- final DirCache treeB = DirCache.read(db);
- final DirCache treeO = DirCache.read(db);
- final DirCache treeP = DirCache.read(db);
- final DirCache treeT = DirCache.read(db);
+ final DirCache treeB = db.readDirCache();
+ final DirCache treeO = db.readDirCache();
+ final DirCache treeP = db.readDirCache();
+ final DirCache treeT = db.readDirCache();
{
final DirCacheBuilder b = treeB.builder();
final DirCacheBuilder o = treeO.builder();
@@ -93,7 +94,7 @@ public class CherryPickTest extends RepositoryTestCase {
t.finish();
}
- final ObjectWriter ow = new ObjectWriter(db);
+ final ObjectInserter ow = db.newObjectInserter();
final ObjectId B = commit(ow, treeB, new ObjectId[] {});
final ObjectId O = commit(ow, treeO, new ObjectId[] { B });
final ObjectId P = commit(ow, treeP, new ObjectId[] { B });
@@ -128,15 +129,17 @@ public class CherryPickTest extends RepositoryTestCase {
.getObjectId(0));
}
- private ObjectId commit(final ObjectWriter ow, final DirCache treeB,
+ private ObjectId commit(final ObjectInserter odi, final DirCache treeB,
final ObjectId[] parentIds) throws Exception {
final Commit c = new Commit(db);
- c.setTreeId(treeB.writeTree(ow));
+ c.setTreeId(treeB.writeTree(odi));
c.setAuthor(new PersonIdent("A U Thor", "a.u.thor", 1L, 0));
c.setCommitter(c.getAuthor());
c.setParentIds(parentIds);
c.setMessage("Tree " + c.getTreeId().name());
- return ow.writeCommit(c);
+ ObjectId id = odi.insert(OBJ_COMMIT, odi.format(c));
+ odi.flush();
+ return id;
}
private DirCacheEntry makeEntry(final String path, final FileMode mode)
@@ -148,9 +151,8 @@ public class CherryPickTest extends RepositoryTestCase {
final String content) throws Exception {
final DirCacheEntry ent = new DirCacheEntry(path);
ent.setFileMode(mode);
- final byte[] contentBytes = Constants.encode(content);
- ent.setObjectId(new ObjectWriter(db).computeBlobSha1(
- contentBytes.length, new ByteArrayInputStream(contentBytes)));
+ ent.setObjectId(new ObjectInserter.Formatter().idFor(OBJ_BLOB,
+ Constants.encode(content)));
return ent;
}
}
diff --git a/org.eclipse.jgit.test/tst/org/eclipse/jgit/merge/SimpleMergeTest.java b/org.eclipse.jgit.test/tst/org/eclipse/jgit/merge/SimpleMergeTest.java
index 690b166cbc..8657c52b16 100644
--- a/org.eclipse.jgit.test/tst/org/eclipse/jgit/merge/SimpleMergeTest.java
+++ b/org.eclipse.jgit.test/tst/org/eclipse/jgit/merge/SimpleMergeTest.java
@@ -44,7 +44,9 @@
package org.eclipse.jgit.merge;
-import java.io.ByteArrayInputStream;
+import static org.eclipse.jgit.lib.Constants.OBJ_BLOB;
+import static org.eclipse.jgit.lib.Constants.OBJ_COMMIT;
+
import java.io.IOException;
import org.eclipse.jgit.dircache.DirCache;
@@ -54,7 +56,7 @@ import org.eclipse.jgit.lib.Commit;
import org.eclipse.jgit.lib.Constants;
import org.eclipse.jgit.lib.FileMode;
import org.eclipse.jgit.lib.ObjectId;
-import org.eclipse.jgit.lib.ObjectWriter;
+import org.eclipse.jgit.lib.ObjectInserter;
import org.eclipse.jgit.lib.PersonIdent;
import org.eclipse.jgit.lib.SampleDataRepositoryTestCase;
import org.eclipse.jgit.treewalk.TreeWalk;
@@ -103,9 +105,9 @@ public class SimpleMergeTest extends SampleDataRepositoryTestCase {
}
public void testTrivialTwoWay_validSubtreeSort() throws Exception {
- final DirCache treeB = DirCache.read(db);
- final DirCache treeO = DirCache.read(db);
- final DirCache treeT = DirCache.read(db);
+ final DirCache treeB = db.readDirCache();
+ final DirCache treeO = db.readDirCache();
+ final DirCache treeT = db.readDirCache();
{
final DirCacheBuilder b = treeB.builder();
final DirCacheBuilder o = treeO.builder();
@@ -126,7 +128,7 @@ public class SimpleMergeTest extends SampleDataRepositoryTestCase {
t.finish();
}
- final ObjectWriter ow = new ObjectWriter(db);
+ final ObjectInserter ow = db.newObjectInserter();
final ObjectId b = commit(ow, treeB, new ObjectId[] {});
final ObjectId o = commit(ow, treeO, new ObjectId[] { b });
final ObjectId t = commit(ow, treeT, new ObjectId[] { b });
@@ -155,9 +157,9 @@ public class SimpleMergeTest extends SampleDataRepositoryTestCase {
}
public void testTrivialTwoWay_concurrentSubtreeChange() throws Exception {
- final DirCache treeB = DirCache.read(db);
- final DirCache treeO = DirCache.read(db);
- final DirCache treeT = DirCache.read(db);
+ final DirCache treeB = db.readDirCache();
+ final DirCache treeO = db.readDirCache();
+ final DirCache treeT = db.readDirCache();
{
final DirCacheBuilder b = treeB.builder();
final DirCacheBuilder o = treeO.builder();
@@ -177,7 +179,7 @@ public class SimpleMergeTest extends SampleDataRepositoryTestCase {
t.finish();
}
- final ObjectWriter ow = new ObjectWriter(db);
+ final ObjectInserter ow = db.newObjectInserter();
final ObjectId b = commit(ow, treeB, new ObjectId[] {});
final ObjectId o = commit(ow, treeO, new ObjectId[] { b });
final ObjectId t = commit(ow, treeT, new ObjectId[] { b });
@@ -202,9 +204,9 @@ public class SimpleMergeTest extends SampleDataRepositoryTestCase {
}
public void testTrivialTwoWay_conflictSubtreeChange() throws Exception {
- final DirCache treeB = DirCache.read(db);
- final DirCache treeO = DirCache.read(db);
- final DirCache treeT = DirCache.read(db);
+ final DirCache treeB = db.readDirCache();
+ final DirCache treeO = db.readDirCache();
+ final DirCache treeT = db.readDirCache();
{
final DirCacheBuilder b = treeB.builder();
final DirCacheBuilder o = treeO.builder();
@@ -224,7 +226,7 @@ public class SimpleMergeTest extends SampleDataRepositoryTestCase {
t.finish();
}
- final ObjectWriter ow = new ObjectWriter(db);
+ final ObjectInserter ow = db.newObjectInserter();
final ObjectId b = commit(ow, treeB, new ObjectId[] {});
final ObjectId o = commit(ow, treeO, new ObjectId[] { b });
final ObjectId t = commit(ow, treeT, new ObjectId[] { b });
@@ -235,9 +237,9 @@ public class SimpleMergeTest extends SampleDataRepositoryTestCase {
}
public void testTrivialTwoWay_leftDFconflict1() throws Exception {
- final DirCache treeB = DirCache.read(db);
- final DirCache treeO = DirCache.read(db);
- final DirCache treeT = DirCache.read(db);
+ final DirCache treeB = db.readDirCache();
+ final DirCache treeO = db.readDirCache();
+ final DirCache treeT = db.readDirCache();
{
final DirCacheBuilder b = treeB.builder();
final DirCacheBuilder o = treeO.builder();
@@ -256,7 +258,7 @@ public class SimpleMergeTest extends SampleDataRepositoryTestCase {
t.finish();
}
- final ObjectWriter ow = new ObjectWriter(db);
+ final ObjectInserter ow = db.newObjectInserter();
final ObjectId b = commit(ow, treeB, new ObjectId[] {});
final ObjectId o = commit(ow, treeO, new ObjectId[] { b });
final ObjectId t = commit(ow, treeT, new ObjectId[] { b });
@@ -267,9 +269,9 @@ public class SimpleMergeTest extends SampleDataRepositoryTestCase {
}
public void testTrivialTwoWay_rightDFconflict1() throws Exception {
- final DirCache treeB = DirCache.read(db);
- final DirCache treeO = DirCache.read(db);
- final DirCache treeT = DirCache.read(db);
+ final DirCache treeB = db.readDirCache();
+ final DirCache treeO = db.readDirCache();
+ final DirCache treeT = db.readDirCache();
{
final DirCacheBuilder b = treeB.builder();
final DirCacheBuilder o = treeO.builder();
@@ -288,7 +290,7 @@ public class SimpleMergeTest extends SampleDataRepositoryTestCase {
t.finish();
}
- final ObjectWriter ow = new ObjectWriter(db);
+ final ObjectInserter ow = db.newObjectInserter();
final ObjectId b = commit(ow, treeB, new ObjectId[] {});
final ObjectId o = commit(ow, treeO, new ObjectId[] { b });
final ObjectId t = commit(ow, treeT, new ObjectId[] { b });
@@ -299,9 +301,9 @@ public class SimpleMergeTest extends SampleDataRepositoryTestCase {
}
public void testTrivialTwoWay_leftDFconflict2() throws Exception {
- final DirCache treeB = DirCache.read(db);
- final DirCache treeO = DirCache.read(db);
- final DirCache treeT = DirCache.read(db);
+ final DirCache treeB = db.readDirCache();
+ final DirCache treeO = db.readDirCache();
+ final DirCache treeT = db.readDirCache();
{
final DirCacheBuilder b = treeB.builder();
final DirCacheBuilder o = treeO.builder();
@@ -318,7 +320,7 @@ public class SimpleMergeTest extends SampleDataRepositoryTestCase {
t.finish();
}
- final ObjectWriter ow = new ObjectWriter(db);
+ final ObjectInserter ow = db.newObjectInserter();
final ObjectId b = commit(ow, treeB, new ObjectId[] {});
final ObjectId o = commit(ow, treeO, new ObjectId[] { b });
final ObjectId t = commit(ow, treeT, new ObjectId[] { b });
@@ -329,9 +331,9 @@ public class SimpleMergeTest extends SampleDataRepositoryTestCase {
}
public void testTrivialTwoWay_rightDFconflict2() throws Exception {
- final DirCache treeB = DirCache.read(db);
- final DirCache treeO = DirCache.read(db);
- final DirCache treeT = DirCache.read(db);
+ final DirCache treeB = db.readDirCache();
+ final DirCache treeO = db.readDirCache();
+ final DirCache treeT = db.readDirCache();
{
final DirCacheBuilder b = treeB.builder();
final DirCacheBuilder o = treeO.builder();
@@ -348,7 +350,7 @@ public class SimpleMergeTest extends SampleDataRepositoryTestCase {
t.finish();
}
- final ObjectWriter ow = new ObjectWriter(db);
+ final ObjectInserter ow = db.newObjectInserter();
final ObjectId b = commit(ow, treeB, new ObjectId[] {});
final ObjectId o = commit(ow, treeO, new ObjectId[] { b });
final ObjectId t = commit(ow, treeT, new ObjectId[] { b });
@@ -363,15 +365,17 @@ public class SimpleMergeTest extends SampleDataRepositoryTestCase {
.getObjectId(0));
}
- private ObjectId commit(final ObjectWriter ow, final DirCache treeB,
+ private ObjectId commit(final ObjectInserter odi, final DirCache treeB,
final ObjectId[] parentIds) throws Exception {
final Commit c = new Commit(db);
- c.setTreeId(treeB.writeTree(ow));
+ c.setTreeId(treeB.writeTree(odi));
c.setAuthor(new PersonIdent("A U Thor", "a.u.thor", 1L, 0));
c.setCommitter(c.getAuthor());
c.setParentIds(parentIds);
c.setMessage("Tree " + c.getTreeId().name());
- return ow.writeCommit(c);
+ ObjectId id = odi.insert(OBJ_COMMIT, odi.format(c));
+ odi.flush();
+ return id;
}
private DirCacheEntry makeEntry(final String path, final FileMode mode)
@@ -383,9 +387,8 @@ public class SimpleMergeTest extends SampleDataRepositoryTestCase {
final String content) throws Exception {
final DirCacheEntry ent = new DirCacheEntry(path);
ent.setFileMode(mode);
- final byte[] contentBytes = Constants.encode(content);
- ent.setObjectId(new ObjectWriter(db).computeBlobSha1(
- contentBytes.length, new ByteArrayInputStream(contentBytes)));
+ ent.setObjectId(new ObjectInserter.Formatter().idFor(OBJ_BLOB,
+ Constants.encode(content)));
return ent;
}
}
diff --git a/org.eclipse.jgit.test/tst/org/eclipse/jgit/revwalk/RevWalkTestCase.java b/org.eclipse.jgit.test/tst/org/eclipse/jgit/revwalk/RevWalkTestCase.java
index 64052323f1..9473fe6318 100644
--- a/org.eclipse.jgit.test/tst/org/eclipse/jgit/revwalk/RevWalkTestCase.java
+++ b/org.eclipse.jgit.test/tst/org/eclipse/jgit/revwalk/RevWalkTestCase.java
@@ -47,18 +47,19 @@ import java.util.Date;
import org.eclipse.jgit.dircache.DirCacheEntry;
import org.eclipse.jgit.junit.TestRepository;
+import org.eclipse.jgit.lib.Repository;
import org.eclipse.jgit.lib.RepositoryTestCase;
/** Support for tests of the {@link RevWalk} class. */
public abstract class RevWalkTestCase extends RepositoryTestCase {
- private TestRepository util;
+ private TestRepository<Repository> util;
protected RevWalk rw;
@Override
public void setUp() throws Exception {
super.setUp();
- util = new TestRepository(db, createRevWalk());
+ util = new TestRepository<Repository>(db, createRevWalk());
rw = util.getRevWalk();
}
diff --git a/org.eclipse.jgit.test/tst/org/eclipse/jgit/lib/ConcurrentRepackTest.java b/org.eclipse.jgit.test/tst/org/eclipse/jgit/storage/file/ConcurrentRepackTest.java
index 69430ed334..c8f2aad759 100644
--- a/org.eclipse.jgit.test/tst/org/eclipse/jgit/lib/ConcurrentRepackTest.java
+++ b/org.eclipse.jgit.test/tst/org/eclipse/jgit/storage/file/ConcurrentRepackTest.java
@@ -42,7 +42,7 @@
* ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
*/
-package org.eclipse.jgit.lib;
+package org.eclipse.jgit.storage.file;
import java.io.BufferedOutputStream;
import java.io.File;
@@ -53,8 +53,17 @@ import java.util.Arrays;
import org.eclipse.jgit.errors.IncorrectObjectTypeException;
import org.eclipse.jgit.errors.MissingObjectException;
+import org.eclipse.jgit.lib.AnyObjectId;
+import org.eclipse.jgit.lib.Constants;
+import org.eclipse.jgit.lib.NullProgressMonitor;
+import org.eclipse.jgit.lib.ObjectId;
+import org.eclipse.jgit.lib.ObjectLoader;
+import org.eclipse.jgit.lib.ObjectWriter;
+import org.eclipse.jgit.lib.Repository;
+import org.eclipse.jgit.lib.RepositoryTestCase;
import org.eclipse.jgit.revwalk.RevObject;
import org.eclipse.jgit.revwalk.RevWalk;
+import org.eclipse.jgit.storage.pack.PackWriter;
public class ConcurrentRepackTest extends RepositoryTestCase {
public void setUp() throws Exception {
@@ -125,10 +134,11 @@ public class ConcurrentRepackTest extends RepositoryTestCase {
// within the pack has been modified.
//
final RevObject o2 = writeBlob(eden, "o2");
- final PackWriter pw = new PackWriter(eden, NullProgressMonitor.INSTANCE);
+ final PackWriter pw = new PackWriter(eden);
pw.addObject(o2);
pw.addObject(o1);
write(out1, pw);
+ pw.release();
// Try the old name, then the new name. The old name should cause the
// pack to reload when it opens and the index and pack mismatch.
@@ -148,7 +158,7 @@ public class ConcurrentRepackTest extends RepositoryTestCase {
final File[] out1 = pack(eden, o1);
assertEquals(o1.name(), parse(o1).name());
- final ObjectLoader load1 = db.openBlob(o1);
+ final ObjectLoader load1 = db.open(o1, Constants.OBJ_BLOB);
assertNotNull(load1);
final RevObject o2 = writeBlob(eden, "o2");
@@ -163,7 +173,7 @@ public class ConcurrentRepackTest extends RepositoryTestCase {
// earlier still resolve the object, even though its underlying
// pack is gone, but the object still exists.
//
- final ObjectLoader load2 = db.openBlob(o1);
+ final ObjectLoader load2 = db.open(o1, Constants.OBJ_BLOB);
assertNotNull(load2);
assertNotSame(load1, load2);
@@ -189,7 +199,7 @@ public class ConcurrentRepackTest extends RepositoryTestCase {
private File[] pack(final Repository src, final RevObject... list)
throws IOException {
- final PackWriter pw = new PackWriter(src, NullProgressMonitor.INSTANCE);
+ final PackWriter pw = new PackWriter(src);
for (final RevObject o : list) {
pw.addObject(o);
}
@@ -199,17 +209,19 @@ public class ConcurrentRepackTest extends RepositoryTestCase {
final File idxFile = fullPackFileName(name, ".idx");
final File[] files = new File[] { packFile, idxFile };
write(files, pw);
+ pw.release();
return files;
}
private static void write(final File[] files, final PackWriter pw)
throws IOException {
final long begin = files[0].getParentFile().lastModified();
+ NullProgressMonitor m = NullProgressMonitor.INSTANCE;
OutputStream out;
out = new BufferedOutputStream(new FileOutputStream(files[0]));
try {
- pw.writePack(out);
+ pw.writePack(m, m, out);
} finally {
out.close();
}
@@ -245,7 +257,7 @@ public class ConcurrentRepackTest extends RepositoryTestCase {
}
private File fullPackFileName(final ObjectId name, final String suffix) {
- final File packdir = new File(db.getObjectsDirectory(), "pack");
+ final File packdir = new File(db.getObjectDatabase().getDirectory(), "pack");
return new File(packdir, "pack-" + name.name() + suffix);
}
diff --git a/org.eclipse.jgit.test/tst/org/eclipse/jgit/storage/file/PackFileTest.java b/org.eclipse.jgit.test/tst/org/eclipse/jgit/storage/file/PackFileTest.java
new file mode 100644
index 0000000000..1a40b8e79c
--- /dev/null
+++ b/org.eclipse.jgit.test/tst/org/eclipse/jgit/storage/file/PackFileTest.java
@@ -0,0 +1,387 @@
+/*
+ * Copyright (C) 2010, Google Inc.
+ * and other copyright owners as documented in the project's IP log.
+ *
+ * This program and the accompanying materials are made available
+ * under the terms of the Eclipse Distribution License v1.0 which
+ * accompanies this distribution, is reproduced below, and is
+ * available at http://www.eclipse.org/org/documents/edl-v10.php
+ *
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or
+ * without modification, are permitted provided that the following
+ * conditions are met:
+ *
+ * - Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ *
+ * - Redistributions in binary form must reproduce the above
+ * copyright notice, this list of conditions and the following
+ * disclaimer in the documentation and/or other materials provided
+ * with the distribution.
+ *
+ * - Neither the name of the Eclipse Foundation, Inc. nor the
+ * names of its contributors may be used to endorse or promote
+ * products derived from this software without specific prior
+ * written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND
+ * CONTRIBUTORS "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES,
+ * INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
+ * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR
+ * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
+ * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT
+ * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
+ * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER
+ * CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT,
+ * STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
+ * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF
+ * ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ */
+
+package org.eclipse.jgit.storage.file;
+
+import java.io.ByteArrayInputStream;
+import java.io.ByteArrayOutputStream;
+import java.io.IOException;
+import java.security.MessageDigest;
+import java.util.Arrays;
+import java.util.zip.Deflater;
+
+import org.eclipse.jgit.errors.LargeObjectException;
+import org.eclipse.jgit.junit.LocalDiskRepositoryTestCase;
+import org.eclipse.jgit.junit.TestRepository;
+import org.eclipse.jgit.junit.TestRng;
+import org.eclipse.jgit.lib.Constants;
+import org.eclipse.jgit.lib.NullProgressMonitor;
+import org.eclipse.jgit.lib.ObjectId;
+import org.eclipse.jgit.lib.ObjectInserter;
+import org.eclipse.jgit.lib.ObjectLoader;
+import org.eclipse.jgit.lib.ObjectStream;
+import org.eclipse.jgit.revwalk.RevBlob;
+import org.eclipse.jgit.storage.pack.DeltaEncoder;
+import org.eclipse.jgit.transport.IndexPack;
+import org.eclipse.jgit.util.IO;
+import org.eclipse.jgit.util.NB;
+import org.eclipse.jgit.util.TemporaryBuffer;
+
+public class PackFileTest extends LocalDiskRepositoryTestCase {
+ private TestRng rng;
+
+ private FileRepository repo;
+
+ private TestRepository<FileRepository> tr;
+
+ private WindowCursor wc;
+
+ protected void setUp() throws Exception {
+ super.setUp();
+ rng = new TestRng(getName());
+ repo = createBareRepository();
+ tr = new TestRepository<FileRepository>(repo);
+ wc = (WindowCursor) repo.newObjectReader();
+ }
+
+ protected void tearDown() throws Exception {
+ if (wc != null)
+ wc.release();
+ super.tearDown();
+ }
+
+ public void testWhole_SmallObject() throws Exception {
+ final int type = Constants.OBJ_BLOB;
+ byte[] data = rng.nextBytes(300);
+ RevBlob id = tr.blob(data);
+ tr.branch("master").commit().add("A", id).create();
+ tr.packAndPrune();
+ assertTrue("has blob", wc.has(id));
+
+ ObjectLoader ol = wc.open(id);
+ assertNotNull("created loader", ol);
+ assertEquals(type, ol.getType());
+ assertEquals(data.length, ol.getSize());
+ assertFalse("is not large", ol.isLarge());
+ assertTrue("same content", Arrays.equals(data, ol.getCachedBytes()));
+
+ ObjectStream in = ol.openStream();
+ assertNotNull("have stream", in);
+ assertEquals(type, in.getType());
+ assertEquals(data.length, in.getSize());
+ byte[] data2 = new byte[data.length];
+ IO.readFully(in, data2, 0, data.length);
+ assertTrue("same content", Arrays.equals(data2, data));
+ assertEquals("stream at EOF", -1, in.read());
+ in.close();
+ }
+
+ public void testWhole_LargeObject() throws Exception {
+ final int type = Constants.OBJ_BLOB;
+ byte[] data = rng.nextBytes(ObjectLoader.STREAM_THRESHOLD + 5);
+ RevBlob id = tr.blob(data);
+ tr.branch("master").commit().add("A", id).create();
+ tr.packAndPrune();
+ assertTrue("has blob", wc.has(id));
+
+ ObjectLoader ol = wc.open(id);
+ assertNotNull("created loader", ol);
+ assertEquals(type, ol.getType());
+ assertEquals(data.length, ol.getSize());
+ assertTrue("is large", ol.isLarge());
+ try {
+ ol.getCachedBytes();
+ fail("Should have thrown LargeObjectException");
+ } catch (LargeObjectException tooBig) {
+ assertEquals(id.name(), tooBig.getMessage());
+ }
+
+ ObjectStream in = ol.openStream();
+ assertNotNull("have stream", in);
+ assertEquals(type, in.getType());
+ assertEquals(data.length, in.getSize());
+ byte[] data2 = new byte[data.length];
+ IO.readFully(in, data2, 0, data.length);
+ assertTrue("same content", Arrays.equals(data2, data));
+ assertEquals("stream at EOF", -1, in.read());
+ in.close();
+ }
+
+ public void testDelta_SmallObjectChain() throws Exception {
+ ObjectInserter.Formatter fmt = new ObjectInserter.Formatter();
+ byte[] data0 = new byte[512];
+ Arrays.fill(data0, (byte) 0xf3);
+ ObjectId id0 = fmt.idFor(Constants.OBJ_BLOB, data0);
+
+ TemporaryBuffer.Heap pack = new TemporaryBuffer.Heap(64 * 1024);
+ packHeader(pack, 4);
+ objectHeader(pack, Constants.OBJ_BLOB, data0.length);
+ deflate(pack, data0);
+
+ byte[] data1 = clone(0x01, data0);
+ byte[] delta1 = delta(data0, data1);
+ ObjectId id1 = fmt.idFor(Constants.OBJ_BLOB, data1);
+ objectHeader(pack, Constants.OBJ_REF_DELTA, delta1.length);
+ id0.copyRawTo(pack);
+ deflate(pack, delta1);
+
+ byte[] data2 = clone(0x02, data1);
+ byte[] delta2 = delta(data1, data2);
+ ObjectId id2 = fmt.idFor(Constants.OBJ_BLOB, data2);
+ objectHeader(pack, Constants.OBJ_REF_DELTA, delta2.length);
+ id1.copyRawTo(pack);
+ deflate(pack, delta2);
+
+ byte[] data3 = clone(0x03, data2);
+ byte[] delta3 = delta(data2, data3);
+ ObjectId id3 = fmt.idFor(Constants.OBJ_BLOB, data3);
+ objectHeader(pack, Constants.OBJ_REF_DELTA, delta3.length);
+ id2.copyRawTo(pack);
+ deflate(pack, delta3);
+
+ digest(pack);
+ final byte[] raw = pack.toByteArray();
+ IndexPack ip = IndexPack.create(repo, new ByteArrayInputStream(raw));
+ ip.setFixThin(true);
+ ip.index(NullProgressMonitor.INSTANCE);
+ ip.renameAndOpenPack();
+
+ assertTrue("has blob", wc.has(id3));
+
+ ObjectLoader ol = wc.open(id3);
+ assertNotNull("created loader", ol);
+ assertEquals(Constants.OBJ_BLOB, ol.getType());
+ assertEquals(data3.length, ol.getSize());
+ assertFalse("is large", ol.isLarge());
+ assertNotNull(ol.getCachedBytes());
+ assertTrue(Arrays.equals(data3, ol.getCachedBytes()));
+
+ ObjectStream in = ol.openStream();
+ assertNotNull("have stream", in);
+ assertEquals(Constants.OBJ_BLOB, in.getType());
+ assertEquals(data3.length, in.getSize());
+ byte[] act = new byte[data3.length];
+ IO.readFully(in, act, 0, data3.length);
+ assertTrue("same content", Arrays.equals(act, data3));
+ assertEquals("stream at EOF", -1, in.read());
+ in.close();
+ }
+
+ public void testDelta_LargeObjectChain() throws Exception {
+ ObjectInserter.Formatter fmt = new ObjectInserter.Formatter();
+ byte[] data0 = new byte[ObjectLoader.STREAM_THRESHOLD + 5];
+ Arrays.fill(data0, (byte) 0xf3);
+ ObjectId id0 = fmt.idFor(Constants.OBJ_BLOB, data0);
+
+ TemporaryBuffer.Heap pack = new TemporaryBuffer.Heap(64 * 1024);
+ packHeader(pack, 4);
+ objectHeader(pack, Constants.OBJ_BLOB, data0.length);
+ deflate(pack, data0);
+
+ byte[] data1 = clone(0x01, data0);
+ byte[] delta1 = delta(data0, data1);
+ ObjectId id1 = fmt.idFor(Constants.OBJ_BLOB, data1);
+ objectHeader(pack, Constants.OBJ_REF_DELTA, delta1.length);
+ id0.copyRawTo(pack);
+ deflate(pack, delta1);
+
+ byte[] data2 = clone(0x02, data1);
+ byte[] delta2 = delta(data1, data2);
+ ObjectId id2 = fmt.idFor(Constants.OBJ_BLOB, data2);
+ objectHeader(pack, Constants.OBJ_REF_DELTA, delta2.length);
+ id1.copyRawTo(pack);
+ deflate(pack, delta2);
+
+ byte[] data3 = clone(0x03, data2);
+ byte[] delta3 = delta(data2, data3);
+ ObjectId id3 = fmt.idFor(Constants.OBJ_BLOB, data3);
+ objectHeader(pack, Constants.OBJ_REF_DELTA, delta3.length);
+ id2.copyRawTo(pack);
+ deflate(pack, delta3);
+
+ digest(pack);
+ final byte[] raw = pack.toByteArray();
+ IndexPack ip = IndexPack.create(repo, new ByteArrayInputStream(raw));
+ ip.setFixThin(true);
+ ip.index(NullProgressMonitor.INSTANCE);
+ ip.renameAndOpenPack();
+
+ assertTrue("has blob", wc.has(id3));
+
+ ObjectLoader ol = wc.open(id3);
+ assertNotNull("created loader", ol);
+ assertEquals(Constants.OBJ_BLOB, ol.getType());
+ assertEquals(data3.length, ol.getSize());
+ assertTrue("is large", ol.isLarge());
+ try {
+ ol.getCachedBytes();
+ fail("Should have thrown LargeObjectException");
+ } catch (LargeObjectException tooBig) {
+ assertEquals(id3.name(), tooBig.getMessage());
+ }
+
+ ObjectStream in = ol.openStream();
+ assertNotNull("have stream", in);
+ assertEquals(Constants.OBJ_BLOB, in.getType());
+ assertEquals(data3.length, in.getSize());
+ byte[] act = new byte[data3.length];
+ IO.readFully(in, act, 0, data3.length);
+ assertTrue("same content", Arrays.equals(act, data3));
+ assertEquals("stream at EOF", -1, in.read());
+ in.close();
+ }
+
+ public void testDelta_LargeInstructionStream() throws Exception {
+ ObjectInserter.Formatter fmt = new ObjectInserter.Formatter();
+ byte[] data0 = new byte[32];
+ Arrays.fill(data0, (byte) 0xf3);
+ ObjectId id0 = fmt.idFor(Constants.OBJ_BLOB, data0);
+
+ byte[] data3 = rng.nextBytes(ObjectLoader.STREAM_THRESHOLD + 5);
+ ByteArrayOutputStream tmp = new ByteArrayOutputStream();
+ DeltaEncoder de = new DeltaEncoder(tmp, data0.length, data3.length);
+ de.insert(data3, 0, data3.length);
+ byte[] delta3 = tmp.toByteArray();
+ assertTrue(delta3.length > ObjectLoader.STREAM_THRESHOLD);
+
+ TemporaryBuffer.Heap pack = new TemporaryBuffer.Heap(64 * 1024);
+ packHeader(pack, 2);
+ objectHeader(pack, Constants.OBJ_BLOB, data0.length);
+ deflate(pack, data0);
+
+ ObjectId id3 = fmt.idFor(Constants.OBJ_BLOB, data3);
+ objectHeader(pack, Constants.OBJ_REF_DELTA, delta3.length);
+ id0.copyRawTo(pack);
+ deflate(pack, delta3);
+
+ digest(pack);
+ final byte[] raw = pack.toByteArray();
+ IndexPack ip = IndexPack.create(repo, new ByteArrayInputStream(raw));
+ ip.setFixThin(true);
+ ip.index(NullProgressMonitor.INSTANCE);
+ ip.renameAndOpenPack();
+
+ assertTrue("has blob", wc.has(id3));
+
+ ObjectLoader ol = wc.open(id3);
+ assertNotNull("created loader", ol);
+ assertEquals(Constants.OBJ_BLOB, ol.getType());
+ assertEquals(data3.length, ol.getSize());
+ assertTrue("is large", ol.isLarge());
+ try {
+ ol.getCachedBytes();
+ fail("Should have thrown LargeObjectException");
+ } catch (LargeObjectException tooBig) {
+ assertEquals(id3.name(), tooBig.getMessage());
+ }
+
+ ObjectStream in = ol.openStream();
+ assertNotNull("have stream", in);
+ assertEquals(Constants.OBJ_BLOB, in.getType());
+ assertEquals(data3.length, in.getSize());
+ byte[] act = new byte[data3.length];
+ IO.readFully(in, act, 0, data3.length);
+ assertTrue("same content", Arrays.equals(act, data3));
+ assertEquals("stream at EOF", -1, in.read());
+ in.close();
+ }
+
+ private byte[] clone(int first, byte[] base) {
+ byte[] r = new byte[base.length];
+ System.arraycopy(base, 1, r, 1, r.length - 1);
+ r[0] = (byte) first;
+ return r;
+ }
+
+ private byte[] delta(byte[] base, byte[] dest) throws IOException {
+ ByteArrayOutputStream tmp = new ByteArrayOutputStream();
+ DeltaEncoder de = new DeltaEncoder(tmp, base.length, dest.length);
+ de.insert(dest, 0, 1);
+ de.copy(1, base.length - 1);
+ return tmp.toByteArray();
+ }
+
+ private void packHeader(TemporaryBuffer.Heap pack, int cnt)
+ throws IOException {
+ final byte[] hdr = new byte[8];
+ NB.encodeInt32(hdr, 0, 2);
+ NB.encodeInt32(hdr, 4, cnt);
+ pack.write(Constants.PACK_SIGNATURE);
+ pack.write(hdr, 0, 8);
+ }
+
+ private void objectHeader(TemporaryBuffer.Heap pack, int type, int sz)
+ throws IOException {
+ byte[] buf = new byte[8];
+ int nextLength = sz >>> 4;
+ buf[0] = (byte) ((nextLength > 0 ? 0x80 : 0x00) | (type << 4) | (sz & 0x0F));
+ sz = nextLength;
+ int n = 1;
+ while (sz > 0) {
+ nextLength >>>= 7;
+ buf[n++] = (byte) ((nextLength > 0 ? 0x80 : 0x00) | (sz & 0x7F));
+ sz = nextLength;
+ }
+ pack.write(buf, 0, n);
+ }
+
+ private void deflate(TemporaryBuffer.Heap pack, final byte[] content)
+ throws IOException {
+ final Deflater deflater = new Deflater();
+ final byte[] buf = new byte[128];
+ deflater.setInput(content, 0, content.length);
+ deflater.finish();
+ do {
+ final int n = deflater.deflate(buf, 0, buf.length);
+ if (n > 0)
+ pack.write(buf, 0, n);
+ } while (!deflater.finished());
+ deflater.end();
+ }
+
+ private void digest(TemporaryBuffer.Heap buf) throws IOException {
+ MessageDigest md = Constants.newMessageDigest();
+ md.update(buf.toByteArray());
+ buf.write(md.digest());
+ }
+}
diff --git a/org.eclipse.jgit.test/tst/org/eclipse/jgit/lib/PackIndexTestCase.java b/org.eclipse.jgit.test/tst/org/eclipse/jgit/storage/file/PackIndexTestCase.java
index d1c5c5eccd..9884142e5c 100644
--- a/org.eclipse.jgit.test/tst/org/eclipse/jgit/lib/PackIndexTestCase.java
+++ b/org.eclipse.jgit.test/tst/org/eclipse/jgit/storage/file/PackIndexTestCase.java
@@ -41,14 +41,15 @@
* ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
*/
-package org.eclipse.jgit.lib;
+package org.eclipse.jgit.storage.file;
import java.io.File;
import java.util.Iterator;
import java.util.NoSuchElementException;
import org.eclipse.jgit.errors.MissingObjectException;
-import org.eclipse.jgit.lib.PackIndex.MutableEntry;
+import org.eclipse.jgit.lib.RepositoryTestCase;
+import org.eclipse.jgit.storage.file.PackIndex.MutableEntry;
public abstract class PackIndexTestCase extends RepositoryTestCase {
diff --git a/org.eclipse.jgit.test/tst/org/eclipse/jgit/lib/PackIndexV1Test.java b/org.eclipse.jgit.test/tst/org/eclipse/jgit/storage/file/PackIndexV1Test.java
index f3082fb294..303eeff72d 100644
--- a/org.eclipse.jgit.test/tst/org/eclipse/jgit/lib/PackIndexV1Test.java
+++ b/org.eclipse.jgit.test/tst/org/eclipse/jgit/storage/file/PackIndexV1Test.java
@@ -43,11 +43,12 @@
* ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
*/
-package org.eclipse.jgit.lib;
+package org.eclipse.jgit.storage.file;
import java.io.File;
import org.eclipse.jgit.errors.MissingObjectException;
+import org.eclipse.jgit.lib.ObjectId;
import org.eclipse.jgit.util.JGitTestUtil;
public class PackIndexV1Test extends PackIndexTestCase {
diff --git a/org.eclipse.jgit.test/tst/org/eclipse/jgit/lib/PackIndexV2Test.java b/org.eclipse.jgit.test/tst/org/eclipse/jgit/storage/file/PackIndexV2Test.java
index c5669f9d24..2d3ec7b729 100644
--- a/org.eclipse.jgit.test/tst/org/eclipse/jgit/lib/PackIndexV2Test.java
+++ b/org.eclipse.jgit.test/tst/org/eclipse/jgit/storage/file/PackIndexV2Test.java
@@ -43,11 +43,12 @@
* ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
*/
-package org.eclipse.jgit.lib;
+package org.eclipse.jgit.storage.file;
import java.io.File;
import org.eclipse.jgit.errors.MissingObjectException;
+import org.eclipse.jgit.lib.ObjectId;
import org.eclipse.jgit.util.JGitTestUtil;
public class PackIndexV2Test extends PackIndexTestCase {
diff --git a/org.eclipse.jgit.test/tst/org/eclipse/jgit/lib/PackReverseIndexTest.java b/org.eclipse.jgit.test/tst/org/eclipse/jgit/storage/file/PackReverseIndexTest.java
index 19b705813f..07a40a425f 100644
--- a/org.eclipse.jgit.test/tst/org/eclipse/jgit/lib/PackReverseIndexTest.java
+++ b/org.eclipse.jgit.test/tst/org/eclipse/jgit/storage/file/PackReverseIndexTest.java
@@ -42,10 +42,11 @@
* ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
*/
-package org.eclipse.jgit.lib;
+package org.eclipse.jgit.storage.file;
import org.eclipse.jgit.errors.CorruptObjectException;
-import org.eclipse.jgit.lib.PackIndex.MutableEntry;
+import org.eclipse.jgit.lib.RepositoryTestCase;
+import org.eclipse.jgit.storage.file.PackIndex.MutableEntry;
import org.eclipse.jgit.util.JGitTestUtil;
public class PackReverseIndexTest extends RepositoryTestCase {
diff --git a/org.eclipse.jgit.test/tst/org/eclipse/jgit/lib/PackWriterTest.java b/org.eclipse.jgit.test/tst/org/eclipse/jgit/storage/file/PackWriterTest.java
index 76b663a29b..9e663d7b4c 100644
--- a/org.eclipse.jgit.test/tst/org/eclipse/jgit/lib/PackWriterTest.java
+++ b/org.eclipse.jgit.test/tst/org/eclipse/jgit/storage/file/PackWriterTest.java
@@ -41,7 +41,7 @@
* ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
*/
-package org.eclipse.jgit.lib;
+package org.eclipse.jgit.storage.file;
import java.io.ByteArrayInputStream;
import java.io.ByteArrayOutputStream;
@@ -59,9 +59,14 @@ import java.util.LinkedList;
import java.util.List;
import org.eclipse.jgit.errors.MissingObjectException;
-import org.eclipse.jgit.lib.PackIndex.MutableEntry;
+import org.eclipse.jgit.lib.NullProgressMonitor;
+import org.eclipse.jgit.lib.ObjectId;
+import org.eclipse.jgit.lib.SampleDataRepositoryTestCase;
+import org.eclipse.jgit.lib.TextProgressMonitor;
import org.eclipse.jgit.revwalk.RevObject;
import org.eclipse.jgit.revwalk.RevWalk;
+import org.eclipse.jgit.storage.file.PackIndex.MutableEntry;
+import org.eclipse.jgit.storage.pack.PackWriter;
import org.eclipse.jgit.transport.IndexPack;
import org.eclipse.jgit.util.JGitTestUtil;
@@ -91,7 +96,7 @@ public class PackWriterTest extends SampleDataRepositoryTestCase {
packBase = new File(trash, "tmp_pack");
packFile = new File(trash, "tmp_pack.pack");
indexFile = new File(trash, "tmp_pack.idx");
- writer = new PackWriter(db, new TextProgressMonitor());
+ writer = new PackWriter(db);
}
/**
@@ -238,7 +243,7 @@ public class PackWriterTest extends SampleDataRepositoryTestCase {
* @throws IOException
*/
public void testWritePack2DeltasCRC32Copy() throws IOException {
- final File packDir = new File(db.getObjectsDirectory(), "pack");
+ final File packDir = new File(db.getObjectDatabase().getDirectory(), "pack");
final File crc32Pack = new File(packDir,
"pack-34be9032ac282b11fa9babdc2b2a93ca996c9c2f.pack");
final File crc32Idx = new File(packDir,
@@ -476,17 +481,21 @@ public class PackWriterTest extends SampleDataRepositoryTestCase {
final Collection<ObjectId> uninterestings, final boolean thin,
final boolean ignoreMissingUninteresting)
throws MissingObjectException, IOException {
+ NullProgressMonitor m = NullProgressMonitor.INSTANCE;
writer.setThin(thin);
writer.setIgnoreMissingUninteresting(ignoreMissingUninteresting);
- writer.preparePack(interestings, uninterestings);
- writer.writePack(os);
+ writer.preparePack(m, interestings, uninterestings);
+ writer.writePack(m, m, os);
+ writer.release();
verifyOpenPack(thin);
}
private void createVerifyOpenPack(final Iterator<RevObject> objectSource)
throws MissingObjectException, IOException {
+ NullProgressMonitor m = NullProgressMonitor.INSTANCE;
writer.preparePack(objectSource);
- writer.writePack(os);
+ writer.writePack(m, m, os);
+ writer.release();
verifyOpenPack(false);
}
diff --git a/org.eclipse.jgit.test/tst/org/eclipse/jgit/lib/RefDirectoryTest.java b/org.eclipse.jgit.test/tst/org/eclipse/jgit/storage/file/RefDirectoryTest.java
index a2812901bc..6e98541603 100644
--- a/org.eclipse.jgit.test/tst/org/eclipse/jgit/lib/RefDirectoryTest.java
+++ b/org.eclipse.jgit.test/tst/org/eclipse/jgit/storage/file/RefDirectoryTest.java
@@ -41,7 +41,7 @@
* ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
*/
-package org.eclipse.jgit.lib;
+package org.eclipse.jgit.storage.file;
import static org.eclipse.jgit.lib.Constants.HEAD;
import static org.eclipse.jgit.lib.Constants.R_HEADS;
@@ -55,6 +55,10 @@ import java.util.Map;
import org.eclipse.jgit.junit.LocalDiskRepositoryTestCase;
import org.eclipse.jgit.junit.TestRepository;
+import org.eclipse.jgit.lib.AnyObjectId;
+import org.eclipse.jgit.lib.Ref;
+import org.eclipse.jgit.lib.RefDatabase;
+import org.eclipse.jgit.lib.Repository;
import org.eclipse.jgit.revwalk.RevCommit;
import org.eclipse.jgit.revwalk.RevTag;
diff --git a/org.eclipse.jgit.test/tst/org/eclipse/jgit/lib/RefUpdateTest.java b/org.eclipse.jgit.test/tst/org/eclipse/jgit/storage/file/RefUpdateTest.java
index 8a9bb52633..875c2e96f5 100644
--- a/org.eclipse.jgit.test/tst/org/eclipse/jgit/lib/RefUpdateTest.java
+++ b/org.eclipse.jgit.test/tst/org/eclipse/jgit/storage/file/RefUpdateTest.java
@@ -43,7 +43,7 @@
* ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
*/
-package org.eclipse.jgit.lib;
+package org.eclipse.jgit.storage.file;
import java.io.File;
import java.io.IOException;
@@ -51,9 +51,22 @@ import java.util.List;
import java.util.Map;
import java.util.Map.Entry;
+import org.eclipse.jgit.lib.Constants;
+import org.eclipse.jgit.lib.ObjectId;
+import org.eclipse.jgit.lib.PersonIdent;
+import org.eclipse.jgit.lib.Ref;
+import org.eclipse.jgit.lib.RefRename;
+import org.eclipse.jgit.lib.RefUpdate;
+import org.eclipse.jgit.lib.Repository;
+import org.eclipse.jgit.lib.SampleDataRepositoryTestCase;
import org.eclipse.jgit.lib.RefUpdate.Result;
import org.eclipse.jgit.revwalk.RevCommit;
import org.eclipse.jgit.revwalk.RevWalk;
+import org.eclipse.jgit.storage.file.FileRepository;
+import org.eclipse.jgit.storage.file.LockFile;
+import org.eclipse.jgit.storage.file.RefDirectory;
+import org.eclipse.jgit.storage.file.RefDirectoryUpdate;
+import org.eclipse.jgit.storage.file.ReflogReader;
public class RefUpdateTest extends SampleDataRepositoryTestCase {
@@ -103,14 +116,14 @@ public class RefUpdateTest extends SampleDataRepositoryTestCase {
assertNotSame(newid, r.getObjectId());
assertSame(ObjectId.class, r.getObjectId().getClass());
assertEquals(newid.copy(), r.getObjectId());
- List<org.eclipse.jgit.lib.ReflogReader.Entry> reverseEntries1 = db.getReflogReader("refs/heads/abc").getReverseEntries();
- org.eclipse.jgit.lib.ReflogReader.Entry entry1 = reverseEntries1.get(0);
+ List<org.eclipse.jgit.storage.file.ReflogReader.Entry> reverseEntries1 = db.getReflogReader("refs/heads/abc").getReverseEntries();
+ org.eclipse.jgit.storage.file.ReflogReader.Entry entry1 = reverseEntries1.get(0);
assertEquals(1, reverseEntries1.size());
assertEquals(ObjectId.zeroId(), entry1.getOldId());
assertEquals(r.getObjectId(), entry1.getNewId());
assertEquals(new PersonIdent(db).toString(), entry1.getWho().toString());
assertEquals("", entry1.getComment());
- List<org.eclipse.jgit.lib.ReflogReader.Entry> reverseEntries2 = db.getReflogReader("HEAD").getReverseEntries();
+ List<org.eclipse.jgit.storage.file.ReflogReader.Entry> reverseEntries2 = db.getReflogReader("HEAD").getReverseEntries();
assertEquals(0, reverseEntries2.size());
}
@@ -326,7 +339,7 @@ public class RefUpdateTest extends SampleDataRepositoryTestCase {
// the branch HEAD referred to is left untouched
assertEquals(pid, db.resolve("refs/heads/master"));
ReflogReader reflogReader = new ReflogReader(db, "HEAD");
- org.eclipse.jgit.lib.ReflogReader.Entry e = reflogReader.getReverseEntries().get(0);
+ org.eclipse.jgit.storage.file.ReflogReader.Entry e = reflogReader.getReverseEntries().get(0);
assertEquals(pid, e.getOldId());
assertEquals(ppid, e.getNewId());
assertEquals("GIT_COMMITTER_EMAIL", e.getWho().getEmailAddress());
@@ -355,7 +368,7 @@ public class RefUpdateTest extends SampleDataRepositoryTestCase {
// the branch HEAD referred to is left untouched
assertNull(db.resolve("refs/heads/unborn"));
ReflogReader reflogReader = new ReflogReader(db, "HEAD");
- org.eclipse.jgit.lib.ReflogReader.Entry e = reflogReader.getReverseEntries().get(0);
+ org.eclipse.jgit.storage.file.ReflogReader.Entry e = reflogReader.getReverseEntries().get(0);
assertEquals(ObjectId.zeroId(), e.getOldId());
assertEquals(ppid, e.getNewId());
assertEquals("GIT_COMMITTER_EMAIL", e.getWho().getEmailAddress());
@@ -664,7 +677,7 @@ public class RefUpdateTest extends SampleDataRepositoryTestCase {
// Create new Repository instance, to reread caches and make sure our
// assumptions are persistent.
- Repository ndb = new Repository(db.getDirectory());
+ Repository ndb = new FileRepository(db.getDirectory());
assertEquals(rb2, ndb.resolve("refs/heads/new/name"));
assertNull(ndb.resolve("refs/heads/b"));
}
@@ -677,9 +690,9 @@ public class RefUpdateTest extends SampleDataRepositoryTestCase {
ObjectId oldHeadId = db.resolve(Constants.HEAD);
writeReflog(db, oldfromId, oldfromId, "Just a message",
fromName);
- List<org.eclipse.jgit.lib.ReflogReader.Entry> oldFromLog = db
+ List<org.eclipse.jgit.storage.file.ReflogReader.Entry> oldFromLog = db
.getReflogReader(fromName).getReverseEntries();
- List<org.eclipse.jgit.lib.ReflogReader.Entry> oldHeadLog = oldHeadId != null ? db
+ List<org.eclipse.jgit.storage.file.ReflogReader.Entry> oldHeadLog = oldHeadId != null ? db
.getReflogReader(Constants.HEAD).getReverseEntries() : null;
assertTrue("internal check, we have a log", new File(db.getDirectory(),
diff --git a/org.eclipse.jgit.test/tst/org/eclipse/jgit/lib/ReflogReaderTest.java b/org.eclipse.jgit.test/tst/org/eclipse/jgit/storage/file/ReflogReaderTest.java
index 6144851fcd..1d268a4740 100644
--- a/org.eclipse.jgit.test/tst/org/eclipse/jgit/lib/ReflogReaderTest.java
+++ b/org.eclipse.jgit.test/tst/org/eclipse/jgit/storage/file/ReflogReaderTest.java
@@ -42,7 +42,7 @@
* ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
*/
-package org.eclipse.jgit.lib;
+package org.eclipse.jgit.storage.file;
import java.io.File;
import java.io.FileNotFoundException;
@@ -51,7 +51,10 @@ import java.io.IOException;
import java.text.SimpleDateFormat;
import java.util.List;
-import org.eclipse.jgit.lib.ReflogReader.Entry;
+import org.eclipse.jgit.lib.ObjectId;
+import org.eclipse.jgit.lib.PersonIdent;
+import org.eclipse.jgit.lib.SampleDataRepositoryTestCase;
+import org.eclipse.jgit.storage.file.ReflogReader.Entry;
public class ReflogReaderTest extends SampleDataRepositoryTestCase {
diff --git a/org.eclipse.jgit.test/tst/org/eclipse/jgit/lib/RepositorySetupWorkDirTest.java b/org.eclipse.jgit.test/tst/org/eclipse/jgit/storage/file/RepositorySetupWorkDirTest.java
index 6e5e0054b8..4f6d5b3bd6 100644
--- a/org.eclipse.jgit.test/tst/org/eclipse/jgit/lib/RepositorySetupWorkDirTest.java
+++ b/org.eclipse.jgit.test/tst/org/eclipse/jgit/storage/file/RepositorySetupWorkDirTest.java
@@ -42,12 +42,17 @@
* ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
*/
-package org.eclipse.jgit.lib;
+package org.eclipse.jgit.storage.file;
import java.io.File;
import java.io.IOException;
+import org.eclipse.jgit.errors.ConfigInvalidException;
+import org.eclipse.jgit.errors.NoWorkTreeException;
import org.eclipse.jgit.junit.LocalDiskRepositoryTestCase;
+import org.eclipse.jgit.lib.ConfigConstants;
+import org.eclipse.jgit.lib.Constants;
+import org.eclipse.jgit.lib.Repository;
/**
* Tests for setting up the working directory when creating a Repository
@@ -57,12 +62,12 @@ public class RepositorySetupWorkDirTest extends LocalDiskRepositoryTestCase {
public void testIsBare_CreateRepositoryFromArbitraryGitDir()
throws Exception {
File gitDir = getFile("workdir");
- assertTrue(new Repository(gitDir).isBare());
+ assertTrue(new FileRepository(gitDir).isBare());
}
public void testNotBare_CreateRepositoryFromDotGitGitDir() throws Exception {
File gitDir = getFile("workdir", Constants.DOT_GIT);
- Repository repo = new Repository(gitDir);
+ Repository repo = new FileRepository(gitDir);
assertFalse(repo.isBare());
assertWorkdirPath(repo, "workdir");
assertGitdirPath(repo, "workdir", Constants.DOT_GIT);
@@ -71,14 +76,14 @@ public class RepositorySetupWorkDirTest extends LocalDiskRepositoryTestCase {
public void testWorkdirIsParentDir_CreateRepositoryFromDotGitGitDir()
throws Exception {
File gitDir = getFile("workdir", Constants.DOT_GIT);
- Repository repo = new Repository(gitDir);
- String workdir = repo.getWorkDir().getName();
+ Repository repo = new FileRepository(gitDir);
+ String workdir = repo.getWorkTree().getName();
assertEquals(workdir, "workdir");
}
public void testNotBare_CreateRepositoryFromWorkDirOnly() throws Exception {
File workdir = getFile("workdir", "repo");
- Repository repo = new Repository(null, workdir);
+ FileRepository repo = new FileRepositoryBuilder().setWorkTree(workdir).build();
assertFalse(repo.isBare());
assertWorkdirPath(repo, "workdir", "repo");
assertGitdirPath(repo, "workdir", "repo", Constants.DOT_GIT);
@@ -87,7 +92,7 @@ public class RepositorySetupWorkDirTest extends LocalDiskRepositoryTestCase {
public void testWorkdirIsDotGit_CreateRepositoryFromWorkDirOnly()
throws Exception {
File workdir = getFile("workdir", "repo");
- Repository repo = new Repository(null, workdir);
+ FileRepository repo = new FileRepositoryBuilder().setWorkTree(workdir).build();
assertGitdirPath(repo, "workdir", "repo", Constants.DOT_GIT);
}
@@ -96,7 +101,7 @@ public class RepositorySetupWorkDirTest extends LocalDiskRepositoryTestCase {
File gitDir = getFile("workdir", "repoWithConfig");
File workTree = getFile("workdir", "treeRoot");
setWorkTree(gitDir, workTree);
- Repository repo = new Repository(gitDir, null);
+ FileRepository repo = new FileRepositoryBuilder().setGitDir(gitDir).build();
assertFalse(repo.isBare());
assertWorkdirPath(repo, "workdir", "treeRoot");
assertGitdirPath(repo, "workdir", "repoWithConfig");
@@ -106,7 +111,7 @@ public class RepositorySetupWorkDirTest extends LocalDiskRepositoryTestCase {
throws Exception {
File gitDir = getFile("workdir", "repoWithConfig");
setBare(gitDir, true);
- Repository repo = new Repository(gitDir, null);
+ FileRepository repo = new FileRepositoryBuilder().setGitDir(gitDir).build();
assertTrue(repo.isBare());
}
@@ -114,7 +119,7 @@ public class RepositorySetupWorkDirTest extends LocalDiskRepositoryTestCase {
throws Exception {
File gitDir = getFile("workdir", "repoWithBareConfigTrue", "child");
setBare(gitDir, false);
- Repository repo = new Repository(gitDir, null);
+ FileRepository repo = new FileRepositoryBuilder().setGitDir(gitDir).build();
assertWorkdirPath(repo, "workdir", "repoWithBareConfigTrue");
}
@@ -122,27 +127,18 @@ public class RepositorySetupWorkDirTest extends LocalDiskRepositoryTestCase {
throws Exception {
File gitDir = getFile("workdir", "repoWithBareConfigFalse", "child");
setBare(gitDir, false);
- Repository repo = new Repository(gitDir, null);
+ FileRepository repo = new FileRepositoryBuilder().setGitDir(gitDir).build();
assertFalse(repo.isBare());
assertWorkdirPath(repo, "workdir", "repoWithBareConfigFalse");
assertGitdirPath(repo, "workdir", "repoWithBareConfigFalse", "child");
}
- public void testNotBare_MakeBareUnbareBySetWorkdir() throws Exception {
- File gitDir = getFile("gitDir");
- Repository repo = new Repository(gitDir);
- repo.setWorkDir(getFile("workingDir"));
- assertFalse(repo.isBare());
- assertWorkdirPath(repo, "workingDir");
- assertGitdirPath(repo, "gitDir");
- }
-
public void testExceptionThrown_BareRepoGetWorkDir() throws Exception {
File gitDir = getFile("workdir");
try {
- new Repository(gitDir).getWorkDir();
- fail("Expected IllegalStateException missing");
- } catch (IllegalStateException e) {
+ new FileRepository(gitDir).getWorkTree();
+ fail("Expected NoWorkTreeException missing");
+ } catch (NoWorkTreeException e) {
// expected
}
}
@@ -150,9 +146,9 @@ public class RepositorySetupWorkDirTest extends LocalDiskRepositoryTestCase {
public void testExceptionThrown_BareRepoGetIndex() throws Exception {
File gitDir = getFile("workdir");
try {
- new Repository(gitDir).getIndex();
- fail("Expected IllegalStateException missing");
- } catch (IllegalStateException e) {
+ new FileRepository(gitDir).getIndex();
+ fail("Expected NoWorkTreeException missing");
+ } catch (NoWorkTreeException e) {
// expected
}
}
@@ -160,9 +156,9 @@ public class RepositorySetupWorkDirTest extends LocalDiskRepositoryTestCase {
public void testExceptionThrown_BareRepoGetIndexFile() throws Exception {
File gitDir = getFile("workdir");
try {
- new Repository(gitDir).getIndexFile();
- fail("Expected Exception missing");
- } catch (IllegalStateException e) {
+ new FileRepository(gitDir).getIndexFile();
+ fail("Expected NoWorkTreeException missing");
+ } catch (NoWorkTreeException e) {
// expected
}
}
@@ -176,20 +172,28 @@ public class RepositorySetupWorkDirTest extends LocalDiskRepositoryTestCase {
return result;
}
- private void setBare(File gitDir, boolean bare) throws IOException {
- Repository repo = new Repository(gitDir, null);
- repo.getConfig().setBoolean(ConfigConstants.CONFIG_CORE_SECTION, null,
+ private void setBare(File gitDir, boolean bare) throws IOException,
+ ConfigInvalidException {
+ FileBasedConfig cfg = configFor(gitDir);
+ cfg.setBoolean(ConfigConstants.CONFIG_CORE_SECTION, null,
ConfigConstants.CONFIG_KEY_BARE, bare);
- repo.getConfig().save();
+ cfg.save();
+ }
+
+ private void setWorkTree(File gitDir, File workTree) throws IOException,
+ ConfigInvalidException {
+ String path = workTree.getAbsolutePath();
+ FileBasedConfig cfg = configFor(gitDir);
+ cfg.setString(ConfigConstants.CONFIG_CORE_SECTION, null,
+ ConfigConstants.CONFIG_KEY_WORKTREE, path);
+ cfg.save();
}
- private void setWorkTree(File gitDir, File workTree) throws IOException {
- Repository repo = new Repository(gitDir, null);
- repo.getConfig()
- .setString(ConfigConstants.CONFIG_CORE_SECTION, null,
- ConfigConstants.CONFIG_KEY_WORKTREE,
- workTree.getAbsolutePath());
- repo.getConfig().save();
+ private FileBasedConfig configFor(File gitDir) throws IOException,
+ ConfigInvalidException {
+ FileBasedConfig cfg = new FileBasedConfig(new File(gitDir, "config"));
+ cfg.load();
+ return cfg;
}
private void assertGitdirPath(Repository repo, String... expected)
@@ -202,7 +206,7 @@ public class RepositorySetupWorkDirTest extends LocalDiskRepositoryTestCase {
private void assertWorkdirPath(Repository repo, String... expected)
throws IOException {
File exp = getFile(expected).getCanonicalFile();
- File act = repo.getWorkDir().getCanonicalFile();
+ File act = repo.getWorkTree().getCanonicalFile();
assertEquals("Wrong working Directory", exp, act);
}
}
diff --git a/org.eclipse.jgit.test/tst/org/eclipse/jgit/lib/T0003_Basic.java b/org.eclipse.jgit.test/tst/org/eclipse/jgit/storage/file/T0003_Basic.java
index ce8a79ef96..477b0dfb50 100644
--- a/org.eclipse.jgit.test/tst/org/eclipse/jgit/lib/T0003_Basic.java
+++ b/org.eclipse.jgit.test/tst/org/eclipse/jgit/storage/file/T0003_Basic.java
@@ -43,7 +43,7 @@
* ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
*/
-package org.eclipse.jgit.lib;
+package org.eclipse.jgit.storage.file;
import java.io.ByteArrayInputStream;
import java.io.File;
@@ -53,7 +53,23 @@ import java.io.FileWriter;
import java.io.IOException;
import java.io.PrintWriter;
+import org.eclipse.jgit.JGitText;
import org.eclipse.jgit.errors.ConfigInvalidException;
+import org.eclipse.jgit.lib.Commit;
+import org.eclipse.jgit.lib.Config;
+import org.eclipse.jgit.lib.Constants;
+import org.eclipse.jgit.lib.FileTreeEntry;
+import org.eclipse.jgit.lib.ObjectDatabase;
+import org.eclipse.jgit.lib.ObjectId;
+import org.eclipse.jgit.lib.ObjectWriter;
+import org.eclipse.jgit.lib.PersonIdent;
+import org.eclipse.jgit.lib.RefUpdate;
+import org.eclipse.jgit.lib.Repository;
+import org.eclipse.jgit.lib.SampleDataRepositoryTestCase;
+import org.eclipse.jgit.lib.Tag;
+import org.eclipse.jgit.lib.Tree;
+import org.eclipse.jgit.lib.TreeEntry;
+import org.eclipse.jgit.lib.WriteTree;
public class T0003_Basic extends SampleDataRepositoryTestCase {
public void test001_Initalize() {
@@ -80,11 +96,11 @@ public class T0003_Basic extends SampleDataRepositoryTestCase {
public void test000_openRepoBadArgs() throws IOException {
try {
- new Repository(null, null);
+ new FileRepositoryBuilder().build();
fail("Must pass either GIT_DIR or GIT_WORK_TREE");
} catch (IllegalArgumentException e) {
assertEquals(
- "Either GIT_DIR or GIT_WORK_TREE must be passed to Repository constructor",
+ JGitText.get().eitherGitDirOrWorkTreeRequired,
e.getMessage());
}
}
@@ -97,16 +113,16 @@ public class T0003_Basic extends SampleDataRepositoryTestCase {
*/
public void test000_openrepo_default_gitDirSet() throws IOException {
File repo1Parent = new File(trash.getParentFile(), "r1");
- Repository repo1initial = new Repository(new File(repo1Parent, Constants.DOT_GIT));
+ Repository repo1initial = new FileRepository(new File(repo1Parent, Constants.DOT_GIT));
repo1initial.create();
repo1initial.close();
File theDir = new File(repo1Parent, Constants.DOT_GIT);
- Repository r = new Repository(theDir, null);
+ FileRepository r = new FileRepositoryBuilder().setGitDir(theDir).build();
assertEqualsPath(theDir, r.getDirectory());
- assertEqualsPath(repo1Parent, r.getWorkDir());
+ assertEqualsPath(repo1Parent, r.getWorkTree());
assertEqualsPath(new File(theDir, "index"), r.getIndexFile());
- assertEqualsPath(new File(theDir, "objects"), r.getObjectsDirectory());
+ assertEqualsPath(new File(theDir, "objects"), r.getObjectDatabase().getDirectory());
}
/**
@@ -117,16 +133,17 @@ public class T0003_Basic extends SampleDataRepositoryTestCase {
*/
public void test000_openrepo_default_gitDirAndWorkTreeSet() throws IOException {
File repo1Parent = new File(trash.getParentFile(), "r1");
- Repository repo1initial = new Repository(new File(repo1Parent, Constants.DOT_GIT));
+ Repository repo1initial = new FileRepository(new File(repo1Parent, Constants.DOT_GIT));
repo1initial.create();
repo1initial.close();
File theDir = new File(repo1Parent, Constants.DOT_GIT);
- Repository r = new Repository(theDir, repo1Parent.getParentFile());
+ FileRepository r = new FileRepositoryBuilder().setGitDir(theDir)
+ .setWorkTree(repo1Parent.getParentFile()).build();
assertEqualsPath(theDir, r.getDirectory());
- assertEqualsPath(repo1Parent.getParentFile(), r.getWorkDir());
+ assertEqualsPath(repo1Parent.getParentFile(), r.getWorkTree());
assertEqualsPath(new File(theDir, "index"), r.getIndexFile());
- assertEqualsPath(new File(theDir, "objects"), r.getObjectsDirectory());
+ assertEqualsPath(new File(theDir, "objects"), r.getObjectDatabase().getDirectory());
}
/**
@@ -137,16 +154,16 @@ public class T0003_Basic extends SampleDataRepositoryTestCase {
*/
public void test000_openrepo_default_workDirSet() throws IOException {
File repo1Parent = new File(trash.getParentFile(), "r1");
- Repository repo1initial = new Repository(new File(repo1Parent, Constants.DOT_GIT));
+ Repository repo1initial = new FileRepository(new File(repo1Parent, Constants.DOT_GIT));
repo1initial.create();
repo1initial.close();
File theDir = new File(repo1Parent, Constants.DOT_GIT);
- Repository r = new Repository(null, repo1Parent);
+ FileRepository r = new FileRepositoryBuilder().setWorkTree(repo1Parent).build();
assertEqualsPath(theDir, r.getDirectory());
- assertEqualsPath(repo1Parent, r.getWorkDir());
+ assertEqualsPath(repo1Parent, r.getWorkTree());
assertEqualsPath(new File(theDir, "index"), r.getIndexFile());
- assertEqualsPath(new File(theDir, "objects"), r.getObjectsDirectory());
+ assertEqualsPath(new File(theDir, "objects"), r.getObjectDatabase().getDirectory());
}
/**
@@ -159,7 +176,7 @@ public class T0003_Basic extends SampleDataRepositoryTestCase {
File repo1Parent = new File(trash.getParentFile(), "r1");
File workdir = new File(trash.getParentFile(), "rw");
workdir.mkdir();
- Repository repo1initial = new Repository(new File(repo1Parent, Constants.DOT_GIT));
+ FileRepository repo1initial = new FileRepository(new File(repo1Parent, Constants.DOT_GIT));
repo1initial.create();
repo1initial.getConfig().setString("core", null, "worktree",
workdir.getAbsolutePath());
@@ -167,11 +184,11 @@ public class T0003_Basic extends SampleDataRepositoryTestCase {
repo1initial.close();
File theDir = new File(repo1Parent, Constants.DOT_GIT);
- Repository r = new Repository(theDir, null);
+ FileRepository r = new FileRepositoryBuilder().setGitDir(theDir).build();
assertEqualsPath(theDir, r.getDirectory());
- assertEqualsPath(workdir, r.getWorkDir());
+ assertEqualsPath(workdir, r.getWorkTree());
assertEqualsPath(new File(theDir, "index"), r.getIndexFile());
- assertEqualsPath(new File(theDir, "objects"), r.getObjectsDirectory());
+ assertEqualsPath(new File(theDir, "objects"), r.getObjectDatabase().getDirectory());
}
/**
@@ -184,7 +201,7 @@ public class T0003_Basic extends SampleDataRepositoryTestCase {
File repo1Parent = new File(trash.getParentFile(), "r1");
File workdir = new File(trash.getParentFile(), "rw");
workdir.mkdir();
- Repository repo1initial = new Repository(new File(repo1Parent, Constants.DOT_GIT));
+ FileRepository repo1initial = new FileRepository(new File(repo1Parent, Constants.DOT_GIT));
repo1initial.create();
repo1initial.getConfig()
.setString("core", null, "worktree", "../../rw");
@@ -192,11 +209,11 @@ public class T0003_Basic extends SampleDataRepositoryTestCase {
repo1initial.close();
File theDir = new File(repo1Parent, Constants.DOT_GIT);
- Repository r = new Repository(theDir, null);
+ FileRepository r = new FileRepositoryBuilder().setGitDir(theDir).build();
assertEqualsPath(theDir, r.getDirectory());
- assertEqualsPath(workdir, r.getWorkDir());
+ assertEqualsPath(workdir, r.getWorkTree());
assertEqualsPath(new File(theDir, "index"), r.getIndexFile());
- assertEqualsPath(new File(theDir, "objects"), r.getObjectsDirectory());
+ assertEqualsPath(new File(theDir, "objects"), r.getObjectDatabase().getDirectory());
}
/**
@@ -210,18 +227,21 @@ public class T0003_Basic extends SampleDataRepositoryTestCase {
File repo1Parent = new File(trash.getParentFile(), "r1");
File indexFile = new File(trash, "idx");
File objDir = new File(trash, "../obj");
- File[] altObjDirs = new File[] { db.getObjectsDirectory() };
- Repository repo1initial = new Repository(new File(repo1Parent, Constants.DOT_GIT));
+ File altObjDir = db.getObjectDatabase().getDirectory();
+ Repository repo1initial = new FileRepository(new File(repo1Parent, Constants.DOT_GIT));
repo1initial.create();
repo1initial.close();
File theDir = new File(repo1Parent, Constants.DOT_GIT);
- Repository r = new Repository(theDir, null, objDir, altObjDirs,
- indexFile);
+ FileRepository r = new FileRepositoryBuilder() //
+ .setGitDir(theDir).setObjectDirectory(objDir) //
+ .addAlternateObjectDirectory(altObjDir) //
+ .setIndexFile(indexFile) //
+ .build();
assertEqualsPath(theDir, r.getDirectory());
- assertEqualsPath(theDir.getParentFile(), r.getWorkDir());
+ assertEqualsPath(theDir.getParentFile(), r.getWorkTree());
assertEqualsPath(indexFile, r.getIndexFile());
- assertEqualsPath(objDir, r.getObjectsDirectory());
+ assertEqualsPath(objDir, r.getObjectDatabase().getDirectory());
assertNotNull(r.mapCommit("6db9c2ebf75590eef973081736730a9ea169a0c4"));
// Must close or the default repo pack files created by this test gets
// locked via the alternate object directories on Windows.
@@ -283,7 +303,7 @@ public class T0003_Basic extends SampleDataRepositoryTestCase {
}
public void test005_ReadSimpleConfig() {
- final RepositoryConfig c = db.getConfig();
+ final Config c = db.getConfig();
assertNotNull(c);
assertEquals("0", c.getString("core", null, "repositoryformatversion"));
assertEquals("0", c.getString("CoRe", null, "REPOSITORYFoRmAtVeRsIoN"));
@@ -294,8 +314,8 @@ public class T0003_Basic extends SampleDataRepositoryTestCase {
public void test006_ReadUglyConfig() throws IOException,
ConfigInvalidException {
- final RepositoryConfig c = db.getConfig();
final File cfg = new File(db.getDirectory(), "config");
+ final FileBasedConfig c = new FileBasedConfig(cfg);
final FileWriter pw = new FileWriter(cfg);
final String configStr = " [core];comment\n\tfilemode = yes\n"
+ "[user]\n"
@@ -321,9 +341,9 @@ public class T0003_Basic extends SampleDataRepositoryTestCase {
}
public void test007_Open() throws IOException {
- final Repository db2 = new Repository(db.getDirectory());
+ final FileRepository db2 = new FileRepository(db.getDirectory());
assertEquals(db.getDirectory(), db2.getDirectory());
- assertEquals(db.getObjectsDirectory(), db2.getObjectsDirectory());
+ assertEquals(db.getObjectDatabase().getDirectory(), db2.getObjectDatabase().getDirectory());
assertNotSame(db.getConfig(), db2.getConfig());
}
@@ -337,7 +357,7 @@ public class T0003_Basic extends SampleDataRepositoryTestCase {
pw.close();
try {
- new Repository(db.getDirectory());
+ new FileRepository(db.getDirectory());
fail("incorrectly opened a bad repository");
} catch (IOException ioe) {
assertTrue(ioe.getMessage().indexOf("format") > 0);
@@ -345,11 +365,7 @@ public class T0003_Basic extends SampleDataRepositoryTestCase {
}
}
- public void test009_CreateCommitOldFormat() throws IOException,
- ConfigInvalidException {
- writeTrashFile(".git/config", "[core]\n" + "legacyHeaders=1\n");
- db.getConfig().load();
-
+ public void test009_CreateCommitOldFormat() throws IOException {
final Tree t = new Tree(db);
final FileTreeEntry f = t.addFile("i-am-a-file");
writeTrashFile(f.getName(), "and this is the data in me\n");
@@ -369,8 +385,10 @@ public class T0003_Basic extends SampleDataRepositoryTestCase {
assertEquals(cmtid, c.getCommitId());
// Verify the commit we just wrote is in the correct format.
- final XInputStream xis = new XInputStream(new FileInputStream(db
- .toFile(cmtid)));
+ ObjectDatabase odb = db.getObjectDatabase();
+ assertTrue("is ObjectDirectory", odb instanceof ObjectDirectory);
+ final XInputStream xis = new XInputStream(new FileInputStream(
+ ((ObjectDirectory) odb).fileFor(cmtid)));
try {
assertEquals(0x78, xis.readUInt8());
assertEquals(0x9c, xis.readUInt8());
@@ -724,10 +742,10 @@ public class T0003_Basic extends SampleDataRepositoryTestCase {
assertEquals("", Repository.stripWorkDir(relBase, relNonFile));
assertEquals("", Repository.stripWorkDir(absBase, absNonFile));
- assertEquals("", Repository.stripWorkDir(db.getWorkDir(), db.getWorkDir()));
+ assertEquals("", Repository.stripWorkDir(db.getWorkTree(), db.getWorkTree()));
- File file = new File(new File(db.getWorkDir(), "subdir"), "File.java");
- assertEquals("subdir/File.java", Repository.stripWorkDir(db.getWorkDir(), file));
+ File file = new File(new File(db.getWorkTree(), "subdir"), "File.java");
+ assertEquals("subdir/File.java", Repository.stripWorkDir(db.getWorkTree(), file));
}
diff --git a/org.eclipse.jgit.test/tst/org/eclipse/jgit/lib/T0004_PackReader.java b/org.eclipse.jgit.test/tst/org/eclipse/jgit/storage/file/T0004_PackReader.java
index 336bba22ce..472d6956e3 100644
--- a/org.eclipse.jgit.test/tst/org/eclipse/jgit/lib/T0004_PackReader.java
+++ b/org.eclipse.jgit.test/tst/org/eclipse/jgit/storage/file/T0004_PackReader.java
@@ -44,11 +44,15 @@
* ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
*/
-package org.eclipse.jgit.lib;
+package org.eclipse.jgit.storage.file;
import java.io.File;
import java.io.IOException;
+import org.eclipse.jgit.lib.Constants;
+import org.eclipse.jgit.lib.ObjectId;
+import org.eclipse.jgit.lib.ObjectLoader;
+import org.eclipse.jgit.lib.SampleDataRepositoryTestCase;
import org.eclipse.jgit.util.JGitTestUtil;
public class T0004_PackReader extends SampleDataRepositoryTestCase {
@@ -59,15 +63,14 @@ public class T0004_PackReader extends SampleDataRepositoryTestCase {
public void test003_lookupCompressedObject() throws IOException {
final PackFile pr;
final ObjectId id;
- final PackedObjectLoader or;
+ final ObjectLoader or;
id = ObjectId.fromString("902d5476fa249b7abc9d84c611577a81381f0327");
pr = new PackFile(TEST_IDX, TEST_PACK);
- or = pr.get(new WindowCursor(), id);
+ or = pr.get(new WindowCursor(null), id);
assertNotNull(or);
assertEquals(Constants.OBJ_TREE, or.getType());
assertEquals(35, or.getSize());
- assertEquals(7736, or.getObjectOffset());
pr.close();
}
@@ -76,11 +79,9 @@ public class T0004_PackReader extends SampleDataRepositoryTestCase {
final ObjectLoader or;
id = ObjectId.fromString("5b6e7c66c276e7610d4a73c70ec1a1f7c1003259");
- or = db.openObject(id);
+ or = db.open(id);
assertNotNull(or);
- assertTrue(or instanceof PackedObjectLoader);
assertEquals(Constants.OBJ_BLOB, or.getType());
assertEquals(18009, or.getSize());
- assertEquals(516, ((PackedObjectLoader) or).getObjectOffset());
}
}
diff --git a/org.eclipse.jgit.test/tst/org/eclipse/jgit/storage/file/UnpackedObjectTest.java b/org.eclipse.jgit.test/tst/org/eclipse/jgit/storage/file/UnpackedObjectTest.java
new file mode 100644
index 0000000000..25dfe4c239
--- /dev/null
+++ b/org.eclipse.jgit.test/tst/org/eclipse/jgit/storage/file/UnpackedObjectTest.java
@@ -0,0 +1,534 @@
+/*
+ * Copyright (C) 2010, Google Inc.
+ * and other copyright owners as documented in the project's IP log.
+ *
+ * This program and the accompanying materials are made available
+ * under the terms of the Eclipse Distribution License v1.0 which
+ * accompanies this distribution, is reproduced below, and is
+ * available at http://www.eclipse.org/org/documents/edl-v10.php
+ *
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or
+ * without modification, are permitted provided that the following
+ * conditions are met:
+ *
+ * - Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ *
+ * - Redistributions in binary form must reproduce the above
+ * copyright notice, this list of conditions and the following
+ * disclaimer in the documentation and/or other materials provided
+ * with the distribution.
+ *
+ * - Neither the name of the Eclipse Foundation, Inc. nor the
+ * names of its contributors may be used to endorse or promote
+ * products derived from this software without specific prior
+ * written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND
+ * CONTRIBUTORS "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES,
+ * INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
+ * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR
+ * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
+ * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT
+ * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
+ * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER
+ * CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT,
+ * STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
+ * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF
+ * ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ */
+
+package org.eclipse.jgit.storage.file;
+
+import java.io.ByteArrayInputStream;
+import java.io.ByteArrayOutputStream;
+import java.io.File;
+import java.io.FileInputStream;
+import java.io.FileOutputStream;
+import java.io.IOException;
+import java.io.InputStream;
+import java.text.MessageFormat;
+import java.util.Arrays;
+import java.util.zip.DeflaterOutputStream;
+
+import org.eclipse.jgit.JGitText;
+import org.eclipse.jgit.errors.CorruptObjectException;
+import org.eclipse.jgit.errors.LargeObjectException;
+import org.eclipse.jgit.junit.LocalDiskRepositoryTestCase;
+import org.eclipse.jgit.junit.TestRng;
+import org.eclipse.jgit.lib.Constants;
+import org.eclipse.jgit.lib.ObjectId;
+import org.eclipse.jgit.lib.ObjectInserter;
+import org.eclipse.jgit.lib.ObjectLoader;
+import org.eclipse.jgit.lib.ObjectStream;
+import org.eclipse.jgit.util.IO;
+
+public class UnpackedObjectTest extends LocalDiskRepositoryTestCase {
+ private TestRng rng;
+
+ private FileRepository repo;
+
+ private WindowCursor wc;
+
+ protected void setUp() throws Exception {
+ super.setUp();
+ rng = new TestRng(getName());
+ repo = createBareRepository();
+ wc = (WindowCursor) repo.newObjectReader();
+ }
+
+ protected void tearDown() throws Exception {
+ if (wc != null)
+ wc.release();
+ super.tearDown();
+ }
+
+ public void testStandardFormat_SmallObject() throws Exception {
+ final int type = Constants.OBJ_BLOB;
+ byte[] data = rng.nextBytes(300);
+ byte[] gz = compressStandardFormat(type, data);
+ ObjectId id = ObjectId.zeroId();
+
+ ObjectLoader ol = UnpackedObject.open(new ByteArrayInputStream(gz),
+ path(id), id, wc);
+ assertNotNull("created loader", ol);
+ assertEquals(type, ol.getType());
+ assertEquals(data.length, ol.getSize());
+ assertFalse("is not large", ol.isLarge());
+ assertTrue("same content", Arrays.equals(data, ol.getCachedBytes()));
+
+ ObjectStream in = ol.openStream();
+ assertNotNull("have stream", in);
+ assertEquals(type, in.getType());
+ assertEquals(data.length, in.getSize());
+ byte[] data2 = new byte[data.length];
+ IO.readFully(in, data2, 0, data.length);
+ assertTrue("same content", Arrays.equals(data2, data));
+ assertEquals("stream at EOF", -1, in.read());
+ in.close();
+ }
+
+ public void testStandardFormat_LargeObject() throws Exception {
+ final int type = Constants.OBJ_BLOB;
+ byte[] data = rng.nextBytes(ObjectLoader.STREAM_THRESHOLD + 5);
+ ObjectId id = new ObjectInserter.Formatter().idFor(type, data);
+ write(id, compressStandardFormat(type, data));
+
+ ObjectLoader ol;
+ {
+ FileInputStream fs = new FileInputStream(path(id));
+ try {
+ ol = UnpackedObject.open(fs, path(id), id, wc);
+ } finally {
+ fs.close();
+ }
+ }
+
+ assertNotNull("created loader", ol);
+ assertEquals(type, ol.getType());
+ assertEquals(data.length, ol.getSize());
+ assertTrue("is large", ol.isLarge());
+ try {
+ ol.getCachedBytes();
+ fail("Should have thrown LargeObjectException");
+ } catch (LargeObjectException tooBig) {
+ assertEquals(id.name(), tooBig.getMessage());
+ }
+
+ ObjectStream in = ol.openStream();
+ assertNotNull("have stream", in);
+ assertEquals(type, in.getType());
+ assertEquals(data.length, in.getSize());
+ byte[] data2 = new byte[data.length];
+ IO.readFully(in, data2, 0, data.length);
+ assertTrue("same content", Arrays.equals(data2, data));
+ assertEquals("stream at EOF", -1, in.read());
+ in.close();
+ }
+
+ public void testStandardFormat_NegativeSize() throws Exception {
+ ObjectId id = ObjectId.zeroId();
+ byte[] data = rng.nextBytes(300);
+
+ try {
+ byte[] gz = compressStandardFormat("blob", "-1", data);
+ UnpackedObject.open(new ByteArrayInputStream(gz), path(id), id, wc);
+ fail("Did not throw CorruptObjectException");
+ } catch (CorruptObjectException coe) {
+ assertEquals(MessageFormat.format(JGitText.get().objectIsCorrupt,
+ id.name(), JGitText.get().corruptObjectNegativeSize), coe
+ .getMessage());
+ }
+ }
+
+ public void testStandardFormat_InvalidType() throws Exception {
+ ObjectId id = ObjectId.zeroId();
+ byte[] data = rng.nextBytes(300);
+
+ try {
+ byte[] gz = compressStandardFormat("not.a.type", "1", data);
+ UnpackedObject.open(new ByteArrayInputStream(gz), path(id), id, wc);
+ fail("Did not throw CorruptObjectException");
+ } catch (CorruptObjectException coe) {
+ assertEquals(MessageFormat.format(JGitText.get().objectIsCorrupt,
+ id.name(), JGitText.get().corruptObjectInvalidType), coe
+ .getMessage());
+ }
+ }
+
+ public void testStandardFormat_NoHeader() throws Exception {
+ ObjectId id = ObjectId.zeroId();
+ byte[] data = {};
+
+ try {
+ byte[] gz = compressStandardFormat("", "", data);
+ UnpackedObject.open(new ByteArrayInputStream(gz), path(id), id, wc);
+ fail("Did not throw CorruptObjectException");
+ } catch (CorruptObjectException coe) {
+ assertEquals(MessageFormat.format(JGitText.get().objectIsCorrupt,
+ id.name(), JGitText.get().corruptObjectNoHeader), coe
+ .getMessage());
+ }
+ }
+
+ public void testStandardFormat_GarbageAfterSize() throws Exception {
+ ObjectId id = ObjectId.zeroId();
+ byte[] data = rng.nextBytes(300);
+
+ try {
+ byte[] gz = compressStandardFormat("blob", "1foo", data);
+ UnpackedObject.open(new ByteArrayInputStream(gz), path(id), id, wc);
+ fail("Did not throw CorruptObjectException");
+ } catch (CorruptObjectException coe) {
+ assertEquals(MessageFormat.format(JGitText.get().objectIsCorrupt,
+ id.name(), JGitText.get().corruptObjectGarbageAfterSize),
+ coe.getMessage());
+ }
+ }
+
+ public void testStandardFormat_SmallObject_CorruptZLibStream()
+ throws Exception {
+ ObjectId id = ObjectId.zeroId();
+ byte[] data = rng.nextBytes(300);
+
+ try {
+ byte[] gz = compressStandardFormat(Constants.OBJ_BLOB, data);
+ for (int i = 5; i < gz.length; i++)
+ gz[i] = 0;
+ UnpackedObject.open(new ByteArrayInputStream(gz), path(id), id, wc);
+ fail("Did not throw CorruptObjectException");
+ } catch (CorruptObjectException coe) {
+ assertEquals(MessageFormat.format(JGitText.get().objectIsCorrupt,
+ id.name(), JGitText.get().corruptObjectBadStream), coe
+ .getMessage());
+ }
+ }
+
+ public void testStandardFormat_SmallObject_TruncatedZLibStream()
+ throws Exception {
+ ObjectId id = ObjectId.zeroId();
+ byte[] data = rng.nextBytes(300);
+
+ try {
+ byte[] gz = compressStandardFormat(Constants.OBJ_BLOB, data);
+ byte[] tr = new byte[gz.length - 1];
+ System.arraycopy(gz, 0, tr, 0, tr.length);
+ UnpackedObject.open(new ByteArrayInputStream(tr), path(id), id, wc);
+ fail("Did not throw CorruptObjectException");
+ } catch (CorruptObjectException coe) {
+ assertEquals(MessageFormat.format(JGitText.get().objectIsCorrupt,
+ id.name(), JGitText.get().corruptObjectBadStream), coe
+ .getMessage());
+ }
+ }
+
+ public void testStandardFormat_SmallObject_TrailingGarbage()
+ throws Exception {
+ ObjectId id = ObjectId.zeroId();
+ byte[] data = rng.nextBytes(300);
+
+ try {
+ byte[] gz = compressStandardFormat(Constants.OBJ_BLOB, data);
+ byte[] tr = new byte[gz.length + 1];
+ System.arraycopy(gz, 0, tr, 0, gz.length);
+ UnpackedObject.open(new ByteArrayInputStream(tr), path(id), id, wc);
+ fail("Did not throw CorruptObjectException");
+ } catch (CorruptObjectException coe) {
+ assertEquals(MessageFormat.format(JGitText.get().objectIsCorrupt,
+ id.name(), JGitText.get().corruptObjectBadStream), coe
+ .getMessage());
+ }
+ }
+
+ public void testStandardFormat_LargeObject_CorruptZLibStream()
+ throws Exception {
+ final int type = Constants.OBJ_BLOB;
+ byte[] data = rng.nextBytes(ObjectLoader.STREAM_THRESHOLD + 5);
+ ObjectId id = new ObjectInserter.Formatter().idFor(type, data);
+ byte[] gz = compressStandardFormat(type, data);
+ gz[gz.length - 1] = 0;
+ gz[gz.length - 2] = 0;
+
+ write(id, gz);
+
+ ObjectLoader ol;
+ {
+ FileInputStream fs = new FileInputStream(path(id));
+ try {
+ ol = UnpackedObject.open(fs, path(id), id, wc);
+ } finally {
+ fs.close();
+ }
+ }
+
+ try {
+ byte[] tmp = new byte[data.length];
+ InputStream in = ol.openStream();
+ try {
+ IO.readFully(in, tmp, 0, tmp.length);
+ } finally {
+ in.close();
+ }
+ fail("Did not throw CorruptObjectException");
+ } catch (CorruptObjectException coe) {
+ assertEquals(MessageFormat.format(JGitText.get().objectIsCorrupt,
+ id.name(), JGitText.get().corruptObjectBadStream), coe
+ .getMessage());
+ }
+ }
+
+ public void testStandardFormat_LargeObject_TruncatedZLibStream()
+ throws Exception {
+ final int type = Constants.OBJ_BLOB;
+ byte[] data = rng.nextBytes(ObjectLoader.STREAM_THRESHOLD + 5);
+ ObjectId id = new ObjectInserter.Formatter().idFor(type, data);
+ byte[] gz = compressStandardFormat(type, data);
+ byte[] tr = new byte[gz.length - 1];
+ System.arraycopy(gz, 0, tr, 0, tr.length);
+
+ write(id, tr);
+
+ ObjectLoader ol;
+ {
+ FileInputStream fs = new FileInputStream(path(id));
+ try {
+ ol = UnpackedObject.open(fs, path(id), id, wc);
+ } finally {
+ fs.close();
+ }
+ }
+
+ byte[] tmp = new byte[data.length];
+ InputStream in = ol.openStream();
+ IO.readFully(in, tmp, 0, tmp.length);
+ try {
+ in.close();
+ fail("close did not throw CorruptObjectException");
+ } catch (CorruptObjectException coe) {
+ assertEquals(MessageFormat.format(JGitText.get().objectIsCorrupt,
+ id.name(), JGitText.get().corruptObjectBadStream), coe
+ .getMessage());
+ }
+ }
+
+ public void testStandardFormat_LargeObject_TrailingGarbage()
+ throws Exception {
+ final int type = Constants.OBJ_BLOB;
+ byte[] data = rng.nextBytes(ObjectLoader.STREAM_THRESHOLD + 5);
+ ObjectId id = new ObjectInserter.Formatter().idFor(type, data);
+ byte[] gz = compressStandardFormat(type, data);
+ byte[] tr = new byte[gz.length + 1];
+ System.arraycopy(gz, 0, tr, 0, gz.length);
+
+ write(id, tr);
+
+ ObjectLoader ol;
+ {
+ FileInputStream fs = new FileInputStream(path(id));
+ try {
+ ol = UnpackedObject.open(fs, path(id), id, wc);
+ } finally {
+ fs.close();
+ }
+ }
+
+ byte[] tmp = new byte[data.length];
+ InputStream in = ol.openStream();
+ IO.readFully(in, tmp, 0, tmp.length);
+ try {
+ in.close();
+ fail("close did not throw CorruptObjectException");
+ } catch (CorruptObjectException coe) {
+ assertEquals(MessageFormat.format(JGitText.get().objectIsCorrupt,
+ id.name(), JGitText.get().corruptObjectBadStream), coe
+ .getMessage());
+ }
+ }
+
+ public void testPackFormat_SmallObject() throws Exception {
+ final int type = Constants.OBJ_BLOB;
+ byte[] data = rng.nextBytes(300);
+ byte[] gz = compressPackFormat(type, data);
+ ObjectId id = ObjectId.zeroId();
+
+ ObjectLoader ol = UnpackedObject.open(new ByteArrayInputStream(gz),
+ path(id), id, wc);
+ assertNotNull("created loader", ol);
+ assertEquals(type, ol.getType());
+ assertEquals(data.length, ol.getSize());
+ assertFalse("is not large", ol.isLarge());
+ assertTrue("same content", Arrays.equals(data, ol.getCachedBytes()));
+
+ ObjectStream in = ol.openStream();
+ assertNotNull("have stream", in);
+ assertEquals(type, in.getType());
+ assertEquals(data.length, in.getSize());
+ byte[] data2 = new byte[data.length];
+ IO.readFully(in, data2, 0, data.length);
+ assertTrue("same content", Arrays.equals(data, ol.getCachedBytes()));
+ in.close();
+ }
+
+ public void testPackFormat_LargeObject() throws Exception {
+ final int type = Constants.OBJ_BLOB;
+ byte[] data = rng.nextBytes(ObjectLoader.STREAM_THRESHOLD + 5);
+ ObjectId id = new ObjectInserter.Formatter().idFor(type, data);
+ write(id, compressPackFormat(type, data));
+
+ ObjectLoader ol;
+ {
+ FileInputStream fs = new FileInputStream(path(id));
+ try {
+ ol = UnpackedObject.open(fs, path(id), id, wc);
+ } finally {
+ fs.close();
+ }
+ }
+
+ assertNotNull("created loader", ol);
+ assertEquals(type, ol.getType());
+ assertEquals(data.length, ol.getSize());
+ assertTrue("is large", ol.isLarge());
+ try {
+ ol.getCachedBytes();
+ fail("Should have thrown LargeObjectException");
+ } catch (LargeObjectException tooBig) {
+ assertEquals(id.name(), tooBig.getMessage());
+ }
+
+ ObjectStream in = ol.openStream();
+ assertNotNull("have stream", in);
+ assertEquals(type, in.getType());
+ assertEquals(data.length, in.getSize());
+ byte[] data2 = new byte[data.length];
+ IO.readFully(in, data2, 0, data.length);
+ assertTrue("same content", Arrays.equals(data2, data));
+ assertEquals("stream at EOF", -1, in.read());
+ in.close();
+ }
+
+ public void testPackFormat_DeltaNotAllowed() throws Exception {
+ ObjectId id = ObjectId.zeroId();
+ byte[] data = rng.nextBytes(300);
+
+ try {
+ byte[] gz = compressPackFormat(Constants.OBJ_OFS_DELTA, data);
+ UnpackedObject.open(new ByteArrayInputStream(gz), path(id), id, wc);
+ fail("Did not throw CorruptObjectException");
+ } catch (CorruptObjectException coe) {
+ assertEquals(MessageFormat.format(JGitText.get().objectIsCorrupt,
+ id.name(), JGitText.get().corruptObjectInvalidType), coe
+ .getMessage());
+ }
+
+ try {
+ byte[] gz = compressPackFormat(Constants.OBJ_REF_DELTA, data);
+ UnpackedObject.open(new ByteArrayInputStream(gz), path(id), id, wc);
+ fail("Did not throw CorruptObjectException");
+ } catch (CorruptObjectException coe) {
+ assertEquals(MessageFormat.format(JGitText.get().objectIsCorrupt,
+ id.name(), JGitText.get().corruptObjectInvalidType), coe
+ .getMessage());
+ }
+
+ try {
+ byte[] gz = compressPackFormat(Constants.OBJ_TYPE_5, data);
+ UnpackedObject.open(new ByteArrayInputStream(gz), path(id), id, wc);
+ fail("Did not throw CorruptObjectException");
+ } catch (CorruptObjectException coe) {
+ assertEquals(MessageFormat.format(JGitText.get().objectIsCorrupt,
+ id.name(), JGitText.get().corruptObjectInvalidType), coe
+ .getMessage());
+ }
+
+ try {
+ byte[] gz = compressPackFormat(Constants.OBJ_EXT, data);
+ UnpackedObject.open(new ByteArrayInputStream(gz), path(id), id, wc);
+ fail("Did not throw CorruptObjectException");
+ } catch (CorruptObjectException coe) {
+ assertEquals(MessageFormat.format(JGitText.get().objectIsCorrupt,
+ id.name(), JGitText.get().corruptObjectInvalidType), coe
+ .getMessage());
+ }
+ }
+
+ private byte[] compressStandardFormat(int type, byte[] data)
+ throws IOException {
+ String typeString = Constants.typeString(type);
+ String length = String.valueOf(data.length);
+ return compressStandardFormat(typeString, length, data);
+ }
+
+ private byte[] compressStandardFormat(String type, String length,
+ byte[] data) throws IOException {
+ ByteArrayOutputStream out = new ByteArrayOutputStream();
+ DeflaterOutputStream d = new DeflaterOutputStream(out);
+ d.write(Constants.encodeASCII(type));
+ d.write(' ');
+ d.write(Constants.encodeASCII(length));
+ d.write(0);
+ d.write(data);
+ d.finish();
+ return out.toByteArray();
+ }
+
+ private byte[] compressPackFormat(int type, byte[] data) throws IOException {
+ byte[] hdr = new byte[64];
+ int rawLength = data.length;
+ int nextLength = rawLength >>> 4;
+ hdr[0] = (byte) ((nextLength > 0 ? 0x80 : 0x00) | (type << 4) | (rawLength & 0x0F));
+ rawLength = nextLength;
+ int n = 1;
+ while (rawLength > 0) {
+ nextLength >>>= 7;
+ hdr[n++] = (byte) ((nextLength > 0 ? 0x80 : 0x00) | (rawLength & 0x7F));
+ rawLength = nextLength;
+ }
+
+ final ByteArrayOutputStream out = new ByteArrayOutputStream();
+ out.write(hdr, 0, n);
+
+ DeflaterOutputStream d = new DeflaterOutputStream(out);
+ d.write(data);
+ d.finish();
+ return out.toByteArray();
+ }
+
+ private File path(ObjectId id) {
+ return repo.getObjectDatabase().fileFor(id);
+ }
+
+ private void write(ObjectId id, byte[] data) throws IOException {
+ File path = path(id);
+ path.getParentFile().mkdirs();
+ FileOutputStream out = new FileOutputStream(path);
+ try {
+ out.write(data);
+ } finally {
+ out.close();
+ }
+ }
+}
diff --git a/org.eclipse.jgit.test/tst/org/eclipse/jgit/lib/WindowCacheGetTest.java b/org.eclipse.jgit.test/tst/org/eclipse/jgit/storage/file/WindowCacheGetTest.java
index 8ff022ddc4..d8c682999c 100644
--- a/org.eclipse.jgit.test/tst/org/eclipse/jgit/lib/WindowCacheGetTest.java
+++ b/org.eclipse.jgit.test/tst/org/eclipse/jgit/storage/file/WindowCacheGetTest.java
@@ -41,7 +41,7 @@
* ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
*/
-package org.eclipse.jgit.lib;
+package org.eclipse.jgit.storage.file;
import java.io.BufferedReader;
import java.io.FileInputStream;
@@ -51,6 +51,10 @@ import java.util.ArrayList;
import java.util.List;
import org.eclipse.jgit.errors.CorruptObjectException;
+import org.eclipse.jgit.lib.Constants;
+import org.eclipse.jgit.lib.ObjectId;
+import org.eclipse.jgit.lib.ObjectLoader;
+import org.eclipse.jgit.lib.SampleDataRepositoryTestCase;
import org.eclipse.jgit.util.JGitTestUtil;
import org.eclipse.jgit.util.MutableInteger;
@@ -73,9 +77,9 @@ public class WindowCacheGetTest extends SampleDataRepositoryTestCase {
final TestObject o = new TestObject();
o.id = ObjectId.fromString(parts[0]);
o.setType(parts[1]);
- o.rawSize = Integer.parseInt(parts[2]);
+ // parts[2] is the inflate size
// parts[3] is the size-in-pack
- o.offset = Long.parseLong(parts[4]);
+ // parts[4] is the offset in the pack
toLoad.add(o);
}
} finally {
@@ -122,12 +126,9 @@ public class WindowCacheGetTest extends SampleDataRepositoryTestCase {
private void doCacheTests() throws IOException {
for (final TestObject o : toLoad) {
- final ObjectLoader or = db.openObject(o.id);
+ final ObjectLoader or = db.open(o.id, o.type);
assertNotNull(or);
- assertTrue(or instanceof PackedObjectLoader);
assertEquals(o.type, or.getType());
- assertEquals(o.rawSize, or.getRawSize());
- assertEquals(o.offset, ((PackedObjectLoader) or).getObjectOffset());
}
}
@@ -136,10 +137,6 @@ public class WindowCacheGetTest extends SampleDataRepositoryTestCase {
int type;
- int rawSize;
-
- long offset;
-
void setType(final String typeStr) throws CorruptObjectException {
final byte[] typeRaw = Constants.encode(typeStr + " ");
final MutableInteger ptr = new MutableInteger();
diff --git a/org.eclipse.jgit.test/tst/org/eclipse/jgit/lib/WindowCacheReconfigureTest.java b/org.eclipse.jgit.test/tst/org/eclipse/jgit/storage/file/WindowCacheReconfigureTest.java
index 9e093c85bd..e52b19d925 100644
--- a/org.eclipse.jgit.test/tst/org/eclipse/jgit/lib/WindowCacheReconfigureTest.java
+++ b/org.eclipse.jgit.test/tst/org/eclipse/jgit/storage/file/WindowCacheReconfigureTest.java
@@ -41,7 +41,9 @@
* ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
*/
-package org.eclipse.jgit.lib;
+package org.eclipse.jgit.storage.file;
+
+import org.eclipse.jgit.lib.RepositoryTestCase;
public class WindowCacheReconfigureTest extends RepositoryTestCase {
public void testConfigureCache_PackedGitLimit_0() {
diff --git a/org.eclipse.jgit.test/tst/org/eclipse/jgit/lib/XInputStream.java b/org.eclipse.jgit.test/tst/org/eclipse/jgit/storage/file/XInputStream.java
index eef32b9276..9978c8e13c 100644
--- a/org.eclipse.jgit.test/tst/org/eclipse/jgit/lib/XInputStream.java
+++ b/org.eclipse.jgit.test/tst/org/eclipse/jgit/storage/file/XInputStream.java
@@ -41,7 +41,7 @@
* ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
*/
-package org.eclipse.jgit.lib;
+package org.eclipse.jgit.storage.file;
import java.io.BufferedInputStream;
import java.io.EOFException;
diff --git a/org.eclipse.jgit.test/tst/org/eclipse/jgit/storage/pack/DeltaIndexTest.java b/org.eclipse.jgit.test/tst/org/eclipse/jgit/storage/pack/DeltaIndexTest.java
new file mode 100644
index 0000000000..868ef8825c
--- /dev/null
+++ b/org.eclipse.jgit.test/tst/org/eclipse/jgit/storage/pack/DeltaIndexTest.java
@@ -0,0 +1,228 @@
+/*
+ * Copyright (C) 2010, Google Inc.
+ * and other copyright owners as documented in the project's IP log.
+ *
+ * This program and the accompanying materials are made available
+ * under the terms of the Eclipse Distribution License v1.0 which
+ * accompanies this distribution, is reproduced below, and is
+ * available at http://www.eclipse.org/org/documents/edl-v10.php
+ *
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or
+ * without modification, are permitted provided that the following
+ * conditions are met:
+ *
+ * - Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ *
+ * - Redistributions in binary form must reproduce the above
+ * copyright notice, this list of conditions and the following
+ * disclaimer in the documentation and/or other materials provided
+ * with the distribution.
+ *
+ * - Neither the name of the Eclipse Foundation, Inc. nor the
+ * names of its contributors may be used to endorse or promote
+ * products derived from this software without specific prior
+ * written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND
+ * CONTRIBUTORS "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES,
+ * INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
+ * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR
+ * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
+ * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT
+ * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
+ * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER
+ * CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT,
+ * STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
+ * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF
+ * ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ */
+
+package org.eclipse.jgit.storage.pack;
+
+import java.io.ByteArrayOutputStream;
+import java.io.IOException;
+import java.util.Arrays;
+
+import junit.framework.TestCase;
+
+import org.eclipse.jgit.junit.TestRng;
+import org.eclipse.jgit.lib.Constants;
+
+public class DeltaIndexTest extends TestCase {
+ private TestRng rng;
+
+ private ByteArrayOutputStream actDeltaBuf;
+
+ private ByteArrayOutputStream expDeltaBuf;
+
+ private DeltaEncoder expDeltaEnc;
+
+ private byte[] src;
+
+ private byte[] dst;
+
+ private ByteArrayOutputStream dstBuf;
+
+ protected void setUp() throws Exception {
+ super.setUp();
+ rng = new TestRng(getName());
+ actDeltaBuf = new ByteArrayOutputStream();
+ expDeltaBuf = new ByteArrayOutputStream();
+ expDeltaEnc = new DeltaEncoder(expDeltaBuf, 0, 0);
+ dstBuf = new ByteArrayOutputStream();
+ }
+
+ public void testInsertWholeObject_Length12() throws IOException {
+ src = rng.nextBytes(12);
+ insert(src);
+ doTest();
+ }
+
+ public void testCopyWholeObject_Length128() throws IOException {
+ src = rng.nextBytes(128);
+ copy(0, 128);
+ doTest();
+ }
+
+ public void testCopyWholeObject_Length123() throws IOException {
+ src = rng.nextBytes(123);
+ copy(0, 123);
+ doTest();
+ }
+
+ public void testCopyZeros_Length128() throws IOException {
+ src = new byte[2048];
+ copy(0, src.length);
+ doTest();
+
+ // The index should be smaller than expected due to the chain
+ // being truncated. Without truncation we would expect to have
+ // more than 3584 bytes used.
+ //
+ assertEquals(2636, new DeltaIndex(src).getIndexSize());
+ }
+
+ public void testShuffleSegments() throws IOException {
+ src = rng.nextBytes(128);
+ copy(64, 64);
+ copy(0, 64);
+ doTest();
+ }
+
+ public void testInsertHeadMiddle() throws IOException {
+ src = rng.nextBytes(1024);
+ insert("foo");
+ copy(0, 512);
+ insert("yet more fooery");
+ copy(0, 512);
+ doTest();
+ }
+
+ public void testInsertTail() throws IOException {
+ src = rng.nextBytes(1024);
+ copy(0, 512);
+ insert("bar");
+ doTest();
+ }
+
+ public void testIndexSize() {
+ src = rng.nextBytes(1024);
+ DeltaIndex di = new DeltaIndex(src);
+ assertEquals(1860, di.getIndexSize());
+ assertEquals("DeltaIndex[2 KiB]", di.toString());
+ }
+
+ public void testLimitObjectSize_Length12InsertFails() throws IOException {
+ src = rng.nextBytes(12);
+ dst = src;
+
+ DeltaIndex di = new DeltaIndex(src);
+ assertFalse(di.encode(actDeltaBuf, dst, src.length));
+ }
+
+ public void testLimitObjectSize_Length130InsertFails() throws IOException {
+ src = rng.nextBytes(130);
+ dst = rng.nextBytes(130);
+
+ DeltaIndex di = new DeltaIndex(src);
+ assertFalse(di.encode(actDeltaBuf, dst, src.length));
+ }
+
+ public void testLimitObjectSize_Length130CopyOk() throws IOException {
+ src = rng.nextBytes(130);
+ copy(0, 130);
+ dst = dstBuf.toByteArray();
+
+ DeltaIndex di = new DeltaIndex(src);
+ assertTrue(di.encode(actDeltaBuf, dst, dst.length));
+
+ byte[] actDelta = actDeltaBuf.toByteArray();
+ byte[] expDelta = expDeltaBuf.toByteArray();
+
+ assertEquals(BinaryDelta.format(expDelta, false), //
+ BinaryDelta.format(actDelta, false));
+ }
+
+ public void testLimitObjectSize_Length130CopyFails() throws IOException {
+ src = rng.nextBytes(130);
+ copy(0, 130);
+ dst = dstBuf.toByteArray();
+
+ // The header requires 4 bytes for these objects, so a target length
+ // of 5 is bigger than the copy instruction and should cause an abort.
+ //
+ DeltaIndex di = new DeltaIndex(src);
+ assertFalse(di.encode(actDeltaBuf, dst, 5));
+ assertEquals(4, actDeltaBuf.size());
+ }
+
+ public void testLimitObjectSize_InsertFrontFails() throws IOException {
+ src = rng.nextBytes(130);
+ insert("eight");
+ copy(0, 130);
+ dst = dstBuf.toByteArray();
+
+ // The header requires 4 bytes for these objects, so a target length
+ // of 5 is bigger than the copy instruction and should cause an abort.
+ //
+ DeltaIndex di = new DeltaIndex(src);
+ assertFalse(di.encode(actDeltaBuf, dst, 5));
+ assertEquals(4, actDeltaBuf.size());
+ }
+
+ private void copy(int offset, int len) throws IOException {
+ dstBuf.write(src, offset, len);
+ expDeltaEnc.copy(offset, len);
+ }
+
+ private void insert(String text) throws IOException {
+ insert(Constants.encode(text));
+ }
+
+ private void insert(byte[] text) throws IOException {
+ dstBuf.write(text);
+ expDeltaEnc.insert(text);
+ }
+
+ private void doTest() throws IOException {
+ dst = dstBuf.toByteArray();
+
+ DeltaIndex di = new DeltaIndex(src);
+ di.encode(actDeltaBuf, dst);
+
+ byte[] actDelta = actDeltaBuf.toByteArray();
+ byte[] expDelta = expDeltaBuf.toByteArray();
+
+ assertEquals(BinaryDelta.format(expDelta, false), //
+ BinaryDelta.format(actDelta, false));
+
+ assertTrue("delta is not empty", actDelta.length > 0);
+ assertEquals(src.length, BinaryDelta.getBaseSize(actDelta));
+ assertEquals(dst.length, BinaryDelta.getResultSize(actDelta));
+ assertTrue(Arrays.equals(dst, BinaryDelta.apply(src, actDelta)));
+ }
+}
diff --git a/org.eclipse.jgit.test/tst/org/eclipse/jgit/storage/pack/DeltaStreamTest.java b/org.eclipse.jgit.test/tst/org/eclipse/jgit/storage/pack/DeltaStreamTest.java
new file mode 100644
index 0000000000..9b34ad5e09
--- /dev/null
+++ b/org.eclipse.jgit.test/tst/org/eclipse/jgit/storage/pack/DeltaStreamTest.java
@@ -0,0 +1,273 @@
+/*
+ * Copyright (C) 2010, Google Inc.
+ * and other copyright owners as documented in the project's IP log.
+ *
+ * This program and the accompanying materials are made available
+ * under the terms of the Eclipse Distribution License v1.0 which
+ * accompanies this distribution, is reproduced below, and is
+ * available at http://www.eclipse.org/org/documents/edl-v10.php
+ *
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or
+ * without modification, are permitted provided that the following
+ * conditions are met:
+ *
+ * - Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ *
+ * - Redistributions in binary form must reproduce the above
+ * copyright notice, this list of conditions and the following
+ * disclaimer in the documentation and/or other materials provided
+ * with the distribution.
+ *
+ * - Neither the name of the Eclipse Foundation, Inc. nor the
+ * names of its contributors may be used to endorse or promote
+ * products derived from this software without specific prior
+ * written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND
+ * CONTRIBUTORS "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES,
+ * INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
+ * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR
+ * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
+ * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT
+ * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
+ * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER
+ * CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT,
+ * STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
+ * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF
+ * ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ */
+
+package org.eclipse.jgit.storage.pack;
+
+import java.io.ByteArrayInputStream;
+import java.io.ByteArrayOutputStream;
+import java.io.IOException;
+import java.io.InputStream;
+import java.util.Arrays;
+
+import junit.framework.TestCase;
+
+import org.eclipse.jgit.JGitText;
+import org.eclipse.jgit.errors.CorruptObjectException;
+import org.eclipse.jgit.junit.TestRng;
+import org.eclipse.jgit.lib.Constants;
+import org.eclipse.jgit.util.IO;
+
+public class DeltaStreamTest extends TestCase {
+ private TestRng rng;
+
+ private ByteArrayOutputStream deltaBuf;
+
+ private DeltaEncoder deltaEnc;
+
+ private byte[] base;
+
+ private byte[] data;
+
+ private int dataPtr;
+
+ private byte[] delta;
+
+ protected void setUp() throws Exception {
+ super.setUp();
+ rng = new TestRng(getName());
+ deltaBuf = new ByteArrayOutputStream();
+ }
+
+ public void testCopy_SingleOp() throws IOException {
+ init((1 << 16) + 1, (1 << 8) + 1);
+ copy(0, data.length);
+ assertValidState();
+ }
+
+ public void testCopy_MaxSize() throws IOException {
+ int max = (0xff << 16) + (0xff << 8) + 0xff;
+ init(1 + max, max);
+ copy(1, max);
+ assertValidState();
+ }
+
+ public void testCopy_64k() throws IOException {
+ init(0x10000 + 2, 0x10000 + 1);
+ copy(1, 0x10000);
+ copy(0x10001, 1);
+ assertValidState();
+ }
+
+ public void testCopy_Gap() throws IOException {
+ init(256, 8);
+ copy(4, 4);
+ copy(128, 4);
+ assertValidState();
+ }
+
+ public void testCopy_OutOfOrder() throws IOException {
+ init((1 << 16) + 1, (1 << 16) + 1);
+ copy(1 << 8, 1 << 8);
+ copy(0, data.length - dataPtr);
+ assertValidState();
+ }
+
+ public void testInsert_SingleOp() throws IOException {
+ init((1 << 16) + 1, 2);
+ insert("hi");
+ assertValidState();
+ }
+
+ public void testInsertAndCopy() throws IOException {
+ init(8, 512);
+ insert(new byte[127]);
+ insert(new byte[127]);
+ insert(new byte[127]);
+ insert(new byte[125]);
+ copy(2, 6);
+ assertValidState();
+ }
+
+ public void testSkip() throws IOException {
+ init(32, 15);
+ copy(2, 2);
+ insert("ab");
+ insert("cd");
+ copy(4, 4);
+ copy(0, 2);
+ insert("efg");
+ assertValidState();
+
+ for (int p = 0; p < data.length; p++) {
+ byte[] act = new byte[data.length];
+ System.arraycopy(data, 0, act, 0, p);
+ DeltaStream in = open();
+ IO.skipFully(in, p);
+ assertEquals(data.length - p, in.read(act, p, data.length - p));
+ assertEquals(-1, in.read());
+ assertTrue("skipping " + p, Arrays.equals(data, act));
+ }
+
+ // Skip all the way to the end should still recognize EOF.
+ DeltaStream in = open();
+ IO.skipFully(in, data.length);
+ assertEquals(-1, in.read());
+ assertEquals(0, in.skip(1));
+
+ // Skip should not open the base as we move past it, but it
+ // will open when we need to start copying data from it.
+ final boolean[] opened = new boolean[1];
+ in = new DeltaStream(new ByteArrayInputStream(delta)) {
+ @Override
+ protected long getBaseSize() throws IOException {
+ return base.length;
+ }
+
+ @Override
+ protected InputStream openBase() throws IOException {
+ opened[0] = true;
+ return new ByteArrayInputStream(base);
+ }
+ };
+ IO.skipFully(in, 7);
+ assertFalse("not yet open", opened[0]);
+ assertEquals(data[7], in.read());
+ assertTrue("now open", opened[0]);
+ }
+
+ public void testIncorrectBaseSize() throws IOException {
+ init(4, 4);
+ copy(0, 4);
+ assertValidState();
+
+ DeltaStream in = new DeltaStream(new ByteArrayInputStream(delta)) {
+ @Override
+ protected long getBaseSize() throws IOException {
+ return 128;
+ }
+
+ @Override
+ protected InputStream openBase() throws IOException {
+ return new ByteArrayInputStream(base);
+ }
+ };
+ try {
+ in.read(new byte[4]);
+ fail("did not throw an exception");
+ } catch (CorruptObjectException e) {
+ assertEquals(JGitText.get().baseLengthIncorrect, e.getMessage());
+ }
+
+ in = new DeltaStream(new ByteArrayInputStream(delta)) {
+ @Override
+ protected long getBaseSize() throws IOException {
+ return 4;
+ }
+
+ @Override
+ protected InputStream openBase() throws IOException {
+ return new ByteArrayInputStream(new byte[0]);
+ }
+ };
+ try {
+ in.read(new byte[4]);
+ fail("did not throw an exception");
+ } catch (CorruptObjectException e) {
+ assertEquals(JGitText.get().baseLengthIncorrect, e.getMessage());
+ }
+ }
+
+ private void init(int baseSize, int dataSize) throws IOException {
+ base = rng.nextBytes(baseSize);
+ data = new byte[dataSize];
+ deltaEnc = new DeltaEncoder(deltaBuf, baseSize, dataSize);
+ }
+
+ private void copy(int offset, int len) throws IOException {
+ System.arraycopy(base, offset, data, dataPtr, len);
+ deltaEnc.copy(offset, len);
+ assertEquals(deltaBuf.size(), deltaEnc.getSize());
+ dataPtr += len;
+ }
+
+ private void insert(String text) throws IOException {
+ insert(Constants.encode(text));
+ }
+
+ private void insert(byte[] text) throws IOException {
+ System.arraycopy(text, 0, data, dataPtr, text.length);
+ deltaEnc.insert(text);
+ assertEquals(deltaBuf.size(), deltaEnc.getSize());
+ dataPtr += text.length;
+ }
+
+ private void assertValidState() throws IOException {
+ assertEquals("test filled example result", data.length, dataPtr);
+
+ delta = deltaBuf.toByteArray();
+ assertEquals(base.length, BinaryDelta.getBaseSize(delta));
+ assertEquals(data.length, BinaryDelta.getResultSize(delta));
+ assertTrue(Arrays.equals(data, BinaryDelta.apply(base, delta)));
+
+ byte[] act = new byte[data.length];
+ DeltaStream in = open();
+ assertEquals(data.length, in.getSize());
+ assertEquals(data.length, in.read(act));
+ assertEquals(-1, in.read());
+ assertTrue(Arrays.equals(data, act));
+ }
+
+ private DeltaStream open() throws IOException {
+ return new DeltaStream(new ByteArrayInputStream(delta)) {
+ @Override
+ protected long getBaseSize() throws IOException {
+ return base.length;
+ }
+
+ @Override
+ protected InputStream openBase() throws IOException {
+ return new ByteArrayInputStream(base);
+ }
+ };
+ }
+}
diff --git a/org.eclipse.jgit.test/tst/org/eclipse/jgit/transport/BundleWriterTest.java b/org.eclipse.jgit.test/tst/org/eclipse/jgit/transport/BundleWriterTest.java
index 2d6aa28d5a..cc7056225b 100644
--- a/org.eclipse.jgit.test/tst/org/eclipse/jgit/transport/BundleWriterTest.java
+++ b/org.eclipse.jgit.test/tst/org/eclipse/jgit/transport/BundleWriterTest.java
@@ -148,12 +148,12 @@ public class BundleWriterTest extends SampleDataRepositoryTestCase {
throws FileNotFoundException, IOException {
final BundleWriter bw;
- bw = new BundleWriter(db, NullProgressMonitor.INSTANCE);
+ bw = new BundleWriter(db);
bw.include(name, ObjectId.fromString(anObjectToInclude));
if (assume != null)
bw.assume(assume);
final ByteArrayOutputStream out = new ByteArrayOutputStream();
- bw.writeBundle(out);
+ bw.writeBundle(NullProgressMonitor.INSTANCE, out);
return out.toByteArray();
}
diff --git a/org.eclipse.jgit.test/tst/org/eclipse/jgit/transport/IndexPackTest.java b/org.eclipse.jgit.test/tst/org/eclipse/jgit/transport/IndexPackTest.java
index e18f741ac4..110804f91a 100644
--- a/org.eclipse.jgit.test/tst/org/eclipse/jgit/transport/IndexPackTest.java
+++ b/org.eclipse.jgit.test/tst/org/eclipse/jgit/transport/IndexPackTest.java
@@ -58,10 +58,10 @@ import org.eclipse.jgit.junit.TestRepository;
import org.eclipse.jgit.lib.Constants;
import org.eclipse.jgit.lib.NullProgressMonitor;
import org.eclipse.jgit.lib.ObjectId;
-import org.eclipse.jgit.lib.PackFile;
import org.eclipse.jgit.lib.RepositoryTestCase;
import org.eclipse.jgit.lib.TextProgressMonitor;
import org.eclipse.jgit.revwalk.RevBlob;
+import org.eclipse.jgit.storage.file.PackFile;
import org.eclipse.jgit.util.JGitTestUtil;
import org.eclipse.jgit.util.NB;
import org.eclipse.jgit.util.TemporaryBuffer;
diff --git a/org.eclipse.jgit.test/tst/org/eclipse/jgit/transport/ReceivePackRefFilterTest.java b/org.eclipse.jgit.test/tst/org/eclipse/jgit/transport/ReceivePackRefFilterTest.java
index 40c719f699..b331f9cf54 100644
--- a/org.eclipse.jgit.test/tst/org/eclipse/jgit/transport/ReceivePackRefFilterTest.java
+++ b/org.eclipse.jgit.test/tst/org/eclipse/jgit/transport/ReceivePackRefFilterTest.java
@@ -56,7 +56,6 @@ import org.eclipse.jgit.junit.LocalDiskRepositoryTestCase;
import org.eclipse.jgit.junit.TestRepository;
import org.eclipse.jgit.lib.Constants;
import org.eclipse.jgit.lib.NullProgressMonitor;
-import org.eclipse.jgit.lib.ObjectDirectory;
import org.eclipse.jgit.lib.ObjectId;
import org.eclipse.jgit.lib.ObjectLoader;
import org.eclipse.jgit.lib.Ref;
@@ -64,6 +63,7 @@ import org.eclipse.jgit.lib.Repository;
import org.eclipse.jgit.revwalk.RevBlob;
import org.eclipse.jgit.revwalk.RevCommit;
import org.eclipse.jgit.revwalk.RevTree;
+import org.eclipse.jgit.storage.file.ObjectDirectory;
import org.eclipse.jgit.util.NB;
import org.eclipse.jgit.util.TemporaryBuffer;
@@ -176,7 +176,7 @@ public class ReceivePackRefFilterTest extends LocalDiskRepositoryTestCase {
// Verify the only storage of b is our packed delta above.
//
ObjectDirectory od = (ObjectDirectory) src.getObjectDatabase();
- assertTrue("has b", od.hasObject(b));
+ assertTrue("has b", src.hasObject(b));
assertFalse("b not loose", od.fileFor(b).exists());
// Now use b but in a different commit than what is hidden.
@@ -255,7 +255,7 @@ public class ReceivePackRefFilterTest extends LocalDiskRepositoryTestCase {
}
public void testUsingHiddenDeltaBaseFails() throws Exception {
- final TemporaryBuffer.Heap pack = new TemporaryBuffer.Heap(64);
+ final TemporaryBuffer.Heap pack = new TemporaryBuffer.Heap(1024);
packHeader(pack, 1);
pack.write((Constants.OBJ_REF_DELTA) << 4 | 4);
b.copyRawTo(pack);
@@ -292,18 +292,18 @@ public class ReceivePackRefFilterTest extends LocalDiskRepositoryTestCase {
public void testUsingHiddenCommonBlobFails() throws Exception {
// Try to use the 'b' blob that is hidden.
//
- TestRepository s = new TestRepository(src);
+ TestRepository<Repository> s = new TestRepository<Repository>(src);
RevCommit N = s.commit().parent(B).add("q", s.blob("b")).create();
// But don't include it in the pack.
//
- final TemporaryBuffer.Heap pack = new TemporaryBuffer.Heap(64);
+ final TemporaryBuffer.Heap pack = new TemporaryBuffer.Heap(1024);
packHeader(pack, 2);
- copy(pack, src.openObject(N));
- copy(pack,src.openObject(s.parseBody(N).getTree()));
+ copy(pack, src.open(N));
+ copy(pack,src.open(s.parseBody(N).getTree()));
digest(pack);
- final TemporaryBuffer.Heap inBuf = new TemporaryBuffer.Heap(256);
+ final TemporaryBuffer.Heap inBuf = new TemporaryBuffer.Heap(1024);
final PacketLineOut inPckLine = new PacketLineOut(inBuf);
inPckLine.writeString(ObjectId.zeroId().name() + ' ' + N.name() + ' '
+ "refs/heads/s" + '\0'
@@ -333,19 +333,19 @@ public class ReceivePackRefFilterTest extends LocalDiskRepositoryTestCase {
public void testUsingUnknownBlobFails() throws Exception {
// Try to use the 'n' blob that is not on the server.
//
- TestRepository s = new TestRepository(src);
+ TestRepository<Repository> s = new TestRepository<Repository>(src);
RevBlob n = s.blob("n");
RevCommit N = s.commit().parent(B).add("q", n).create();
// But don't include it in the pack.
//
- final TemporaryBuffer.Heap pack = new TemporaryBuffer.Heap(64);
+ final TemporaryBuffer.Heap pack = new TemporaryBuffer.Heap(1024);
packHeader(pack, 2);
- copy(pack, src.openObject(N));
- copy(pack,src.openObject(s.parseBody(N).getTree()));
+ copy(pack, src.open(N));
+ copy(pack,src.open(s.parseBody(N).getTree()));
digest(pack);
- final TemporaryBuffer.Heap inBuf = new TemporaryBuffer.Heap(256);
+ final TemporaryBuffer.Heap inBuf = new TemporaryBuffer.Heap(1024);
final PacketLineOut inPckLine = new PacketLineOut(inBuf);
inPckLine.writeString(ObjectId.zeroId().name() + ' ' + N.name() + ' '
+ "refs/heads/s" + '\0'
@@ -373,18 +373,18 @@ public class ReceivePackRefFilterTest extends LocalDiskRepositoryTestCase {
}
public void testUsingUnknownTreeFails() throws Exception {
- TestRepository s = new TestRepository(src);
+ TestRepository<Repository> s = new TestRepository<Repository>(src);
RevCommit N = s.commit().parent(B).add("q", s.blob("a")).create();
RevTree t = s.parseBody(N).getTree();
// Don't include the tree in the pack.
//
- final TemporaryBuffer.Heap pack = new TemporaryBuffer.Heap(64);
+ final TemporaryBuffer.Heap pack = new TemporaryBuffer.Heap(1024);
packHeader(pack, 1);
- copy(pack, src.openObject(N));
+ copy(pack, src.open(N));
digest(pack);
- final TemporaryBuffer.Heap inBuf = new TemporaryBuffer.Heap(256);
+ final TemporaryBuffer.Heap inBuf = new TemporaryBuffer.Heap(1024);
final PacketLineOut inPckLine = new PacketLineOut(inBuf);
inPckLine.writeString(ObjectId.zeroId().name() + ' ' + N.name() + ' '
+ "refs/heads/s" + '\0'
diff --git a/org.eclipse.jgit.test/tst/org/eclipse/jgit/transport/TransportTest.java b/org.eclipse.jgit.test/tst/org/eclipse/jgit/transport/TransportTest.java
index e3518251fe..a6bdd8886f 100644
--- a/org.eclipse.jgit.test/tst/org/eclipse/jgit/transport/TransportTest.java
+++ b/org.eclipse.jgit.test/tst/org/eclipse/jgit/transport/TransportTest.java
@@ -48,7 +48,7 @@ import java.util.Arrays;
import java.util.Collection;
import java.util.Collections;
-import org.eclipse.jgit.lib.RepositoryConfig;
+import org.eclipse.jgit.lib.Config;
import org.eclipse.jgit.lib.SampleDataRepositoryTestCase;
public class TransportTest extends SampleDataRepositoryTestCase {
@@ -59,7 +59,7 @@ public class TransportTest extends SampleDataRepositoryTestCase {
@Override
public void setUp() throws Exception {
super.setUp();
- final RepositoryConfig config = db.getConfig();
+ final Config config = db.getConfig();
remoteConfig = new RemoteConfig(config, "test");
remoteConfig.addURI(new URIish("http://everyones.loves.git/u/2"));
transport = null;
diff --git a/org.eclipse.jgit.test/tst/org/eclipse/jgit/treewalk/AbstractTreeIteratorTest.java b/org.eclipse.jgit.test/tst/org/eclipse/jgit/treewalk/AbstractTreeIteratorTest.java
index e96445a30a..12c11482ae 100644
--- a/org.eclipse.jgit.test/tst/org/eclipse/jgit/treewalk/AbstractTreeIteratorTest.java
+++ b/org.eclipse.jgit.test/tst/org/eclipse/jgit/treewalk/AbstractTreeIteratorTest.java
@@ -51,7 +51,7 @@ import junit.framework.TestCase;
import org.eclipse.jgit.errors.IncorrectObjectTypeException;
import org.eclipse.jgit.lib.Constants;
import org.eclipse.jgit.lib.FileMode;
-import org.eclipse.jgit.lib.Repository;
+import org.eclipse.jgit.lib.ObjectReader;
public class AbstractTreeIteratorTest extends TestCase {
@@ -73,7 +73,7 @@ public class AbstractTreeIteratorTest extends TestCase {
}
@Override
- public AbstractTreeIterator createSubtreeIterator(Repository repo)
+ public AbstractTreeIterator createSubtreeIterator(ObjectReader reader)
throws IncorrectObjectTypeException, IOException {
return null;
}
diff --git a/org.eclipse.jgit.test/tst/org/eclipse/jgit/treewalk/EmptyTreeIteratorTest.java b/org.eclipse.jgit.test/tst/org/eclipse/jgit/treewalk/EmptyTreeIteratorTest.java
index 111264b1c9..1ea2dc6250 100644
--- a/org.eclipse.jgit.test/tst/org/eclipse/jgit/treewalk/EmptyTreeIteratorTest.java
+++ b/org.eclipse.jgit.test/tst/org/eclipse/jgit/treewalk/EmptyTreeIteratorTest.java
@@ -44,6 +44,7 @@
package org.eclipse.jgit.treewalk;
import org.eclipse.jgit.lib.ObjectId;
+import org.eclipse.jgit.lib.ObjectReader;
import org.eclipse.jgit.lib.RepositoryTestCase;
public class EmptyTreeIteratorTest extends RepositoryTestCase {
@@ -55,7 +56,8 @@ public class EmptyTreeIteratorTest extends RepositoryTestCase {
public void testCreateSubtreeIterator() throws Exception {
final EmptyTreeIterator etp = new EmptyTreeIterator();
- final AbstractTreeIterator sub = etp.createSubtreeIterator(db);
+ final ObjectReader reader = db.newObjectReader();
+ final AbstractTreeIterator sub = etp.createSubtreeIterator(reader);
assertNotNull(sub);
assertTrue(sub.first());
assertTrue(sub.eof());
@@ -106,7 +108,8 @@ public class EmptyTreeIteratorTest extends RepositoryTestCase {
called[0] = true;
}
};
- parent.createSubtreeIterator(db).stopWalk();
+ final ObjectReader reader = db.newObjectReader();
+ parent.createSubtreeIterator(reader).stopWalk();
assertTrue(called[0]);
}
}
diff --git a/org.eclipse.jgit.test/tst/org/eclipse/jgit/treewalk/FileTreeIteratorTest.java b/org.eclipse.jgit.test/tst/org/eclipse/jgit/treewalk/FileTreeIteratorTest.java
index eb08e495b9..f939c90d81 100644
--- a/org.eclipse.jgit.test/tst/org/eclipse/jgit/treewalk/FileTreeIteratorTest.java
+++ b/org.eclipse.jgit.test/tst/org/eclipse/jgit/treewalk/FileTreeIteratorTest.java
@@ -49,6 +49,7 @@ import java.security.MessageDigest;
import org.eclipse.jgit.lib.Constants;
import org.eclipse.jgit.lib.FileMode;
import org.eclipse.jgit.lib.ObjectId;
+import org.eclipse.jgit.lib.ObjectReader;
import org.eclipse.jgit.lib.RepositoryTestCase;
import org.eclipse.jgit.util.RawParseUtils;
@@ -124,7 +125,8 @@ public class FileTreeIteratorTest extends RepositoryTestCase {
assertFalse(top.eof());
assertEquals(FileMode.TREE.getBits(), top.mode);
- final AbstractTreeIterator sub = top.createSubtreeIterator(db);
+ final ObjectReader reader = db.newObjectReader();
+ final AbstractTreeIterator sub = top.createSubtreeIterator(reader);
assertTrue(sub instanceof FileTreeIterator);
final FileTreeIterator subfti = (FileTreeIterator) sub;
assertTrue(sub.first());
diff --git a/org.eclipse.jgit.test/tst/org/eclipse/jgit/treewalk/NameConflictTreeWalkTest.java b/org.eclipse.jgit.test/tst/org/eclipse/jgit/treewalk/NameConflictTreeWalkTest.java
index 35298b803f..675331baf4 100644
--- a/org.eclipse.jgit.test/tst/org/eclipse/jgit/treewalk/NameConflictTreeWalkTest.java
+++ b/org.eclipse.jgit.test/tst/org/eclipse/jgit/treewalk/NameConflictTreeWalkTest.java
@@ -66,8 +66,8 @@ public class NameConflictTreeWalkTest extends RepositoryTestCase {
private static final FileMode EXECUTABLE_FILE = FileMode.EXECUTABLE_FILE;
public void testNoDF_NoGap() throws Exception {
- final DirCache tree0 = DirCache.read(db);
- final DirCache tree1 = DirCache.read(db);
+ final DirCache tree0 = db.readDirCache();
+ final DirCache tree1 = db.readDirCache();
{
final DirCacheBuilder b0 = tree0.builder();
final DirCacheBuilder b1 = tree1.builder();
@@ -97,8 +97,8 @@ public class NameConflictTreeWalkTest extends RepositoryTestCase {
}
public void testDF_NoGap() throws Exception {
- final DirCache tree0 = DirCache.read(db);
- final DirCache tree1 = DirCache.read(db);
+ final DirCache tree0 = db.readDirCache();
+ final DirCache tree1 = db.readDirCache();
{
final DirCacheBuilder b0 = tree0.builder();
final DirCacheBuilder b1 = tree1.builder();
@@ -128,8 +128,8 @@ public class NameConflictTreeWalkTest extends RepositoryTestCase {
}
public void testDF_GapByOne() throws Exception {
- final DirCache tree0 = DirCache.read(db);
- final DirCache tree1 = DirCache.read(db);
+ final DirCache tree0 = db.readDirCache();
+ final DirCache tree1 = db.readDirCache();
{
final DirCacheBuilder b0 = tree0.builder();
final DirCacheBuilder b1 = tree1.builder();
@@ -160,8 +160,8 @@ public class NameConflictTreeWalkTest extends RepositoryTestCase {
}
public void testDF_SkipsSeenSubtree() throws Exception {
- final DirCache tree0 = DirCache.read(db);
- final DirCache tree1 = DirCache.read(db);
+ final DirCache tree0 = db.readDirCache();
+ final DirCache tree1 = db.readDirCache();
{
final DirCacheBuilder b0 = tree0.builder();
final DirCacheBuilder b1 = tree1.builder();
diff --git a/org.eclipse.jgit.test/tst/org/eclipse/jgit/treewalk/PostOrderTreeWalkTest.java b/org.eclipse.jgit.test/tst/org/eclipse/jgit/treewalk/PostOrderTreeWalkTest.java
index d136b8f297..274df5bec0 100644
--- a/org.eclipse.jgit.test/tst/org/eclipse/jgit/treewalk/PostOrderTreeWalkTest.java
+++ b/org.eclipse.jgit.test/tst/org/eclipse/jgit/treewalk/PostOrderTreeWalkTest.java
@@ -86,7 +86,7 @@ public class PostOrderTreeWalkTest extends RepositoryTestCase {
}
public void testNoPostOrder() throws Exception {
- final DirCache tree = DirCache.read(db);
+ final DirCache tree = db.readDirCache();
{
final DirCacheBuilder b = tree.builder();
@@ -115,7 +115,7 @@ public class PostOrderTreeWalkTest extends RepositoryTestCase {
}
public void testWithPostOrder_EnterSubtree() throws Exception {
- final DirCache tree = DirCache.read(db);
+ final DirCache tree = db.readDirCache();
{
final DirCacheBuilder b = tree.builder();
@@ -150,7 +150,7 @@ public class PostOrderTreeWalkTest extends RepositoryTestCase {
}
public void testWithPostOrder_NoEnterSubtree() throws Exception {
- final DirCache tree = DirCache.read(db);
+ final DirCache tree = db.readDirCache();
{
final DirCacheBuilder b = tree.builder();
diff --git a/org.eclipse.jgit.test/tst/org/eclipse/jgit/treewalk/filter/PathSuffixFilterTestCase.java b/org.eclipse.jgit.test/tst/org/eclipse/jgit/treewalk/filter/PathSuffixFilterTestCase.java
index 1aaefc415f..302eada999 100644
--- a/org.eclipse.jgit.test/tst/org/eclipse/jgit/treewalk/filter/PathSuffixFilterTestCase.java
+++ b/org.eclipse.jgit.test/tst/org/eclipse/jgit/treewalk/filter/PathSuffixFilterTestCase.java
@@ -43,6 +43,8 @@
package org.eclipse.jgit.treewalk.filter;
+import static org.eclipse.jgit.lib.Constants.OBJ_BLOB;
+
import java.io.IOException;
import java.util.LinkedList;
import java.util.List;
@@ -52,17 +54,17 @@ import org.eclipse.jgit.dircache.DirCacheBuilder;
import org.eclipse.jgit.dircache.DirCacheEntry;
import org.eclipse.jgit.lib.FileMode;
import org.eclipse.jgit.lib.ObjectId;
-import org.eclipse.jgit.lib.ObjectWriter;
+import org.eclipse.jgit.lib.ObjectInserter;
import org.eclipse.jgit.lib.RepositoryTestCase;
import org.eclipse.jgit.treewalk.TreeWalk;
public class PathSuffixFilterTestCase extends RepositoryTestCase {
public void testNonRecursiveFiltering() throws IOException {
- final ObjectWriter ow = new ObjectWriter(db);
- final ObjectId aSth = ow.writeBlob("a.sth".getBytes());
- final ObjectId aTxt = ow.writeBlob("a.txt".getBytes());
- final DirCache dc = DirCache.read(db);
+ final ObjectInserter odi = db.newObjectInserter();
+ final ObjectId aSth = odi.insert(OBJ_BLOB, "a.sth".getBytes());
+ final ObjectId aTxt = odi.insert(OBJ_BLOB, "a.txt".getBytes());
+ final DirCache dc = db.readDirCache();
final DirCacheBuilder builder = dc.builder();
final DirCacheEntry aSthEntry = new DirCacheEntry("a.sth");
aSthEntry.setFileMode(FileMode.REGULAR_FILE);
@@ -73,7 +75,8 @@ public class PathSuffixFilterTestCase extends RepositoryTestCase {
builder.add(aSthEntry);
builder.add(aTxtEntry);
builder.finish();
- final ObjectId treeId = dc.writeTree(ow);
+ final ObjectId treeId = dc.writeTree(odi);
+ odi.flush();
final TreeWalk tw = new TreeWalk(db);
@@ -92,12 +95,12 @@ public class PathSuffixFilterTestCase extends RepositoryTestCase {
}
public void testRecursiveFiltering() throws IOException {
- final ObjectWriter ow = new ObjectWriter(db);
- final ObjectId aSth = ow.writeBlob("a.sth".getBytes());
- final ObjectId aTxt = ow.writeBlob("a.txt".getBytes());
- final ObjectId bSth = ow.writeBlob("b.sth".getBytes());
- final ObjectId bTxt = ow.writeBlob("b.txt".getBytes());
- final DirCache dc = DirCache.read(db);
+ final ObjectInserter odi = db.newObjectInserter();
+ final ObjectId aSth = odi.insert(OBJ_BLOB, "a.sth".getBytes());
+ final ObjectId aTxt = odi.insert(OBJ_BLOB, "a.txt".getBytes());
+ final ObjectId bSth = odi.insert(OBJ_BLOB, "b.sth".getBytes());
+ final ObjectId bTxt = odi.insert(OBJ_BLOB, "b.txt".getBytes());
+ final DirCache dc = db.readDirCache();
final DirCacheBuilder builder = dc.builder();
final DirCacheEntry aSthEntry = new DirCacheEntry("a.sth");
aSthEntry.setFileMode(FileMode.REGULAR_FILE);
@@ -116,7 +119,8 @@ public class PathSuffixFilterTestCase extends RepositoryTestCase {
builder.add(bSthEntry);
builder.add(bTxtEntry);
builder.finish();
- final ObjectId treeId = dc.writeTree(ow);
+ final ObjectId treeId = dc.writeTree(odi);
+ odi.flush();
final TreeWalk tw = new TreeWalk(db);
diff --git a/org.eclipse.jgit/META-INF/MANIFEST.MF b/org.eclipse.jgit/META-INF/MANIFEST.MF
index 51d440e58e..a8f39d93bf 100644
--- a/org.eclipse.jgit/META-INF/MANIFEST.MF
+++ b/org.eclipse.jgit/META-INF/MANIFEST.MF
@@ -9,6 +9,7 @@ Export-Package: org.eclipse.jgit;version="0.9.0",
org.eclipse.jgit.api;version="0.9.0",
org.eclipse.jgit.diff;version="0.9.0",
org.eclipse.jgit.dircache;version="0.9.0",
+ org.eclipse.jgit.events;version="0.9.0",
org.eclipse.jgit.errors;version="0.9.0",
org.eclipse.jgit.fnmatch;version="0.9.0",
org.eclipse.jgit.ignore;version="0.9.0",
@@ -19,6 +20,8 @@ Export-Package: org.eclipse.jgit;version="0.9.0",
org.eclipse.jgit.revplot;version="0.9.0",
org.eclipse.jgit.revwalk;version="0.9.0",
org.eclipse.jgit.revwalk.filter;version="0.9.0",
+ org.eclipse.jgit.storage.file;version="0.9.0",
+ org.eclipse.jgit.storage.pack;version="0.9.0",
org.eclipse.jgit.transport;version="0.9.0",
org.eclipse.jgit.treewalk;version="0.9.0",
org.eclipse.jgit.treewalk.filter;version="0.9.0",
diff --git a/org.eclipse.jgit/resources/org/eclipse/jgit/JGitText.properties b/org.eclipse.jgit/resources/org/eclipse/jgit/JGitText.properties
index ca1832e571..88c6c8e3a4 100644
--- a/org.eclipse.jgit/resources/org/eclipse/jgit/JGitText.properties
+++ b/org.eclipse.jgit/resources/org/eclipse/jgit/JGitText.properties
@@ -127,7 +127,7 @@ duplicateAdvertisementsOf=duplicate advertisements of {0}
duplicateRef=Duplicate ref: {0}
duplicateRemoteRefUpdateIsIllegal=Duplicate remote ref update is illegal. Affected remote name: {0}
duplicateStagesNotAllowed=Duplicate stages not allowed
-eitherGIT_DIRorGIT_WORK_TREEmustBePassed=Either GIT_DIR or GIT_WORK_TREE must be passed to Repository constructor
+eitherGitDirOrWorkTreeRequired=One of setGitDir or setWorkTree must be called.
emptyPathNotPermitted=Empty path not permitted.
encryptionError=Encryption error: {0}
endOfFileInEscape=End of file in escape
@@ -224,6 +224,7 @@ mergeStrategyAlreadyExistsAsDefault=Merge strategy "{0}" already exists as a def
mergeStrategyDoesNotSupportHeads=merge strategy {0} does not support {1} heads to be merged into HEAD
mergeUsingStrategyResultedInDescription=Merge using strategy {0} resulted in: {1}. {2}
missingAccesskey=Missing accesskey.
+missingDeltaBase=delta base
missingForwardImageInGITBinaryPatch=Missing forward-image in GIT binary patch
missingObject=Missing {0} {1}
missingPrerequisiteCommits=missing prerequisite commits:
@@ -304,6 +305,7 @@ renamesAlreadyFound=Renames have already been found.
renamesFindingByContent=Finding renames by content similarity
renamesFindingExact=Finding exact renames
repositoryAlreadyExists=Repository already exists: {0}
+repositoryConfigFileInvalid=Repository config file {0} invalid {1}
repositoryIsRequired=Repository is required.
repositoryNotFound=repository not found: {0}
repositoryState_applyMailbox=Apply mailbox
diff --git a/org.eclipse.jgit/src/org/eclipse/jgit/JGitText.java b/org.eclipse.jgit/src/org/eclipse/jgit/JGitText.java
index 71e5e4a899..9df9887af4 100644
--- a/org.eclipse.jgit/src/org/eclipse/jgit/JGitText.java
+++ b/org.eclipse.jgit/src/org/eclipse/jgit/JGitText.java
@@ -187,7 +187,7 @@ public class JGitText extends TranslationBundle {
/***/ public String duplicateRef;
/***/ public String duplicateRemoteRefUpdateIsIllegal;
/***/ public String duplicateStagesNotAllowed;
- /***/ public String eitherGIT_DIRorGIT_WORK_TREEmustBePassed;
+ /***/ public String eitherGitDirOrWorkTreeRequired;
/***/ public String emptyPathNotPermitted;
/***/ public String encryptionError;
/***/ public String endOfFileInEscape;
@@ -284,6 +284,7 @@ public class JGitText extends TranslationBundle {
/***/ public String mergeStrategyDoesNotSupportHeads;
/***/ public String mergeUsingStrategyResultedInDescription;
/***/ public String missingAccesskey;
+ /***/ public String missingDeltaBase;
/***/ public String missingForwardImageInGITBinaryPatch;
/***/ public String missingObject;
/***/ public String missingPrerequisiteCommits;
@@ -363,6 +364,7 @@ public class JGitText extends TranslationBundle {
/***/ public String renamesFindingByContent;
/***/ public String renamesFindingExact;
/***/ public String repositoryAlreadyExists;
+ /***/ public String repositoryConfigFileInvalid;
/***/ public String repositoryIsRequired;
/***/ public String repositoryNotFound;
/***/ public String repositoryState_applyMailbox;
diff --git a/org.eclipse.jgit/src/org/eclipse/jgit/api/AddCommand.java b/org.eclipse.jgit/src/org/eclipse/jgit/api/AddCommand.java
index 157b85f9c9..e41ab580bb 100644
--- a/org.eclipse.jgit/src/org/eclipse/jgit/api/AddCommand.java
+++ b/org.eclipse.jgit/src/org/eclipse/jgit/api/AddCommand.java
@@ -127,7 +127,7 @@ public class AddCommand extends GitCommand<DirCache> {
addAll = true;
try {
- dc = DirCache.lock(repo);
+ dc = repo.lockDirCache();
ObjectWriter ow = new ObjectWriter(repo);
DirCacheIterator c;
@@ -147,7 +147,7 @@ public class AddCommand extends GitCommand<DirCache> {
while (tw.next()) {
String path = tw.getPathString();
- final File file = new File(repo.getWorkDir(), path);
+ final File file = new File(repo.getWorkTree(), path);
WorkingTreeIterator f = tw.getTree(1, WorkingTreeIterator.class);
if (tw.getTree(0, DirCacheIterator.class) == null &&
f != null && f.isEntryIgnored()) {
diff --git a/org.eclipse.jgit/src/org/eclipse/jgit/api/CommitCommand.java b/org.eclipse.jgit/src/org/eclipse/jgit/api/CommitCommand.java
index eef952e7cd..17b7113470 100644
--- a/org.eclipse.jgit/src/org/eclipse/jgit/api/CommitCommand.java
+++ b/org.eclipse.jgit/src/org/eclipse/jgit/api/CommitCommand.java
@@ -54,13 +54,13 @@ import org.eclipse.jgit.errors.UnmergedPathException;
import org.eclipse.jgit.lib.Commit;
import org.eclipse.jgit.lib.Constants;
import org.eclipse.jgit.lib.ObjectId;
-import org.eclipse.jgit.lib.ObjectWriter;
+import org.eclipse.jgit.lib.ObjectInserter;
import org.eclipse.jgit.lib.PersonIdent;
import org.eclipse.jgit.lib.Ref;
import org.eclipse.jgit.lib.RefUpdate;
-import org.eclipse.jgit.lib.RefUpdate.Result;
import org.eclipse.jgit.lib.Repository;
import org.eclipse.jgit.lib.RepositoryState;
+import org.eclipse.jgit.lib.RefUpdate.Result;
import org.eclipse.jgit.revwalk.RevCommit;
import org.eclipse.jgit.revwalk.RevWalk;
@@ -139,54 +139,66 @@ public class CommitCommand extends GitCommand<RevCommit> {
parents.add(0, headId);
// lock the index
- DirCache index = DirCache.lock(repo);
+ DirCache index = repo.lockDirCache();
try {
- ObjectWriter repoWriter = new ObjectWriter(repo);
-
- // Write the index as tree to the object database. This may fail
- // for example when the index contains unmerged pathes
- // (unresolved conflicts)
- ObjectId indexTreeId = index.writeTree(repoWriter);
+ ObjectInserter odi = repo.newObjectInserter();
+ try {
+ // Write the index as tree to the object database. This may
+ // fail for example when the index contains unmerged paths
+ // (unresolved conflicts)
+ ObjectId indexTreeId = index.writeTree(odi);
- // Create a Commit object, populate it and write it
- Commit commit = new Commit(repo);
- commit.setCommitter(committer);
- commit.setAuthor(author);
- commit.setMessage(message);
+ // Create a Commit object, populate it and write it
+ Commit commit = new Commit(repo);
+ commit.setCommitter(committer);
+ commit.setAuthor(author);
+ commit.setMessage(message);
- commit.setParentIds(parents.toArray(new ObjectId[]{}));
- commit.setTreeId(indexTreeId);
- ObjectId commitId = repoWriter.writeCommit(commit);
+ commit.setParentIds(parents.toArray(new ObjectId[] {}));
+ commit.setTreeId(indexTreeId);
+ ObjectId commitId = odi.insert(Constants.OBJ_COMMIT, odi
+ .format(commit));
+ odi.flush();
- RevCommit revCommit = new RevWalk(repo).parseCommit(commitId);
- RefUpdate ru = repo.updateRef(Constants.HEAD);
- ru.setNewObjectId(commitId);
- ru.setRefLogMessage("commit : " + revCommit.getShortMessage(),
- false);
+ RevWalk revWalk = new RevWalk(repo);
+ try {
+ RevCommit revCommit = revWalk.parseCommit(commitId);
+ RefUpdate ru = repo.updateRef(Constants.HEAD);
+ ru.setNewObjectId(commitId);
+ ru.setRefLogMessage("commit : "
+ + revCommit.getShortMessage(), false);
- ru.setExpectedOldObjectId(headId);
- Result rc = ru.update();
- switch (rc) {
- case NEW:
- case FAST_FORWARD:
- setCallable(false);
- if (state == RepositoryState.MERGING_RESOLVED) {
- // Commit was successful. Now delete the files
- // used for merge commits
- new File(repo.getDirectory(), Constants.MERGE_HEAD)
- .delete();
- new File(repo.getDirectory(), Constants.MERGE_MSG)
- .delete();
+ ru.setExpectedOldObjectId(headId);
+ Result rc = ru.update();
+ switch (rc) {
+ case NEW:
+ case FAST_FORWARD: {
+ setCallable(false);
+ File meta = repo.getDirectory();
+ if (state == RepositoryState.MERGING_RESOLVED
+ && meta != null) {
+ // Commit was successful. Now delete the files
+ // used for merge commits
+ new File(meta, Constants.MERGE_HEAD).delete();
+ new File(meta, Constants.MERGE_MSG).delete();
+ }
+ return revCommit;
+ }
+ case REJECTED:
+ case LOCK_FAILURE:
+ throw new ConcurrentRefUpdateException(JGitText
+ .get().couldNotLockHEAD, ru.getRef(), rc);
+ default:
+ throw new JGitInternalException(MessageFormat
+ .format(JGitText.get().updatingRefFailed,
+ Constants.HEAD,
+ commitId.toString(), rc));
+ }
+ } finally {
+ revWalk.release();
}
- return revCommit;
- case REJECTED:
- case LOCK_FAILURE:
- throw new ConcurrentRefUpdateException(
- JGitText.get().couldNotLockHEAD, ru.getRef(), rc);
- default:
- throw new JGitInternalException(MessageFormat.format(
- JGitText.get().updatingRefFailed
- , Constants.HEAD, commitId.toString(), rc));
+ } finally {
+ odi.release();
}
} finally {
index.unlock();
diff --git a/org.eclipse.jgit/src/org/eclipse/jgit/api/MergeCommand.java b/org.eclipse.jgit/src/org/eclipse/jgit/api/MergeCommand.java
index 00a0309152..972aa618ad 100644
--- a/org.eclipse.jgit/src/org/eclipse/jgit/api/MergeCommand.java
+++ b/org.eclipse.jgit/src/org/eclipse/jgit/api/MergeCommand.java
@@ -119,37 +119,41 @@ public class MergeCommand extends GitCommand<MergeResult> {
// Check for FAST_FORWARD, ALREADY_UP_TO_DATE
RevWalk revWalk = new RevWalk(repo);
- RevCommit headCommit = revWalk.lookupCommit(head.getObjectId());
-
- Ref ref = commits.get(0);
-
- refLogMessage.append(ref.getName());
-
- // handle annotated tags
- ObjectId objectId = ref.getPeeledObjectId();
- if (objectId == null)
- objectId = ref.getObjectId();
-
- RevCommit srcCommit = revWalk.lookupCommit(objectId);
- if (revWalk.isMergedInto(srcCommit, headCommit)) {
- setCallable(false);
- return new MergeResult(headCommit,
- MergeStatus.ALREADY_UP_TO_DATE, mergeStrategy);
- } else if (revWalk.isMergedInto(headCommit, srcCommit)) {
- // FAST_FORWARD detected: skip doing a real merge but only
- // update HEAD
- refLogMessage.append(": " + MergeStatus.FAST_FORWARD);
- checkoutNewHead(revWalk, headCommit, srcCommit);
- updateHead(refLogMessage, srcCommit, head.getObjectId());
- setCallable(false);
- return new MergeResult(srcCommit, MergeStatus.FAST_FORWARD,
- mergeStrategy);
- } else {
- return new MergeResult(
- headCommit,
- MergeResult.MergeStatus.NOT_SUPPORTED,
- mergeStrategy,
- JGitText.get().onlyAlreadyUpToDateAndFastForwardMergesAreAvailable);
+ try {
+ RevCommit headCommit = revWalk.lookupCommit(head.getObjectId());
+
+ Ref ref = commits.get(0);
+
+ refLogMessage.append(ref.getName());
+
+ // handle annotated tags
+ ObjectId objectId = ref.getPeeledObjectId();
+ if (objectId == null)
+ objectId = ref.getObjectId();
+
+ RevCommit srcCommit = revWalk.lookupCommit(objectId);
+ if (revWalk.isMergedInto(srcCommit, headCommit)) {
+ setCallable(false);
+ return new MergeResult(headCommit,
+ MergeStatus.ALREADY_UP_TO_DATE, mergeStrategy);
+ } else if (revWalk.isMergedInto(headCommit, srcCommit)) {
+ // FAST_FORWARD detected: skip doing a real merge but only
+ // update HEAD
+ refLogMessage.append(": " + MergeStatus.FAST_FORWARD);
+ checkoutNewHead(revWalk, headCommit, srcCommit);
+ updateHead(refLogMessage, srcCommit, head.getObjectId());
+ setCallable(false);
+ return new MergeResult(srcCommit, MergeStatus.FAST_FORWARD,
+ mergeStrategy);
+ } else {
+ return new MergeResult(
+ headCommit,
+ MergeResult.MergeStatus.NOT_SUPPORTED,
+ mergeStrategy,
+ JGitText.get().onlyAlreadyUpToDateAndFastForwardMergesAreAvailable);
+ }
+ } finally {
+ revWalk.release();
}
} catch (IOException e) {
throw new JGitInternalException(
@@ -163,7 +167,7 @@ public class MergeCommand extends GitCommand<MergeResult> {
RevCommit newHeadCommit) throws IOException, CheckoutConflictException {
GitIndex index = repo.getIndex();
- File workDir = repo.getWorkDir();
+ File workDir = repo.getWorkTree();
if (workDir != null) {
WorkDirCheckout workDirCheckout = new WorkDirCheckout(repo,
workDir, headCommit.asCommit(revWalk).getTree(), index,
diff --git a/org.eclipse.jgit/src/org/eclipse/jgit/diff/DiffFormatter.java b/org.eclipse.jgit/src/org/eclipse/jgit/diff/DiffFormatter.java
index cdcc5e63e4..835cf6f865 100644
--- a/org.eclipse.jgit/src/org/eclipse/jgit/diff/DiffFormatter.java
+++ b/org.eclipse.jgit/src/org/eclipse/jgit/diff/DiffFormatter.java
@@ -50,20 +50,24 @@ import static org.eclipse.jgit.lib.FileMode.GITLINK;
import java.io.ByteArrayOutputStream;
import java.io.IOException;
+import java.io.InputStream;
import java.io.OutputStream;
import java.util.List;
import org.eclipse.jgit.JGitText;
import org.eclipse.jgit.errors.CorruptObjectException;
+import org.eclipse.jgit.errors.LargeObjectException;
import org.eclipse.jgit.errors.MissingObjectException;
import org.eclipse.jgit.lib.AbbreviatedObjectId;
import org.eclipse.jgit.lib.Constants;
+import org.eclipse.jgit.lib.CoreConfig;
import org.eclipse.jgit.lib.FileMode;
import org.eclipse.jgit.lib.ObjectLoader;
import org.eclipse.jgit.lib.Repository;
import org.eclipse.jgit.patch.FileHeader;
import org.eclipse.jgit.patch.HunkHeader;
import org.eclipse.jgit.patch.FileHeader.PatchType;
+import org.eclipse.jgit.util.IO;
import org.eclipse.jgit.util.QuotedString;
import org.eclipse.jgit.util.io.DisabledOutputStream;
@@ -83,6 +87,8 @@ public class DiffFormatter {
private RawText.Factory rawTextFactory = RawText.FACTORY;
+ private long bigFileThreshold = 50 * 1024 * 1024;
+
/**
* Create a new formatter with a default level of context.
*
@@ -110,6 +116,9 @@ public class DiffFormatter {
*/
public void setRepository(Repository repository) {
db = repository;
+
+ CoreConfig cfg = db.getConfig().get(CoreConfig.KEY);
+ bigFileThreshold = cfg.getStreamFileThreshold();
}
/**
@@ -158,6 +167,19 @@ public class DiffFormatter {
}
/**
+ * Set the maximum file size that should be considered for diff output.
+ * <p>
+ * Text files that are larger than this size will not have a difference
+ * generated during output.
+ *
+ * @param bigFileThreshold
+ * the limit, in bytes.
+ */
+ public void setBigFileThreshold(long bigFileThreshold) {
+ this.bigFileThreshold = bigFileThreshold;
+ }
+
+ /**
* Flush the underlying output stream of this formatter.
*
* @throws IOException
@@ -318,9 +340,32 @@ public class DiffFormatter {
if (db == null)
throw new IllegalStateException(JGitText.get().repositoryIsRequired);
+
if (id.isComplete()) {
- ObjectLoader ldr = db.openObject(id.toObjectId());
- return ldr.getCachedBytes();
+ ObjectLoader ldr = db.open(id.toObjectId());
+ if (!ldr.isLarge())
+ return ldr.getCachedBytes();
+
+ long sz = ldr.getSize();
+ if (sz < bigFileThreshold && sz < Integer.MAX_VALUE) {
+ byte[] buf;
+ try {
+ buf = new byte[(int) sz];
+ } catch (OutOfMemoryError noMemory) {
+ LargeObjectException e;
+
+ e = new LargeObjectException(id.toObjectId());
+ e.initCause(noMemory);
+ throw e;
+ }
+ InputStream in = ldr.openStream();
+ try {
+ IO.readFully(in, buf, 0, buf.length);
+ } finally {
+ in.close();
+ }
+ return buf;
+ }
}
return new byte[] {};
diff --git a/org.eclipse.jgit/src/org/eclipse/jgit/diff/RenameDetector.java b/org.eclipse.jgit/src/org/eclipse/jgit/diff/RenameDetector.java
index cf5615a1cb..a3203e349f 100644
--- a/org.eclipse.jgit/src/org/eclipse/jgit/diff/RenameDetector.java
+++ b/org.eclipse.jgit/src/org/eclipse/jgit/diff/RenameDetector.java
@@ -57,6 +57,7 @@ import org.eclipse.jgit.diff.DiffEntry.ChangeType;
import org.eclipse.jgit.lib.AbbreviatedObjectId;
import org.eclipse.jgit.lib.FileMode;
import org.eclipse.jgit.lib.NullProgressMonitor;
+import org.eclipse.jgit.lib.ObjectReader;
import org.eclipse.jgit.lib.ProgressMonitor;
import org.eclipse.jgit.lib.Repository;
@@ -294,14 +295,19 @@ public class RenameDetector {
return;
if (getRenameLimit() == 0 || cnt <= getRenameLimit()) {
- SimilarityRenameDetector d;
-
- d = new SimilarityRenameDetector(repo, deleted, added);
- d.setRenameScore(getRenameScore());
- d.compute(pm);
- deleted = d.getLeftOverSources();
- added = d.getLeftOverDestinations();
- entries.addAll(d.getMatches());
+ ObjectReader reader = repo.newObjectReader();
+ try {
+ SimilarityRenameDetector d;
+
+ d = new SimilarityRenameDetector(reader, deleted, added);
+ d.setRenameScore(getRenameScore());
+ d.compute(pm);
+ deleted = d.getLeftOverSources();
+ added = d.getLeftOverDestinations();
+ entries.addAll(d.getMatches());
+ } finally {
+ reader.release();
+ }
} else {
overRenameLimit = true;
}
diff --git a/org.eclipse.jgit/src/org/eclipse/jgit/diff/SimilarityIndex.java b/org.eclipse.jgit/src/org/eclipse/jgit/diff/SimilarityIndex.java
index d5a31d6044..22b74f461c 100644
--- a/org.eclipse.jgit/src/org/eclipse/jgit/diff/SimilarityIndex.java
+++ b/org.eclipse.jgit/src/org/eclipse/jgit/diff/SimilarityIndex.java
@@ -43,9 +43,14 @@
package org.eclipse.jgit.diff;
+import java.io.EOFException;
+import java.io.IOException;
+import java.io.InputStream;
import java.util.Arrays;
+import org.eclipse.jgit.errors.MissingObjectException;
import org.eclipse.jgit.lib.ObjectLoader;
+import org.eclipse.jgit.lib.ObjectStream;
/**
* Index structure of lines/blocks in one file.
@@ -107,10 +112,20 @@ class SimilarityIndex {
fileSize = size;
}
- void hash(ObjectLoader obj) {
- byte[] raw = obj.getCachedBytes();
- setFileSize(raw.length);
- hash(raw, 0, raw.length);
+ void hash(ObjectLoader obj) throws MissingObjectException, IOException {
+ if (obj.isLarge()) {
+ ObjectStream in = obj.openStream();
+ try {
+ setFileSize(in.getSize());
+ hash(in, fileSize);
+ } finally {
+ in.close();
+ }
+ } else {
+ byte[] raw = obj.getCachedBytes();
+ setFileSize(raw.length);
+ hash(raw, 0, raw.length);
+ }
}
void hash(byte[] raw, int ptr, final int end) {
@@ -129,6 +144,35 @@ class SimilarityIndex {
}
}
+ void hash(InputStream in, long remaining) throws IOException {
+ byte[] buf = new byte[4096];
+ int ptr = 0;
+ int cnt = 0;
+
+ while (0 < remaining) {
+ int hash = 5381;
+
+ // Hash one line, or one block, whichever occurs first.
+ int n = 0;
+ do {
+ if (ptr == cnt) {
+ ptr = 0;
+ cnt = in.read(buf, 0, buf.length);
+ if (cnt <= 0)
+ throw new EOFException();
+ }
+
+ n++;
+ int c = buf[ptr++] & 0xff;
+ if (c == '\n')
+ break;
+ hash = (hash << 5) ^ c;
+ } while (n < 64 && n < remaining);
+ add(hash, n);
+ remaining -= n;
+ }
+ }
+
/**
* Sort the internal table so it can be used for efficient scoring.
* <p>
diff --git a/org.eclipse.jgit/src/org/eclipse/jgit/diff/SimilarityRenameDetector.java b/org.eclipse.jgit/src/org/eclipse/jgit/diff/SimilarityRenameDetector.java
index e2115f0acc..d05fc2a313 100644
--- a/org.eclipse.jgit/src/org/eclipse/jgit/diff/SimilarityRenameDetector.java
+++ b/org.eclipse.jgit/src/org/eclipse/jgit/diff/SimilarityRenameDetector.java
@@ -50,11 +50,12 @@ import java.util.List;
import org.eclipse.jgit.JGitText;
import org.eclipse.jgit.diff.DiffEntry.ChangeType;
+import org.eclipse.jgit.lib.Constants;
import org.eclipse.jgit.lib.FileMode;
import org.eclipse.jgit.lib.NullProgressMonitor;
import org.eclipse.jgit.lib.ObjectId;
+import org.eclipse.jgit.lib.ObjectReader;
import org.eclipse.jgit.lib.ProgressMonitor;
-import org.eclipse.jgit.lib.Repository;
class SimilarityRenameDetector {
/**
@@ -71,7 +72,7 @@ class SimilarityRenameDetector {
private static final int SCORE_SHIFT = 2 * BITS_PER_INDEX;
- private final Repository repo;
+ private ObjectReader reader;
/**
* All sources to consider for copies or renames.
@@ -111,9 +112,9 @@ class SimilarityRenameDetector {
private List<DiffEntry> out;
- SimilarityRenameDetector(Repository repo, List<DiffEntry> srcs,
+ SimilarityRenameDetector(ObjectReader reader, List<DiffEntry> srcs,
List<DiffEntry> dsts) {
- this.repo = repo;
+ this.reader = reader;
this.srcs = srcs;
this.dsts = dsts;
}
@@ -336,13 +337,13 @@ class SimilarityRenameDetector {
private SimilarityIndex hash(ObjectId objectId) throws IOException {
SimilarityIndex r = new SimilarityIndex();
- r.hash(repo.openObject(objectId));
+ r.hash(reader.open(objectId));
r.sort();
return r;
}
private long size(ObjectId objectId) throws IOException {
- return repo.openObject(objectId).getSize();
+ return reader.getObjectSize(objectId, Constants.OBJ_BLOB);
}
private static int score(long value) {
diff --git a/org.eclipse.jgit/src/org/eclipse/jgit/dircache/DirCache.java b/org.eclipse.jgit/src/org/eclipse/jgit/dircache/DirCache.java
index 42fea48520..cc10fad2b4 100644
--- a/org.eclipse.jgit/src/org/eclipse/jgit/dircache/DirCache.java
+++ b/org.eclipse.jgit/src/org/eclipse/jgit/dircache/DirCache.java
@@ -64,10 +64,9 @@ import org.eclipse.jgit.JGitText;
import org.eclipse.jgit.errors.CorruptObjectException;
import org.eclipse.jgit.errors.UnmergedPathException;
import org.eclipse.jgit.lib.Constants;
-import org.eclipse.jgit.lib.LockFile;
import org.eclipse.jgit.lib.ObjectId;
-import org.eclipse.jgit.lib.ObjectWriter;
-import org.eclipse.jgit.lib.Repository;
+import org.eclipse.jgit.lib.ObjectInserter;
+import org.eclipse.jgit.storage.file.LockFile;
import org.eclipse.jgit.util.IO;
import org.eclipse.jgit.util.MutableInteger;
import org.eclipse.jgit.util.NB;
@@ -158,28 +157,6 @@ public class DirCache {
}
/**
- * Create a new in-core index representation and read an index from disk.
- * <p>
- * The new index will be read before it is returned to the caller. Read
- * failures are reported as exceptions and therefore prevent the method from
- * returning a partially populated index.
- *
- * @param db
- * repository the caller wants to read the default index of.
- * @return a cache representing the contents of the specified index file (if
- * it exists) or an empty cache if the file does not exist.
- * @throws IOException
- * the index file is present but could not be read.
- * @throws CorruptObjectException
- * the index file is using a format or extension that this
- * library does not support.
- */
- public static DirCache read(final Repository db)
- throws CorruptObjectException, IOException {
- return read(new File(db.getDirectory(), "index"));
- }
-
- /**
* Create a new in-core index representation, lock it, and read from disk.
* <p>
* The new index will be locked and then read before it is returned to the
@@ -220,29 +197,6 @@ public class DirCache {
return c;
}
- /**
- * Create a new in-core index representation, lock it, and read from disk.
- * <p>
- * The new index will be locked and then read before it is returned to the
- * caller. Read failures are reported as exceptions and therefore prevent
- * the method from returning a partially populated index.
- *
- * @param db
- * repository the caller wants to read the default index of.
- * @return a cache representing the contents of the specified index file (if
- * it exists) or an empty cache if the file does not exist.
- * @throws IOException
- * the index file is present but could not be read, or the lock
- * could not be obtained.
- * @throws CorruptObjectException
- * the index file is using a format or extension that this
- * library does not support.
- */
- public static DirCache lock(final Repository db)
- throws CorruptObjectException, IOException {
- return lock(new File(db.getDirectory(), "index"));
- }
-
/** Location of the current version of the index file. */
private final File liveFile;
@@ -768,7 +722,9 @@ public class DirCache {
* Write all index trees to the object store, returning the root tree.
*
* @param ow
- * the writer to use when serializing to the store.
+ * the writer to use when serializing to the store. The caller is
+ * responsible for flushing the inserter before trying to use the
+ * returned tree identity.
* @return identity for the root tree.
* @throws UnmergedPathException
* one or more paths contain higher-order stages (stage > 0),
@@ -779,7 +735,7 @@ public class DirCache {
* @throws IOException
* an unexpected error occurred writing to the object store.
*/
- public ObjectId writeTree(final ObjectWriter ow)
+ public ObjectId writeTree(final ObjectInserter ow)
throws UnmergedPathException, IOException {
return getCacheTree(true).writeTree(sortedEntries, 0, 0, ow);
}
diff --git a/org.eclipse.jgit/src/org/eclipse/jgit/dircache/DirCacheBuildIterator.java b/org.eclipse.jgit/src/org/eclipse/jgit/dircache/DirCacheBuildIterator.java
index 181192d141..1eb95c4be0 100644
--- a/org.eclipse.jgit/src/org/eclipse/jgit/dircache/DirCacheBuildIterator.java
+++ b/org.eclipse.jgit/src/org/eclipse/jgit/dircache/DirCacheBuildIterator.java
@@ -50,7 +50,7 @@ import java.io.IOException;
import org.eclipse.jgit.errors.CorruptObjectException;
import org.eclipse.jgit.errors.IncorrectObjectTypeException;
import org.eclipse.jgit.lib.Constants;
-import org.eclipse.jgit.lib.Repository;
+import org.eclipse.jgit.lib.ObjectReader;
import org.eclipse.jgit.treewalk.AbstractTreeIterator;
/**
@@ -106,7 +106,7 @@ public class DirCacheBuildIterator extends DirCacheIterator {
}
@Override
- public AbstractTreeIterator createSubtreeIterator(final Repository repo)
+ public AbstractTreeIterator createSubtreeIterator(final ObjectReader reader)
throws IncorrectObjectTypeException, IOException {
if (currentSubtree == null)
throw new IncorrectObjectTypeException(getEntryObjectId(),
diff --git a/org.eclipse.jgit/src/org/eclipse/jgit/dircache/DirCacheBuilder.java b/org.eclipse.jgit/src/org/eclipse/jgit/dircache/DirCacheBuilder.java
index e6b6197819..5665002dc7 100644
--- a/org.eclipse.jgit/src/org/eclipse/jgit/dircache/DirCacheBuilder.java
+++ b/org.eclipse.jgit/src/org/eclipse/jgit/dircache/DirCacheBuilder.java
@@ -50,8 +50,7 @@ import java.util.Arrays;
import org.eclipse.jgit.JGitText;
import org.eclipse.jgit.lib.AnyObjectId;
-import org.eclipse.jgit.lib.Repository;
-import org.eclipse.jgit.lib.WindowCursor;
+import org.eclipse.jgit.lib.ObjectReader;
import org.eclipse.jgit.treewalk.AbstractTreeIterator;
import org.eclipse.jgit.treewalk.CanonicalTreeParser;
import org.eclipse.jgit.treewalk.TreeWalk;
@@ -149,11 +148,12 @@ public class DirCacheBuilder extends BaseDirCacheEditor {
* as necessary.
* @param stage
* stage of the entries when adding them.
- * @param db
- * repository the tree(s) will be read from during recursive
+ * @param reader
+ * reader the tree(s) will be read from during recursive
* traversal. This must be the same repository that the resulting
* DirCache would be written out to (or used in) otherwise the
* caller is simply asking for deferred MissingObjectExceptions.
+ * Caller is responsible for releasing this reader when done.
* @param tree
* the tree to recursively add. This tree's contents will appear
* under <code>pathPrefix</code>. The ObjectId must be that of a
@@ -163,16 +163,11 @@ public class DirCacheBuilder extends BaseDirCacheEditor {
* a tree cannot be read to iterate through its entries.
*/
public void addTree(final byte[] pathPrefix, final int stage,
- final Repository db, final AnyObjectId tree) throws IOException {
- final TreeWalk tw = new TreeWalk(db);
+ final ObjectReader reader, final AnyObjectId tree) throws IOException {
+ final TreeWalk tw = new TreeWalk(reader);
tw.reset();
- final WindowCursor curs = new WindowCursor();
- try {
- tw.addTree(new CanonicalTreeParser(pathPrefix, db, tree
- .toObjectId(), curs));
- } finally {
- curs.release();
- }
+ tw.addTree(new CanonicalTreeParser(pathPrefix, reader, tree
+ .toObjectId()));
tw.setRecursive(true);
if (tw.next()) {
final DirCacheEntry newEntry = toEntry(stage, tw);
diff --git a/org.eclipse.jgit/src/org/eclipse/jgit/dircache/DirCacheIterator.java b/org.eclipse.jgit/src/org/eclipse/jgit/dircache/DirCacheIterator.java
index 9c47187821..b4e2d2c2dd 100644
--- a/org.eclipse.jgit/src/org/eclipse/jgit/dircache/DirCacheIterator.java
+++ b/org.eclipse.jgit/src/org/eclipse/jgit/dircache/DirCacheIterator.java
@@ -50,7 +50,7 @@ import java.util.Arrays;
import org.eclipse.jgit.errors.IncorrectObjectTypeException;
import org.eclipse.jgit.lib.Constants;
import org.eclipse.jgit.lib.FileMode;
-import org.eclipse.jgit.lib.Repository;
+import org.eclipse.jgit.lib.ObjectReader;
import org.eclipse.jgit.treewalk.AbstractTreeIterator;
import org.eclipse.jgit.treewalk.EmptyTreeIterator;
@@ -125,7 +125,7 @@ public class DirCacheIterator extends AbstractTreeIterator {
}
@Override
- public AbstractTreeIterator createSubtreeIterator(final Repository repo)
+ public AbstractTreeIterator createSubtreeIterator(final ObjectReader reader)
throws IncorrectObjectTypeException, IOException {
if (currentSubtree == null)
throw new IncorrectObjectTypeException(getEntryObjectId(),
diff --git a/org.eclipse.jgit/src/org/eclipse/jgit/dircache/DirCacheTree.java b/org.eclipse.jgit/src/org/eclipse/jgit/dircache/DirCacheTree.java
index 144b1a6cfc..e04b797ab3 100644
--- a/org.eclipse.jgit/src/org/eclipse/jgit/dircache/DirCacheTree.java
+++ b/org.eclipse.jgit/src/org/eclipse/jgit/dircache/DirCacheTree.java
@@ -57,7 +57,7 @@ import org.eclipse.jgit.errors.UnmergedPathException;
import org.eclipse.jgit.lib.Constants;
import org.eclipse.jgit.lib.FileMode;
import org.eclipse.jgit.lib.ObjectId;
-import org.eclipse.jgit.lib.ObjectWriter;
+import org.eclipse.jgit.lib.ObjectInserter;
import org.eclipse.jgit.util.MutableInteger;
import org.eclipse.jgit.util.RawParseUtils;
@@ -311,7 +311,7 @@ public class DirCacheTree {
* an unexpected error occurred writing to the object store.
*/
ObjectId writeTree(final DirCacheEntry[] cache, int cIdx,
- final int pathOffset, final ObjectWriter ow)
+ final int pathOffset, final ObjectInserter ow)
throws UnmergedPathException, IOException {
if (id == null) {
final int endIdx = cIdx + entrySpan;
@@ -346,13 +346,13 @@ public class DirCacheTree {
entryIdx++;
}
- id = ow.writeCanonicalTree(out.toByteArray());
+ id = ow.insert(Constants.OBJ_TREE, out.toByteArray());
}
return id;
}
private int computeSize(final DirCacheEntry[] cache, int cIdx,
- final int pathOffset, final ObjectWriter ow)
+ final int pathOffset, final ObjectInserter ow)
throws UnmergedPathException, IOException {
final int endIdx = cIdx + entrySpan;
int childIdx = 0;
diff --git a/org.eclipse.jgit/src/org/eclipse/jgit/errors/LargeObjectException.java b/org.eclipse.jgit/src/org/eclipse/jgit/errors/LargeObjectException.java
new file mode 100644
index 0000000000..d897c51de1
--- /dev/null
+++ b/org.eclipse.jgit/src/org/eclipse/jgit/errors/LargeObjectException.java
@@ -0,0 +1,67 @@
+/*
+ * Copyright (C) 2010, Google Inc.
+ * and other copyright owners as documented in the project's IP log.
+ *
+ * This program and the accompanying materials are made available
+ * under the terms of the Eclipse Distribution License v1.0 which
+ * accompanies this distribution, is reproduced below, and is
+ * available at http://www.eclipse.org/org/documents/edl-v10.php
+ *
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or
+ * without modification, are permitted provided that the following
+ * conditions are met:
+ *
+ * - Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ *
+ * - Redistributions in binary form must reproduce the above
+ * copyright notice, this list of conditions and the following
+ * disclaimer in the documentation and/or other materials provided
+ * with the distribution.
+ *
+ * - Neither the name of the Eclipse Foundation, Inc. nor the
+ * names of its contributors may be used to endorse or promote
+ * products derived from this software without specific prior
+ * written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND
+ * CONTRIBUTORS "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES,
+ * INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
+ * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR
+ * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
+ * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT
+ * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
+ * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER
+ * CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT,
+ * STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
+ * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF
+ * ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ */
+
+package org.eclipse.jgit.errors;
+
+import org.eclipse.jgit.lib.ObjectId;
+
+/** An object is too big to load into memory as a single byte array. */
+public class LargeObjectException extends RuntimeException {
+ private static final long serialVersionUID = 1L;
+
+ /** Create a large object exception, where the object isn't known. */
+ public LargeObjectException() {
+ // Do nothing.
+ }
+
+ /**
+ * Create a large object exception, naming the object that is too big.
+ *
+ * @param id
+ * identity of the object that is too big to be loaded as a byte
+ * array in this JVM.
+ */
+ public LargeObjectException(ObjectId id) {
+ super(id.name());
+ }
+}
diff --git a/org.eclipse.jgit/src/org/eclipse/jgit/errors/NoWorkTreeException.java b/org.eclipse.jgit/src/org/eclipse/jgit/errors/NoWorkTreeException.java
new file mode 100644
index 0000000000..f2980efe69
--- /dev/null
+++ b/org.eclipse.jgit/src/org/eclipse/jgit/errors/NoWorkTreeException.java
@@ -0,0 +1,59 @@
+/*
+ * Copyright (C) 2010, Google Inc.
+ * and other copyright owners as documented in the project's IP log.
+ *
+ * This program and the accompanying materials are made available
+ * under the terms of the Eclipse Distribution License v1.0 which
+ * accompanies this distribution, is reproduced below, and is
+ * available at http://www.eclipse.org/org/documents/edl-v10.php
+ *
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or
+ * without modification, are permitted provided that the following
+ * conditions are met:
+ *
+ * - Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ *
+ * - Redistributions in binary form must reproduce the above
+ * copyright notice, this list of conditions and the following
+ * disclaimer in the documentation and/or other materials provided
+ * with the distribution.
+ *
+ * - Neither the name of the Eclipse Foundation, Inc. nor the
+ * names of its contributors may be used to endorse or promote
+ * products derived from this software without specific prior
+ * written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND
+ * CONTRIBUTORS "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES,
+ * INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
+ * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR
+ * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
+ * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT
+ * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
+ * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER
+ * CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT,
+ * STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
+ * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF
+ * ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ */
+
+package org.eclipse.jgit.errors;
+
+import org.eclipse.jgit.JGitText;
+import org.eclipse.jgit.lib.Repository;
+
+/**
+ * Indicates a {@link Repository} has no working directory, and is thus bare.
+ */
+public class NoWorkTreeException extends IllegalStateException {
+ private static final long serialVersionUID = 1L;
+
+ /** Creates an exception indicating there is no work tree for a repository. */
+ public NoWorkTreeException() {
+ super(JGitText.get().bareRepositoryNoWorkdirAndIndex);
+ }
+}
diff --git a/org.eclipse.jgit/src/org/eclipse/jgit/errors/StoredObjectRepresentationNotAvailableException.java b/org.eclipse.jgit/src/org/eclipse/jgit/errors/StoredObjectRepresentationNotAvailableException.java
new file mode 100644
index 0000000000..e9e3f4d65e
--- /dev/null
+++ b/org.eclipse.jgit/src/org/eclipse/jgit/errors/StoredObjectRepresentationNotAvailableException.java
@@ -0,0 +1,61 @@
+/*
+ * Copyright (C) 2010, Google Inc.
+ * and other copyright owners as documented in the project's IP log.
+ *
+ * This program and the accompanying materials are made available
+ * under the terms of the Eclipse Distribution License v1.0 which
+ * accompanies this distribution, is reproduced below, and is
+ * available at http://www.eclipse.org/org/documents/edl-v10.php
+ *
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or
+ * without modification, are permitted provided that the following
+ * conditions are met:
+ *
+ * - Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ *
+ * - Redistributions in binary form must reproduce the above
+ * copyright notice, this list of conditions and the following
+ * disclaimer in the documentation and/or other materials provided
+ * with the distribution.
+ *
+ * - Neither the name of the Eclipse Foundation, Inc. nor the
+ * names of its contributors may be used to endorse or promote
+ * products derived from this software without specific prior
+ * written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND
+ * CONTRIBUTORS "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES,
+ * INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
+ * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR
+ * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
+ * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT
+ * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
+ * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER
+ * CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT,
+ * STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
+ * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF
+ * ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ */
+
+package org.eclipse.jgit.errors;
+
+import org.eclipse.jgit.storage.pack.ObjectToPack;
+
+/** A previously selected representation is no longer available. */
+public class StoredObjectRepresentationNotAvailableException extends Exception {
+ private static final long serialVersionUID = 1L;
+
+ /**
+ * Construct an error for an object.
+ *
+ * @param otp
+ * the object whose current representation is no longer present.
+ */
+ public StoredObjectRepresentationNotAvailableException(ObjectToPack otp) {
+ // Do nothing.
+ }
+}
diff --git a/org.eclipse.jgit/src/org/eclipse/jgit/events/ConfigChangedEvent.java b/org.eclipse.jgit/src/org/eclipse/jgit/events/ConfigChangedEvent.java
new file mode 100644
index 0000000000..79598eacb3
--- /dev/null
+++ b/org.eclipse.jgit/src/org/eclipse/jgit/events/ConfigChangedEvent.java
@@ -0,0 +1,57 @@
+/*
+ * Copyright (C) 2010, Google Inc.
+ * and other copyright owners as documented in the project's IP log.
+ *
+ * This program and the accompanying materials are made available
+ * under the terms of the Eclipse Distribution License v1.0 which
+ * accompanies this distribution, is reproduced below, and is
+ * available at http://www.eclipse.org/org/documents/edl-v10.php
+ *
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or
+ * without modification, are permitted provided that the following
+ * conditions are met:
+ *
+ * - Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ *
+ * - Redistributions in binary form must reproduce the above
+ * copyright notice, this list of conditions and the following
+ * disclaimer in the documentation and/or other materials provided
+ * with the distribution.
+ *
+ * - Neither the name of the Eclipse Foundation, Inc. nor the
+ * names of its contributors may be used to endorse or promote
+ * products derived from this software without specific prior
+ * written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND
+ * CONTRIBUTORS "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES,
+ * INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
+ * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR
+ * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
+ * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT
+ * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
+ * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER
+ * CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT,
+ * STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
+ * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF
+ * ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ */
+
+package org.eclipse.jgit.events;
+
+/** Describes a change to one or more keys in the configuration. */
+public class ConfigChangedEvent extends RepositoryEvent<ConfigChangedListener> {
+ @Override
+ public Class<ConfigChangedListener> getListenerType() {
+ return ConfigChangedListener.class;
+ }
+
+ @Override
+ public void dispatch(ConfigChangedListener listener) {
+ listener.onConfigChanged(this);
+ }
+}
diff --git a/org.eclipse.jgit/src/org/eclipse/jgit/lib/RepositoryAdapter.java b/org.eclipse.jgit/src/org/eclipse/jgit/events/ConfigChangedListener.java
index e43c33ad7d..322cf7f6d6 100644
--- a/org.eclipse.jgit/src/org/eclipse/jgit/lib/RepositoryAdapter.java
+++ b/org.eclipse.jgit/src/org/eclipse/jgit/events/ConfigChangedListener.java
@@ -41,20 +41,15 @@
* ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
*/
-package org.eclipse.jgit.lib;
-
-/**
- * A default {@link RepositoryListener} that does nothing except invoke an
- * optional general method for any repository change.
- */
-public class RepositoryAdapter implements RepositoryListener {
-
- public void indexChanged(final IndexChangedEvent e) {
- // Empty
- }
-
- public void refsChanged(final RefsChangedEvent e) {
- // Empty
- }
+package org.eclipse.jgit.events;
+/** Receives {@link ConfigChangedEvent}s. */
+public interface ConfigChangedListener extends RepositoryListener {
+ /**
+ * Invoked when any change is made to the configuration.
+ *
+ * @param event
+ * information about the changes.
+ */
+ void onConfigChanged(ConfigChangedEvent event);
}
diff --git a/org.eclipse.jgit/src/org/eclipse/jgit/events/IndexChangedEvent.java b/org.eclipse.jgit/src/org/eclipse/jgit/events/IndexChangedEvent.java
new file mode 100644
index 0000000000..a54288ee9e
--- /dev/null
+++ b/org.eclipse.jgit/src/org/eclipse/jgit/events/IndexChangedEvent.java
@@ -0,0 +1,57 @@
+/*
+ * Copyright (C) 2010, Google Inc.
+ * and other copyright owners as documented in the project's IP log.
+ *
+ * This program and the accompanying materials are made available
+ * under the terms of the Eclipse Distribution License v1.0 which
+ * accompanies this distribution, is reproduced below, and is
+ * available at http://www.eclipse.org/org/documents/edl-v10.php
+ *
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or
+ * without modification, are permitted provided that the following
+ * conditions are met:
+ *
+ * - Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ *
+ * - Redistributions in binary form must reproduce the above
+ * copyright notice, this list of conditions and the following
+ * disclaimer in the documentation and/or other materials provided
+ * with the distribution.
+ *
+ * - Neither the name of the Eclipse Foundation, Inc. nor the
+ * names of its contributors may be used to endorse or promote
+ * products derived from this software without specific prior
+ * written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND
+ * CONTRIBUTORS "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES,
+ * INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
+ * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR
+ * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
+ * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT
+ * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
+ * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER
+ * CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT,
+ * STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
+ * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF
+ * ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ */
+
+package org.eclipse.jgit.events;
+
+/** Describes a change to one or more paths in the index file. */
+public class IndexChangedEvent extends RepositoryEvent<IndexChangedListener> {
+ @Override
+ public Class<IndexChangedListener> getListenerType() {
+ return IndexChangedListener.class;
+ }
+
+ @Override
+ public void dispatch(IndexChangedListener listener) {
+ listener.onIndexChanged(this);
+ }
+}
diff --git a/org.eclipse.jgit/src/org/eclipse/jgit/lib/RepositoryChangedEvent.java b/org.eclipse.jgit/src/org/eclipse/jgit/events/IndexChangedListener.java
index 495049ce74..d41ef74ee9 100644
--- a/org.eclipse.jgit/src/org/eclipse/jgit/lib/RepositoryChangedEvent.java
+++ b/org.eclipse.jgit/src/org/eclipse/jgit/events/IndexChangedListener.java
@@ -41,30 +41,15 @@
* ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
*/
-package org.eclipse.jgit.lib;
-
-/**
- * This class passes information about changed refs to a
- * {@link RepositoryListener}
- *
- * Currently only a reference to the repository is passed.
- */
-public class RepositoryChangedEvent {
- private final Repository repository;
-
- RepositoryChangedEvent(final Repository repository) {
- this.repository = repository;
- }
+package org.eclipse.jgit.events;
+/** Receives {@link IndexChangedEvent}s. */
+public interface IndexChangedListener extends RepositoryListener {
/**
- * @return the repository that was changed
+ * Invoked when any change is made to the index.
+ *
+ * @param event
+ * information about the changes.
*/
- public Repository getRepository() {
- return repository;
- }
-
- @Override
- public String toString() {
- return "RepositoryChangedEvent[" + repository + "]";
- }
+ void onIndexChanged(IndexChangedEvent event);
}
diff --git a/org.eclipse.jgit/src/org/eclipse/jgit/lib/IndexChangedEvent.java b/org.eclipse.jgit/src/org/eclipse/jgit/events/ListenerHandle.java
index c866db531e..ef90b2205c 100644
--- a/org.eclipse.jgit/src/org/eclipse/jgit/lib/IndexChangedEvent.java
+++ b/org.eclipse.jgit/src/org/eclipse/jgit/events/ListenerHandle.java
@@ -1,5 +1,5 @@
/*
- * Copyright (C) 2008, Robin Rosenberg <robin.rosenberg@dewire.com>
+ * Copyright (C) 2010, Google Inc.
* and other copyright owners as documented in the project's IP log.
*
* This program and the accompanying materials are made available
@@ -41,27 +41,31 @@
* ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
*/
-package org.eclipse.jgit.lib;
+package org.eclipse.jgit.events;
-/**
- * This class passes information about a changed Git index to a
- * {@link RepositoryListener}
- *
- * Currently only a reference to the repository is passed.
- */
-public class IndexChangedEvent extends RepositoryChangedEvent {
- /**
- * Create an event describing index changes in a repository.
- *
- * @param repository
- * the repository whose index (DirCache) recently changed.
- */
- public IndexChangedEvent(final Repository repository) {
- super(repository);
+/** Tracks a previously registered {@link RepositoryListener}. */
+public class ListenerHandle {
+ private final ListenerList parent;
+
+ final Class<? extends RepositoryListener> type;
+
+ final RepositoryListener listener;
+
+ ListenerHandle(ListenerList parent,
+ Class<? extends RepositoryListener> type,
+ RepositoryListener listener) {
+ this.parent = parent;
+ this.type = type;
+ this.listener = listener;
+ }
+
+ /** Remove the listener and stop receiving events. */
+ public void remove() {
+ parent.remove(this);
}
@Override
public String toString() {
- return "IndexChangedEvent[" + getRepository() + "]";
+ return type.getSimpleName() + "[" + listener + "]";
}
}
diff --git a/org.eclipse.jgit/src/org/eclipse/jgit/events/ListenerList.java b/org.eclipse.jgit/src/org/eclipse/jgit/events/ListenerList.java
new file mode 100644
index 0000000000..6ac4b0f8ba
--- /dev/null
+++ b/org.eclipse.jgit/src/org/eclipse/jgit/events/ListenerList.java
@@ -0,0 +1,142 @@
+/*
+ * Copyright (C) 2010, Google Inc.
+ * and other copyright owners as documented in the project's IP log.
+ *
+ * This program and the accompanying materials are made available
+ * under the terms of the Eclipse Distribution License v1.0 which
+ * accompanies this distribution, is reproduced below, and is
+ * available at http://www.eclipse.org/org/documents/edl-v10.php
+ *
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or
+ * without modification, are permitted provided that the following
+ * conditions are met:
+ *
+ * - Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ *
+ * - Redistributions in binary form must reproduce the above
+ * copyright notice, this list of conditions and the following
+ * disclaimer in the documentation and/or other materials provided
+ * with the distribution.
+ *
+ * - Neither the name of the Eclipse Foundation, Inc. nor the
+ * names of its contributors may be used to endorse or promote
+ * products derived from this software without specific prior
+ * written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND
+ * CONTRIBUTORS "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES,
+ * INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
+ * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR
+ * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
+ * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT
+ * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
+ * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER
+ * CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT,
+ * STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
+ * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF
+ * ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ */
+
+package org.eclipse.jgit.events;
+
+import java.util.List;
+import java.util.concurrent.ConcurrentHashMap;
+import java.util.concurrent.ConcurrentMap;
+import java.util.concurrent.CopyOnWriteArrayList;
+
+/** Manages a thread-safe list of {@link RepositoryListener}s. */
+public class ListenerList {
+ private final ConcurrentMap<Class<? extends RepositoryListener>, CopyOnWriteArrayList<ListenerHandle>> lists = new ConcurrentHashMap<Class<? extends RepositoryListener>, CopyOnWriteArrayList<ListenerHandle>>();
+
+ /**
+ * Register an IndexChangedListener.
+ *
+ * @param listener
+ * the listener implementation.
+ * @return handle to later remove the listener.
+ */
+ public ListenerHandle addIndexChangedListener(IndexChangedListener listener) {
+ return addListener(IndexChangedListener.class, listener);
+ }
+
+ /**
+ * Register a RefsChangedListener.
+ *
+ * @param listener
+ * the listener implementation.
+ * @return handle to later remove the listener.
+ */
+ public ListenerHandle addRefsChangedListener(RefsChangedListener listener) {
+ return addListener(RefsChangedListener.class, listener);
+ }
+
+ /**
+ * Register a ConfigChangedListener.
+ *
+ * @param listener
+ * the listener implementation.
+ * @return handle to later remove the listener.
+ */
+ public ListenerHandle addConfigChangedListener(
+ ConfigChangedListener listener) {
+ return addListener(ConfigChangedListener.class, listener);
+ }
+
+ /**
+ * Add a listener to the list.
+ *
+ * @param <T>
+ * the type of listener being registered.
+ * @param type
+ * type of listener being registered.
+ * @param listener
+ * the listener instance.
+ * @return a handle to later remove the registration, if desired.
+ */
+ public <T extends RepositoryListener> ListenerHandle addListener(
+ Class<T> type, T listener) {
+ ListenerHandle handle = new ListenerHandle(this, type, listener);
+ add(handle);
+ return handle;
+ }
+
+ /**
+ * Dispatch an event to all interested listeners.
+ * <p>
+ * Listeners are selected by the type of listener the event delivers to.
+ *
+ * @param event
+ * the event to deliver.
+ */
+ @SuppressWarnings("unchecked")
+ public void dispatch(RepositoryEvent event) {
+ List<ListenerHandle> list = lists.get(event.getListenerType());
+ if (list != null) {
+ for (ListenerHandle handle : list)
+ event.dispatch(handle.listener);
+ }
+ }
+
+ private void add(ListenerHandle handle) {
+ List<ListenerHandle> list = lists.get(handle.type);
+ if (list == null) {
+ CopyOnWriteArrayList<ListenerHandle> newList;
+
+ newList = new CopyOnWriteArrayList<ListenerHandle>();
+ list = lists.putIfAbsent(handle.type, newList);
+ if (list == null)
+ list = newList;
+ }
+ list.add(handle);
+ }
+
+ void remove(ListenerHandle handle) {
+ List<ListenerHandle> list = lists.get(handle.type);
+ if (list != null)
+ list.remove(handle);
+ }
+}
diff --git a/org.eclipse.jgit/src/org/eclipse/jgit/events/RefsChangedEvent.java b/org.eclipse.jgit/src/org/eclipse/jgit/events/RefsChangedEvent.java
new file mode 100644
index 0000000000..36af3f8b77
--- /dev/null
+++ b/org.eclipse.jgit/src/org/eclipse/jgit/events/RefsChangedEvent.java
@@ -0,0 +1,57 @@
+/*
+ * Copyright (C) 2010, Google Inc.
+ * and other copyright owners as documented in the project's IP log.
+ *
+ * This program and the accompanying materials are made available
+ * under the terms of the Eclipse Distribution License v1.0 which
+ * accompanies this distribution, is reproduced below, and is
+ * available at http://www.eclipse.org/org/documents/edl-v10.php
+ *
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or
+ * without modification, are permitted provided that the following
+ * conditions are met:
+ *
+ * - Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ *
+ * - Redistributions in binary form must reproduce the above
+ * copyright notice, this list of conditions and the following
+ * disclaimer in the documentation and/or other materials provided
+ * with the distribution.
+ *
+ * - Neither the name of the Eclipse Foundation, Inc. nor the
+ * names of its contributors may be used to endorse or promote
+ * products derived from this software without specific prior
+ * written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND
+ * CONTRIBUTORS "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES,
+ * INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
+ * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR
+ * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
+ * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT
+ * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
+ * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER
+ * CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT,
+ * STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
+ * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF
+ * ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ */
+
+package org.eclipse.jgit.events;
+
+/** Describes a change to one or more references of a repository. */
+public class RefsChangedEvent extends RepositoryEvent<RefsChangedListener> {
+ @Override
+ public Class<RefsChangedListener> getListenerType() {
+ return RefsChangedListener.class;
+ }
+
+ @Override
+ public void dispatch(RefsChangedListener listener) {
+ listener.onRefsChanged(this);
+ }
+}
diff --git a/org.eclipse.jgit/src/org/eclipse/jgit/lib/RefsChangedEvent.java b/org.eclipse.jgit/src/org/eclipse/jgit/events/RefsChangedListener.java
index 705c6138e3..9c0f4ed588 100644
--- a/org.eclipse.jgit/src/org/eclipse/jgit/lib/RefsChangedEvent.java
+++ b/org.eclipse.jgit/src/org/eclipse/jgit/events/RefsChangedListener.java
@@ -41,27 +41,15 @@
* ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
*/
-package org.eclipse.jgit.lib;
+package org.eclipse.jgit.events;
-/**
- * This class passes information about a changed Git index to a
- * {@link RepositoryListener}
- *
- * Currently only a reference to the repository is passed.
- */
-public class RefsChangedEvent extends RepositoryChangedEvent {
+/** Receives {@link RefsChangedEvent}s. */
+public interface RefsChangedListener extends RepositoryListener {
/**
- * Create an event describing reference changes in a repository.
+ * Invoked when any reference changes.
*
- * @param repository
- * the repository whose references recently changed.
+ * @param event
+ * information about the changes.
*/
- public RefsChangedEvent(final Repository repository) {
- super(repository);
- }
-
- @Override
- public String toString() {
- return "RefsChangedEvent[" + getRepository() + "]";
- }
+ void onRefsChanged(RefsChangedEvent event);
}
diff --git a/org.eclipse.jgit/src/org/eclipse/jgit/events/RepositoryEvent.java b/org.eclipse.jgit/src/org/eclipse/jgit/events/RepositoryEvent.java
new file mode 100644
index 0000000000..ba1c81d5d8
--- /dev/null
+++ b/org.eclipse.jgit/src/org/eclipse/jgit/events/RepositoryEvent.java
@@ -0,0 +1,95 @@
+/*
+ * Copyright (C) 2010, Google Inc.
+ * Copyright (C) 2008, Robin Rosenberg <robin.rosenberg@dewire.com>
+ * and other copyright owners as documented in the project's IP log.
+ *
+ * This program and the accompanying materials are made available
+ * under the terms of the Eclipse Distribution License v1.0 which
+ * accompanies this distribution, is reproduced below, and is
+ * available at http://www.eclipse.org/org/documents/edl-v10.php
+ *
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or
+ * without modification, are permitted provided that the following
+ * conditions are met:
+ *
+ * - Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ *
+ * - Redistributions in binary form must reproduce the above
+ * copyright notice, this list of conditions and the following
+ * disclaimer in the documentation and/or other materials provided
+ * with the distribution.
+ *
+ * - Neither the name of the Eclipse Foundation, Inc. nor the
+ * names of its contributors may be used to endorse or promote
+ * products derived from this software without specific prior
+ * written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND
+ * CONTRIBUTORS "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES,
+ * INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
+ * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR
+ * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
+ * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT
+ * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
+ * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER
+ * CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT,
+ * STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
+ * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF
+ * ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ */
+
+package org.eclipse.jgit.events;
+
+import org.eclipse.jgit.lib.Repository;
+
+/**
+ * Describes a modification made to a repository.
+ *
+ * @param <T>
+ * type of listener this event dispatches to.
+ */
+public abstract class RepositoryEvent<T extends RepositoryListener> {
+ private Repository repository;
+
+ /**
+ * Set the repository this event occurred on.
+ * <p>
+ * This method should only be invoked once on each event object, and is
+ * automatically set by {@link Repository#fireEvent(RepositoryEvent)}.
+ *
+ * @param r
+ * the repository.
+ */
+ public void setRepository(Repository r) {
+ if (repository == null)
+ repository = r;
+ }
+
+ /** @return the repository that was changed. */
+ public Repository getRepository() {
+ return repository;
+ }
+
+ /** @return type of listener this event dispatches to. */
+ public abstract Class<T> getListenerType();
+
+ /**
+ * Dispatch this event to the given listener.
+ *
+ * @param listener
+ * listener that wants this event.
+ */
+ public abstract void dispatch(T listener);
+
+ @Override
+ public String toString() {
+ String type = getClass().getSimpleName();
+ if (repository == null)
+ return type;
+ return type + "[" + repository + "]";
+ }
+}
diff --git a/org.eclipse.jgit/src/org/eclipse/jgit/lib/RepositoryListener.java b/org.eclipse.jgit/src/org/eclipse/jgit/events/RepositoryListener.java
index 0473093e20..4f951e5f87 100644
--- a/org.eclipse.jgit/src/org/eclipse/jgit/lib/RepositoryListener.java
+++ b/org.eclipse.jgit/src/org/eclipse/jgit/events/RepositoryListener.java
@@ -1,5 +1,5 @@
/*
- * Copyright (C) 2008, Robin Rosenberg <robin.rosenberg@dewire.com>
+ * Copyright (C) 2010, Google Inc.
* and other copyright owners as documented in the project's IP log.
*
* This program and the accompanying materials are made available
@@ -41,29 +41,9 @@
* ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
*/
-package org.eclipse.jgit.lib;
+package org.eclipse.jgit.events;
-/**
- * A RepositoryListener gets notification about changes in refs or repository.
- * <p>
- * It currently does <em>not</em> get notification about which items are
- * changed.
- */
+/** A listener can register for event delivery. */
public interface RepositoryListener {
- /**
- * Invoked when a ref changes
- *
- * @param e
- * information about the changes.
- */
- void refsChanged(RefsChangedEvent e);
-
- /**
- * Invoked when the index changes
- *
- * @param e
- * information about the changes.
- */
- void indexChanged(IndexChangedEvent e);
-
+ // Empty marker interface; see extensions for actual methods.
}
diff --git a/org.eclipse.jgit/src/org/eclipse/jgit/lib/AlternateRepositoryDatabase.java b/org.eclipse.jgit/src/org/eclipse/jgit/lib/AlternateRepositoryDatabase.java
deleted file mode 100644
index 40f110684e..0000000000
--- a/org.eclipse.jgit/src/org/eclipse/jgit/lib/AlternateRepositoryDatabase.java
+++ /dev/null
@@ -1,142 +0,0 @@
-/*
- * Copyright (C) 2010, Constantine Plotnikov <constantine.plotnikov@gmail.com>
- * Copyright (C) 2009, Google Inc.
- * and other copyright owners as documented in the project's IP log.
- *
- * This program and the accompanying materials are made available
- * under the terms of the Eclipse Distribution License v1.0 which
- * accompanies this distribution, is reproduced below, and is
- * available at http://www.eclipse.org/org/documents/edl-v10.php
- *
- * All rights reserved.
- *
- * Redistribution and use in source and binary forms, with or
- * without modification, are permitted provided that the following
- * conditions are met:
- *
- * - Redistributions of source code must retain the above copyright
- * notice, this list of conditions and the following disclaimer.
- *
- * - Redistributions in binary form must reproduce the above
- * copyright notice, this list of conditions and the following
- * disclaimer in the documentation and/or other materials provided
- * with the distribution.
- *
- * - Neither the name of the Eclipse Foundation, Inc. nor the
- * names of its contributors may be used to endorse or promote
- * products derived from this software without specific prior
- * written permission.
- *
- * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND
- * CONTRIBUTORS "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES,
- * INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
- * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
- * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR
- * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
- * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT
- * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
- * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER
- * CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT,
- * STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
- * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF
- * ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
- */
-
-package org.eclipse.jgit.lib;
-
-import java.io.IOException;
-import java.util.Collection;
-
-/**
- * An ObjectDatabase of another {@link Repository}.
- * <p>
- * This {@code ObjectDatabase} wraps around another {@code Repository}'s object
- * database, providing its contents to the caller, and closing the Repository
- * when this database is closed. The primary user of this class is
- * {@link ObjectDirectory}, when the {@code info/alternates} file points at the
- * {@code objects/} directory of another repository.
- */
-public final class AlternateRepositoryDatabase extends ObjectDatabase {
- private final Repository repository;
-
- private final ObjectDatabase odb;
-
- /**
- * @param alt
- * the alternate repository to wrap and export.
- */
- public AlternateRepositoryDatabase(final Repository alt) {
- repository = alt;
- odb = repository.getObjectDatabase();
- }
-
- /** @return the alternate repository objects are borrowed from. */
- public Repository getRepository() {
- return repository;
- }
-
- @Override
- public void closeSelf() {
- repository.close();
- }
-
- @Override
- public void create() throws IOException {
- repository.create();
- }
-
- @Override
- public boolean exists() {
- return odb.exists();
- }
-
- @Override
- protected boolean hasObject1(final AnyObjectId objectId) {
- return odb.hasObject1(objectId);
- }
-
- @Override
- protected boolean tryAgain1() {
- return odb.tryAgain1();
- }
-
- @Override
- protected boolean hasObject2(final String objectName) {
- return odb.hasObject2(objectName);
- }
-
- @Override
- protected ObjectLoader openObject1(final WindowCursor curs,
- final AnyObjectId objectId) throws IOException {
- return odb.openObject1(curs, objectId);
- }
-
- @Override
- protected ObjectLoader openObject2(final WindowCursor curs,
- final String objectName, final AnyObjectId objectId)
- throws IOException {
- return odb.openObject2(curs, objectName, objectId);
- }
-
- @Override
- void openObjectInAllPacks1(final Collection<PackedObjectLoader> out,
- final WindowCursor curs, final AnyObjectId objectId)
- throws IOException {
- odb.openObjectInAllPacks1(out, curs, objectId);
- }
-
- @Override
- protected ObjectDatabase[] loadAlternates() throws IOException {
- return odb.getAlternates();
- }
-
- @Override
- protected void closeAlternates(final ObjectDatabase[] alt) {
- // Do nothing; these belong to odb to close, not us.
- }
-
- @Override
- public ObjectDatabase newCachedDatabase() {
- return odb.newCachedDatabase();
- }
-}
diff --git a/org.eclipse.jgit/src/org/eclipse/jgit/lib/AnyObjectId.java b/org.eclipse.jgit/src/org/eclipse/jgit/lib/AnyObjectId.java
index 7d08f3d4c9..ecaa82b75a 100644
--- a/org.eclipse.jgit/src/org/eclipse/jgit/lib/AnyObjectId.java
+++ b/org.eclipse.jgit/src/org/eclipse/jgit/lib/AnyObjectId.java
@@ -113,7 +113,7 @@ public abstract class AnyObjectId implements Comparable {
* @return < 0 if this id comes before other; 0 if this id is equal to
* other; > 0 if this id comes after other.
*/
- public int compareTo(final ObjectId other) {
+ public int compareTo(final AnyObjectId other) {
if (this == other)
return 0;
@@ -139,7 +139,7 @@ public abstract class AnyObjectId implements Comparable {
}
public int compareTo(final Object other) {
- return compareTo(((ObjectId) other));
+ return compareTo(((AnyObjectId) other));
}
/**
diff --git a/org.eclipse.jgit/src/org/eclipse/jgit/lib/BaseRepositoryBuilder.java b/org.eclipse.jgit/src/org/eclipse/jgit/lib/BaseRepositoryBuilder.java
new file mode 100644
index 0000000000..92edb0325c
--- /dev/null
+++ b/org.eclipse.jgit/src/org/eclipse/jgit/lib/BaseRepositoryBuilder.java
@@ -0,0 +1,616 @@
+package org.eclipse.jgit.lib;
+
+import static org.eclipse.jgit.lib.ConfigConstants.CONFIG_CORE_SECTION;
+import static org.eclipse.jgit.lib.ConfigConstants.CONFIG_KEY_BARE;
+import static org.eclipse.jgit.lib.ConfigConstants.CONFIG_KEY_WORKTREE;
+import static org.eclipse.jgit.lib.Constants.DOT_GIT;
+import static org.eclipse.jgit.lib.Constants.GIT_ALTERNATE_OBJECT_DIRECTORIES_KEY;
+import static org.eclipse.jgit.lib.Constants.GIT_CEILING_DIRECTORIES_KEY;
+import static org.eclipse.jgit.lib.Constants.GIT_DIR_KEY;
+import static org.eclipse.jgit.lib.Constants.GIT_INDEX_KEY;
+import static org.eclipse.jgit.lib.Constants.GIT_OBJECT_DIRECTORY_KEY;
+import static org.eclipse.jgit.lib.Constants.GIT_WORK_TREE_KEY;
+
+import java.io.File;
+import java.io.IOException;
+import java.text.MessageFormat;
+import java.util.Collection;
+import java.util.LinkedList;
+import java.util.List;
+
+import org.eclipse.jgit.JGitText;
+import org.eclipse.jgit.errors.ConfigInvalidException;
+import org.eclipse.jgit.lib.RepositoryCache.FileKey;
+import org.eclipse.jgit.storage.file.FileBasedConfig;
+import org.eclipse.jgit.storage.file.FileRepository;
+import org.eclipse.jgit.storage.file.FileRepositoryBuilder;
+import org.eclipse.jgit.util.FS;
+import org.eclipse.jgit.util.SystemReader;
+
+/**
+ * Base builder to customize repository construction.
+ * <p>
+ * Repository implementations may subclass this builder in order to add custom
+ * repository detection methods.
+ *
+ * @param <B>
+ * type of the repository builder.
+ * @param <R>
+ * type of the repository that is constructed.
+ * @see RepositoryBuilder
+ * @see FileRepositoryBuilder
+ */
+public class BaseRepositoryBuilder<B extends BaseRepositoryBuilder, R extends Repository> {
+ private FS fs;
+
+ private File gitDir;
+
+ private File objectDirectory;
+
+ private List<File> alternateObjectDirectories;
+
+ private File indexFile;
+
+ private File workTree;
+
+ /** Directories limiting the search for a Git repository. */
+ private List<File> ceilingDirectories;
+
+ /** True only if the caller wants to force bare behavior. */
+ private boolean bare;
+
+ /** Configuration file of target repository, lazily loaded if required. */
+ private Config config;
+
+ /**
+ * Set the file system abstraction needed by this repository.
+ *
+ * @param fs
+ * the abstraction.
+ * @return {@code this} (for chaining calls).
+ */
+ public B setFS(FS fs) {
+ this.fs = fs;
+ return self();
+ }
+
+ /** @return the file system abstraction, or null if not set. */
+ public FS getFS() {
+ return fs;
+ }
+
+ /**
+ * Set the Git directory storing the repository metadata.
+ * <p>
+ * The meta directory stores the objects, references, and meta files like
+ * {@code MERGE_HEAD}, or the index file. If {@code null} the path is
+ * assumed to be {@code workTree/.git}.
+ *
+ * @param gitDir
+ * {@code GIT_DIR}, the repository meta directory.
+ * @return {@code this} (for chaining calls).
+ */
+ public B setGitDir(File gitDir) {
+ this.gitDir = gitDir;
+ this.config = null;
+ return self();
+ }
+
+ /** @return the meta data directory; null if not set. */
+ public File getGitDir() {
+ return gitDir;
+ }
+
+ /**
+ * Set the directory storing the repository's objects.
+ *
+ * @param objectDirectory
+ * {@code GIT_OBJECT_DIRECTORY}, the directory where the
+ * repository's object files are stored.
+ * @return {@code this} (for chaining calls).
+ */
+ public B setObjectDirectory(File objectDirectory) {
+ this.objectDirectory = objectDirectory;
+ return self();
+ }
+
+ /** @return the object directory; null if not set. */
+ public File getObjectDirectory() {
+ return objectDirectory;
+ }
+
+ /**
+ * Add an alternate object directory to the search list.
+ * <p>
+ * This setting handles one alternate directory at a time, and is provided
+ * to support {@code GIT_ALTERNATE_OBJECT_DIRECTORIES}.
+ *
+ * @param other
+ * another objects directory to search after the standard one.
+ * @return {@code this} (for chaining calls).
+ */
+ public B addAlternateObjectDirectory(File other) {
+ if (other != null) {
+ if (alternateObjectDirectories == null)
+ alternateObjectDirectories = new LinkedList<File>();
+ alternateObjectDirectories.add(other);
+ }
+ return self();
+ }
+
+ /**
+ * Add alternate object directories to the search list.
+ * <p>
+ * This setting handles several alternate directories at once, and is
+ * provided to support {@code GIT_ALTERNATE_OBJECT_DIRECTORIES}.
+ *
+ * @param inList
+ * other object directories to search after the standard one. The
+ * collection's contents is copied to an internal list.
+ * @return {@code this} (for chaining calls).
+ */
+ public B addAlternateObjectDirectories(Collection<File> inList) {
+ if (inList != null) {
+ for (File path : inList)
+ addAlternateObjectDirectory(path);
+ }
+ return self();
+ }
+
+ /**
+ * Add alternate object directories to the search list.
+ * <p>
+ * This setting handles several alternate directories at once, and is
+ * provided to support {@code GIT_ALTERNATE_OBJECT_DIRECTORIES}.
+ *
+ * @param inList
+ * other object directories to search after the standard one. The
+ * array's contents is copied to an internal list.
+ * @return {@code this} (for chaining calls).
+ */
+ public B addAlternateObjectDirectories(File[] inList) {
+ if (inList != null) {
+ for (File path : inList)
+ addAlternateObjectDirectory(path);
+ }
+ return self();
+ }
+
+ /** @return ordered array of alternate directories; null if non were set. */
+ public File[] getAlternateObjectDirectories() {
+ final List<File> alts = alternateObjectDirectories;
+ if (alts == null)
+ return null;
+ return alts.toArray(new File[alts.size()]);
+ }
+
+ /**
+ * Force the repository to be treated as bare (have no working directory).
+ * <p>
+ * If bare the working directory aspects of the repository won't be
+ * configured, and will not be accessible.
+ *
+ * @return {@code this} (for chaining calls).
+ */
+ public B setBare() {
+ setIndexFile(null);
+ setWorkTree(null);
+ bare = true;
+ return self();
+ }
+
+ /** @return true if this repository was forced bare by {@link #setBare()}. */
+ public boolean isBare() {
+ return bare;
+ }
+
+ /**
+ * Set the top level directory of the working files.
+ *
+ * @param workTree
+ * {@code GIT_WORK_TREE}, the working directory of the checkout.
+ * @return {@code this} (for chaining calls).
+ */
+ public B setWorkTree(File workTree) {
+ this.workTree = workTree;
+ return self();
+ }
+
+ /** @return the work tree directory, or null if not set. */
+ public File getWorkTree() {
+ return workTree;
+ }
+
+ /**
+ * Set the local index file that is caching checked out file status.
+ * <p>
+ * The location of the index file tracking the status information for each
+ * checked out file in {@code workTree}. This may be null to assume the
+ * default {@code gitDiir/index}.
+ *
+ * @param indexFile
+ * {@code GIT_INDEX_FILE}, the index file location.
+ * @return {@code this} (for chaining calls).
+ */
+ public B setIndexFile(File indexFile) {
+ this.indexFile = indexFile;
+ return self();
+ }
+
+ /** @return the index file location, or null if not set. */
+ public File getIndexFile() {
+ return indexFile;
+ }
+
+ /**
+ * Read standard Git environment variables and configure from those.
+ * <p>
+ * This method tries to read the standard Git environment variables, such as
+ * {@code GIT_DIR} and {@code GIT_WORK_TREE} to configure this builder
+ * instance. If an environment variable is set, it overrides the value
+ * already set in this builder.
+ *
+ * @return {@code this} (for chaining calls).
+ */
+ public B readEnvironment() {
+ return readEnvironment(SystemReader.getInstance());
+ }
+
+ /**
+ * Read standard Git environment variables and configure from those.
+ * <p>
+ * This method tries to read the standard Git environment variables, such as
+ * {@code GIT_DIR} and {@code GIT_WORK_TREE} to configure this builder
+ * instance. If a property is already set in the builder, the environment
+ * variable is not used.
+ *
+ * @param sr
+ * the SystemReader abstraction to access the environment.
+ * @return {@code this} (for chaining calls).
+ */
+ public B readEnvironment(SystemReader sr) {
+ if (getGitDir() == null) {
+ String val = sr.getenv(GIT_DIR_KEY);
+ if (val != null)
+ setGitDir(new File(val));
+ }
+
+ if (getObjectDirectory() == null) {
+ String val = sr.getenv(GIT_OBJECT_DIRECTORY_KEY);
+ if (val != null)
+ setObjectDirectory(new File(val));
+ }
+
+ if (getAlternateObjectDirectories() == null) {
+ String val = sr.getenv(GIT_ALTERNATE_OBJECT_DIRECTORIES_KEY);
+ if (val != null) {
+ for (String path : val.split(File.pathSeparator))
+ addAlternateObjectDirectory(new File(path));
+ }
+ }
+
+ if (getWorkTree() == null) {
+ String val = sr.getenv(GIT_WORK_TREE_KEY);
+ if (val != null)
+ setWorkTree(new File(val));
+ }
+
+ if (getIndexFile() == null) {
+ String val = sr.getenv(GIT_INDEX_KEY);
+ if (val != null)
+ setIndexFile(new File(val));
+ }
+
+ if (ceilingDirectories == null) {
+ String val = sr.getenv(GIT_CEILING_DIRECTORIES_KEY);
+ if (val != null) {
+ for (String path : val.split(File.pathSeparator))
+ addCeilingDirectory(new File(path));
+ }
+ }
+
+ return self();
+ }
+
+ /**
+ * Add a ceiling directory to the search limit list.
+ * <p>
+ * This setting handles one ceiling directory at a time, and is provided to
+ * support {@code GIT_CEILING_DIRECTORIES}.
+ *
+ * @param root
+ * a path to stop searching at; its parent will not be searched.
+ * @return {@code this} (for chaining calls).
+ */
+ public B addCeilingDirectory(File root) {
+ if (root != null) {
+ if (ceilingDirectories == null)
+ ceilingDirectories = new LinkedList<File>();
+ ceilingDirectories.add(root);
+ }
+ return self();
+ }
+
+ /**
+ * Add ceiling directories to the search list.
+ * <p>
+ * This setting handles several ceiling directories at once, and is provided
+ * to support {@code GIT_CEILING_DIRECTORIES}.
+ *
+ * @param inList
+ * directory paths to stop searching at. The collection's
+ * contents is copied to an internal list.
+ * @return {@code this} (for chaining calls).
+ */
+ public B addCeilingDirectories(Collection<File> inList) {
+ if (inList != null) {
+ for (File path : inList)
+ addCeilingDirectory(path);
+ }
+ return self();
+ }
+
+ /**
+ * Add ceiling directories to the search list.
+ * <p>
+ * This setting handles several ceiling directories at once, and is provided
+ * to support {@code GIT_CEILING_DIRECTORIES}.
+ *
+ * @param inList
+ * directory paths to stop searching at. The array's contents is
+ * copied to an internal list.
+ * @return {@code this} (for chaining calls).
+ */
+ public B addCeilingDirectories(File[] inList) {
+ if (inList != null) {
+ for (File path : inList)
+ addCeilingDirectory(path);
+ }
+ return self();
+ }
+
+ /**
+ * Configure {@code GIT_DIR} by searching up the file system.
+ * <p>
+ * Starts from the current working directory of the JVM and scans up through
+ * the directory tree until a Git repository is found. Success can be
+ * determined by checking for {@code getGitDir() != null}.
+ * <p>
+ * The search can be limited to specific spaces of the local filesystem by
+ * {@link #addCeilingDirectory(File)}, or inheriting the list through a
+ * prior call to {@link #readEnvironment()}.
+ *
+ * @return {@code this} (for chaining calls).
+ */
+ public B findGitDir() {
+ if (getGitDir() == null)
+ findGitDir(new File("").getAbsoluteFile());
+ return self();
+ }
+
+ /**
+ * Configure {@code GIT_DIR} by searching up the file system.
+ * <p>
+ * Starts from the supplied directory path and scans up through the parent
+ * directory tree until a Git repository is found. Success can be determined
+ * by checking for {@code getGitDir() != null}.
+ * <p>
+ * The search can be limited to specific spaces of the local filesystem by
+ * {@link #addCeilingDirectory(File)}, or inheriting the list through a
+ * prior call to {@link #readEnvironment()}.
+ *
+ * @param current
+ * directory to begin searching in.
+ * @return {@code this} (for chaining calls).
+ */
+ public B findGitDir(File current) {
+ if (getGitDir() == null) {
+ FS tryFS = safeFS();
+ while (current != null) {
+ File dir = new File(current, DOT_GIT);
+ if (FileKey.isGitRepository(dir, tryFS)) {
+ setGitDir(dir);
+ break;
+ }
+
+ current = current.getParentFile();
+ if (current != null && ceilingDirectories.contains(current))
+ break;
+ }
+ }
+ return self();
+ }
+
+ /**
+ * Guess and populate all parameters not already defined.
+ * <p>
+ * If an option was not set, the setup method will try to default the option
+ * based on other options. If insufficient information is available, an
+ * exception is thrown to the caller.
+ *
+ * @return {@code this}
+ * @throws IllegalArgumentException
+ * insufficient parameters were set, or some parameters are
+ * incompatible with one another.
+ * @throws IOException
+ * the repository could not be accessed to configure the rest of
+ * the builder's parameters.
+ */
+ public B setup() throws IllegalArgumentException, IOException {
+ requireGitDirOrWorkTree();
+ setupGitDir();
+ setupWorkTree();
+ setupInternals();
+ return self();
+ }
+
+ /**
+ * Create a repository matching the configuration in this builder.
+ * <p>
+ * If an option was not set, the build method will try to default the option
+ * based on other options. If insufficient information is available, an
+ * exception is thrown to the caller.
+ *
+ * @return a repository matching this configuration.
+ * @throws IllegalArgumentException
+ * insufficient parameters were set.
+ * @throws IOException
+ * the repository could not be accessed to configure the rest of
+ * the builder's parameters.
+ */
+ @SuppressWarnings("unchecked")
+ public R build() throws IOException {
+ return (R) new FileRepository(setup());
+ }
+
+ /** Require either {@code gitDir} or {@code workTree} to be set. */
+ protected void requireGitDirOrWorkTree() {
+ if (getGitDir() == null && getWorkTree() == null)
+ throw new IllegalArgumentException(
+ JGitText.get().eitherGitDirOrWorkTreeRequired);
+ }
+
+ /**
+ * Perform standard gitDir initialization.
+ *
+ * @throws IOException
+ * the repository could not be accessed
+ */
+ protected void setupGitDir() throws IOException {
+ // No gitDir? Try to assume its under the workTree.
+ //
+ if (getGitDir() == null && getWorkTree() != null)
+ setGitDir(new File(getWorkTree(), DOT_GIT));
+ }
+
+ /**
+ * Perform standard work-tree initialization.
+ * <p>
+ * This is a method typically invoked inside of {@link #setup()}, near the
+ * end after the repository has been identified and its configuration is
+ * available for inspection.
+ *
+ * @throws IOException
+ * the repository configuration could not be read.
+ */
+ protected void setupWorkTree() throws IOException {
+ if (getFS() == null)
+ setFS(FS.DETECTED);
+
+ // If we aren't bare, we should have a work tree.
+ //
+ if (!isBare() && getWorkTree() == null)
+ setWorkTree(guessWorkTreeOrFail());
+
+ if (!isBare()) {
+ // If after guessing we're still not bare, we must have
+ // a metadata directory to hold the repository. Assume
+ // its at the work tree.
+ //
+ if (getGitDir() == null)
+ setGitDir(getWorkTree().getParentFile());
+ if (getIndexFile() == null)
+ setIndexFile(new File(getGitDir(), "index"));
+ }
+ }
+
+ /**
+ * Configure the internal implementation details of the repository.
+ *
+ * @throws IOException
+ * the repository could not be accessed
+ */
+ protected void setupInternals() throws IOException {
+ if (getObjectDirectory() == null && getGitDir() != null)
+ setObjectDirectory(safeFS().resolve(getGitDir(), "objects"));
+ }
+
+ /**
+ * Get the cached repository configuration, loading if not yet available.
+ *
+ * @return the configuration of the repository.
+ * @throws IOException
+ * the configuration is not available, or is badly formed.
+ */
+ protected Config getConfig() throws IOException {
+ if (config == null)
+ config = loadConfig();
+ return config;
+ }
+
+ /**
+ * Parse and load the repository specific configuration.
+ * <p>
+ * The default implementation reads {@code gitDir/config}, or returns an
+ * empty configuration if gitDir was not set.
+ *
+ * @return the repository's configuration.
+ * @throws IOException
+ * the configuration is not available.
+ */
+ protected Config loadConfig() throws IOException {
+ if (getGitDir() != null) {
+ // We only want the repository's configuration file, and not
+ // the user file, as these parameters must be unique to this
+ // repository and not inherited from other files.
+ //
+ File path = safeFS().resolve(getGitDir(), "config");
+ FileBasedConfig cfg = new FileBasedConfig(path);
+ try {
+ cfg.load();
+ } catch (ConfigInvalidException err) {
+ throw new IllegalArgumentException(MessageFormat.format(
+ JGitText.get().repositoryConfigFileInvalid, path
+ .getAbsolutePath(), err.getMessage()));
+ }
+ return cfg;
+ } else {
+ return new Config();
+ }
+ }
+
+ private File guessWorkTreeOrFail() throws IOException {
+ final Config cfg = getConfig();
+
+ // If set, core.worktree wins.
+ //
+ String path = cfg.getString(CONFIG_CORE_SECTION, null,
+ CONFIG_KEY_WORKTREE);
+ if (path != null)
+ return safeFS().resolve(getGitDir(), path);
+
+ // If core.bare is set, honor its value. Assume workTree is
+ // the parent directory of the repository.
+ //
+ if (cfg.getString(CONFIG_CORE_SECTION, null, CONFIG_KEY_BARE) != null) {
+ if (cfg.getBoolean(CONFIG_CORE_SECTION, CONFIG_KEY_BARE, true)) {
+ setBare();
+ return null;
+ }
+ return getGitDir().getParentFile();
+ }
+
+ if (getGitDir().getName().equals(DOT_GIT)) {
+ // No value for the "bare" flag, but gitDir is named ".git",
+ // use the parent of the directory
+ //
+ return getGitDir().getParentFile();
+ }
+
+ // We have to assume we are bare.
+ //
+ setBare();
+ return null;
+ }
+
+ /** @return the configured FS, or {@link FS#DETECTED}. */
+ protected FS safeFS() {
+ return getFS() != null ? getFS() : FS.DETECTED;
+ }
+
+ /** @return {@code this} */
+ @SuppressWarnings("unchecked")
+ protected final B self() {
+ return (B) this;
+ }
+} \ No newline at end of file
diff --git a/org.eclipse.jgit/src/org/eclipse/jgit/lib/BlobBasedConfig.java b/org.eclipse.jgit/src/org/eclipse/jgit/lib/BlobBasedConfig.java
index b05942b02c..b56966ff42 100644
--- a/org.eclipse.jgit/src/org/eclipse/jgit/lib/BlobBasedConfig.java
+++ b/org.eclipse.jgit/src/org/eclipse/jgit/lib/BlobBasedConfig.java
@@ -91,10 +91,8 @@ public class BlobBasedConfig extends Config {
public BlobBasedConfig(Config base, final Repository r,
final ObjectId objectId) throws IOException, ConfigInvalidException {
super(base);
- final ObjectLoader loader = r.openBlob(objectId);
- if (loader == null)
- throw new IOException(MessageFormat.format(JGitText.get().blobNotFound, objectId));
- fromText(RawParseUtils.decode(loader.getBytes()));
+ ObjectLoader loader = r.open(objectId, Constants.OBJ_BLOB);
+ fromText(RawParseUtils.decode(loader.getCachedBytes()));
}
/**
@@ -122,10 +120,7 @@ public class BlobBasedConfig extends Config {
if (tree == null)
throw new FileNotFoundException(MessageFormat.format(JGitText.get().entryNotFoundByPath, path));
final ObjectId blobId = tree.getObjectId(0);
- final ObjectLoader loader = tree.getRepository().openBlob(blobId);
- if (loader == null)
- throw new IOException(MessageFormat.format(JGitText.get().blobNotFoundForPath
- , blobId, path));
- fromText(RawParseUtils.decode(loader.getBytes()));
+ ObjectLoader loader = r.open(blobId,Constants.OBJ_BLOB);
+ fromText(RawParseUtils.decode(loader.getCachedBytes()));
}
}
diff --git a/org.eclipse.jgit/src/org/eclipse/jgit/lib/CachedObjectDatabase.java b/org.eclipse.jgit/src/org/eclipse/jgit/lib/CachedObjectDatabase.java
deleted file mode 100644
index 3dcea1636f..0000000000
--- a/org.eclipse.jgit/src/org/eclipse/jgit/lib/CachedObjectDatabase.java
+++ /dev/null
@@ -1,132 +0,0 @@
-/*
- * Copyright (C) 2010, Constantine Plotnikov <constantine.plotnikov@gmail.com>
- * Copyright (C) 2010, JetBrains s.r.o.
- * and other copyright owners as documented in the project's IP log.
- *
- * This program and the accompanying materials are made available
- * under the terms of the Eclipse Distribution License v1.0 which
- * accompanies this distribution, is reproduced below, and is
- * available at http://www.eclipse.org/org/documents/edl-v10.php
- *
- * All rights reserved.
- *
- * Redistribution and use in source and binary forms, with or
- * without modification, are permitted provided that the following
- * conditions are met:
- *
- * - Redistributions of source code must retain the above copyright
- * notice, this list of conditions and the following disclaimer.
- *
- * - Redistributions in binary form must reproduce the above
- * copyright notice, this list of conditions and the following
- * disclaimer in the documentation and/or other materials provided
- * with the distribution.
- *
- * - Neither the name of the Eclipse Foundation, Inc. nor the
- * names of its contributors may be used to endorse or promote
- * products derived from this software without specific prior
- * written permission.
- *
- * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND
- * CONTRIBUTORS "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES,
- * INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
- * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
- * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR
- * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
- * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT
- * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
- * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER
- * CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT,
- * STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
- * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF
- * ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
- */
-
-package org.eclipse.jgit.lib;
-
-import java.io.IOException;
-import java.util.Collection;
-
-/**
- * {@link ObjectDatabase} wrapper providing temporary lookup caching.
- * <p>
- * The base class for {@code ObjectDatabase}s that wrap other database instances
- * and optimize querying for objects by caching some database dependent
- * information. Instances of this class (or any of its subclasses) can be
- * returned from the method {@link ObjectDatabase#newCachedDatabase()}. This
- * class can be used in scenarios where the database does not change, or when
- * changes in the database while some operation is in progress is an acceptable
- * risk.
- * <p>
- * The default implementation delegates all requests to the wrapped database.
- * The instance might be indirectly invalidated if the wrapped instance is
- * closed. Closing the delegating instance does not implies closing the wrapped
- * instance. For alternative databases, cached instances are used as well.
- */
-public class CachedObjectDatabase extends ObjectDatabase {
- /**
- * The wrapped database instance
- */
- protected final ObjectDatabase wrapped;
-
- /**
- * Create the delegating database instance
- *
- * @param wrapped
- * the wrapped object database
- */
- public CachedObjectDatabase(ObjectDatabase wrapped) {
- this.wrapped = wrapped;
- }
-
- @Override
- protected boolean hasObject1(AnyObjectId objectId) {
- return wrapped.hasObject1(objectId);
- }
-
- @Override
- protected ObjectLoader openObject1(WindowCursor curs, AnyObjectId objectId)
- throws IOException {
- return wrapped.openObject1(curs, objectId);
- }
-
- @Override
- protected boolean hasObject2(String objectName) {
- return wrapped.hasObject2(objectName);
- }
-
- @Override
- protected ObjectDatabase[] loadAlternates() throws IOException {
- ObjectDatabase[] loaded = wrapped.getAlternates();
- ObjectDatabase[] result = new ObjectDatabase[loaded.length];
- for (int i = 0; i < loaded.length; i++) {
- result[i] = loaded[i].newCachedDatabase();
- }
- return result;
- }
-
- @Override
- protected ObjectLoader openObject2(WindowCursor curs, String objectName,
- AnyObjectId objectId) throws IOException {
- return wrapped.openObject2(curs, objectName, objectId);
- }
-
- @Override
- void openObjectInAllPacks1(Collection<PackedObjectLoader> out,
- WindowCursor curs, AnyObjectId objectId) throws IOException {
- wrapped.openObjectInAllPacks1(out, curs, objectId);
- }
-
- @Override
- protected boolean tryAgain1() {
- return wrapped.tryAgain1();
- }
-
- @Override
- public ObjectDatabase newCachedDatabase() {
- // Note that "this" is not returned since subclasses might actually do something,
- // on closeSelf() (for example closing database connections or open repositories).
- // The situation might become even more tricky if we will consider alternates.
- return wrapped.newCachedDatabase();
- }
-}
diff --git a/org.eclipse.jgit/src/org/eclipse/jgit/lib/Commit.java b/org.eclipse.jgit/src/org/eclipse/jgit/lib/Commit.java
index 66dd89120c..eeffb08e95 100644
--- a/org.eclipse.jgit/src/org/eclipse/jgit/lib/Commit.java
+++ b/org.eclipse.jgit/src/org/eclipse/jgit/lib/Commit.java
@@ -339,7 +339,14 @@ public class Commit implements Treeish {
public void commit() throws IOException {
if (getCommitId() != null)
throw new IllegalStateException(MessageFormat.format(JGitText.get().commitAlreadyExists, getCommitId()));
- setCommitId(new ObjectWriter(objdb).writeCommit(this));
+ ObjectInserter odi = objdb.newObjectInserter();
+ try {
+ ObjectId id = odi.insert(Constants.OBJ_COMMIT, odi.format(this));
+ odi.flush();
+ setCommitId(id);
+ } finally {
+ odi.release();
+ }
}
public String toString() {
diff --git a/org.eclipse.jgit/src/org/eclipse/jgit/lib/CoreConfig.java b/org.eclipse.jgit/src/org/eclipse/jgit/lib/CoreConfig.java
index 9080ec1398..5ad7910be8 100644
--- a/org.eclipse.jgit/src/org/eclipse/jgit/lib/CoreConfig.java
+++ b/org.eclipse.jgit/src/org/eclipse/jgit/lib/CoreConfig.java
@@ -47,6 +47,7 @@
package org.eclipse.jgit.lib;
import static java.util.zip.Deflater.DEFAULT_COMPRESSION;
+import static org.eclipse.jgit.lib.ObjectLoader.STREAM_THRESHOLD;
import org.eclipse.jgit.lib.Config.SectionParser;
@@ -67,14 +68,21 @@ public class CoreConfig {
private final boolean logAllRefUpdates;
+ private final int streamFileThreshold;
+
private CoreConfig(final Config rc) {
compression = rc.getInt("core", "compression", DEFAULT_COMPRESSION);
packIndexVersion = rc.getInt("pack", "indexversion", 2);
logAllRefUpdates = rc.getBoolean("core", "logallrefupdates", true);
+
+ long maxMem = Runtime.getRuntime().maxMemory();
+ long sft = rc.getLong("core", null, "streamfilethreshold", STREAM_THRESHOLD);
+ sft = Math.min(sft, maxMem / 4); // don't use more than 1/4 of the heap
+ sft = Math.min(sft, Integer.MAX_VALUE); // cannot exceed array length
+ streamFileThreshold = (int) sft;
}
/**
- * @see ObjectWriter
* @return The compression level to use when storing loose objects
*/
public int getCompression() {
@@ -95,4 +103,9 @@ public class CoreConfig {
public boolean isLogAllRefUpdates() {
return logAllRefUpdates;
}
+
+ /** @return the size threshold beyond which objects must be streamed. */
+ public int getStreamFileThreshold() {
+ return streamFileThreshold;
+ }
}
diff --git a/org.eclipse.jgit/src/org/eclipse/jgit/lib/DeltaPackedObjectLoader.java b/org.eclipse.jgit/src/org/eclipse/jgit/lib/DeltaPackedObjectLoader.java
deleted file mode 100644
index bbc1c62a8a..0000000000
--- a/org.eclipse.jgit/src/org/eclipse/jgit/lib/DeltaPackedObjectLoader.java
+++ /dev/null
@@ -1,118 +0,0 @@
-/*
- * Copyright (C) 2008-2009, Google Inc.
- * Copyright (C) 2008, Marek Zawirski <marek.zawirski@gmail.com>
- * Copyright (C) 2007, Robin Rosenberg <robin.rosenberg@dewire.com>
- * Copyright (C) 2006-2008, Shawn O. Pearce <spearce@spearce.org>
- * and other copyright owners as documented in the project's IP log.
- *
- * This program and the accompanying materials are made available
- * under the terms of the Eclipse Distribution License v1.0 which
- * accompanies this distribution, is reproduced below, and is
- * available at http://www.eclipse.org/org/documents/edl-v10.php
- *
- * All rights reserved.
- *
- * Redistribution and use in source and binary forms, with or
- * without modification, are permitted provided that the following
- * conditions are met:
- *
- * - Redistributions of source code must retain the above copyright
- * notice, this list of conditions and the following disclaimer.
- *
- * - Redistributions in binary form must reproduce the above
- * copyright notice, this list of conditions and the following
- * disclaimer in the documentation and/or other materials provided
- * with the distribution.
- *
- * - Neither the name of the Eclipse Foundation, Inc. nor the
- * names of its contributors may be used to endorse or promote
- * products derived from this software without specific prior
- * written permission.
- *
- * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND
- * CONTRIBUTORS "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES,
- * INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
- * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
- * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR
- * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
- * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT
- * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
- * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER
- * CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT,
- * STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
- * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF
- * ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
- */
-
-package org.eclipse.jgit.lib;
-
-import java.io.IOException;
-import java.text.MessageFormat;
-import java.util.zip.DataFormatException;
-
-import org.eclipse.jgit.JGitText;
-import org.eclipse.jgit.errors.CorruptObjectException;
-
-/** Reader for a deltified object stored in a pack file. */
-abstract class DeltaPackedObjectLoader extends PackedObjectLoader {
- private static final int OBJ_COMMIT = Constants.OBJ_COMMIT;
-
- private final int deltaSize;
-
- DeltaPackedObjectLoader(final PackFile pr, final long objectOffset,
- final int headerSize, final int deltaSz) {
- super(pr, objectOffset, headerSize);
- objectType = -1;
- deltaSize = deltaSz;
- }
-
- @Override
- public void materialize(final WindowCursor curs) throws IOException {
- if (cachedBytes != null) {
- return;
- }
-
- if (objectType != OBJ_COMMIT) {
- UnpackedObjectCache.Entry cache = pack.readCache(objectOffset);
- if (cache != null) {
- curs.release();
- objectType = cache.type;
- objectSize = cache.data.length;
- cachedBytes = cache.data;
- return;
- }
- }
-
- try {
- final PackedObjectLoader baseLoader = getBaseLoader(curs);
- baseLoader.materialize(curs);
- cachedBytes = BinaryDelta.apply(baseLoader.getCachedBytes(), pack
- .decompress(objectOffset + headerSize, deltaSize, curs));
- curs.release();
- objectType = baseLoader.getType();
- objectSize = cachedBytes.length;
- if (objectType != OBJ_COMMIT)
- pack.saveCache(objectOffset, cachedBytes, objectType);
- } catch (DataFormatException dfe) {
- final CorruptObjectException coe;
- coe = new CorruptObjectException(MessageFormat.format(JGitText.get().objectAtHasBadZlibStream,
- objectOffset, pack.getPackFile()));
- coe.initCause(dfe);
- throw coe;
- }
- }
-
- @Override
- public long getRawSize() {
- return deltaSize;
- }
-
- /**
- * @param curs
- * temporary thread storage during data access.
- * @return the object loader for the base object
- * @throws IOException
- */
- protected abstract PackedObjectLoader getBaseLoader(WindowCursor curs)
- throws IOException;
-}
diff --git a/org.eclipse.jgit/src/org/eclipse/jgit/lib/FileTreeEntry.java b/org.eclipse.jgit/src/org/eclipse/jgit/lib/FileTreeEntry.java
index ed1b51d8d5..7990956f6b 100644
--- a/org.eclipse.jgit/src/org/eclipse/jgit/lib/FileTreeEntry.java
+++ b/org.eclipse.jgit/src/org/eclipse/jgit/lib/FileTreeEntry.java
@@ -100,7 +100,7 @@ public class FileTreeEntry extends TreeEntry {
* @throws IOException
*/
public ObjectLoader openReader() throws IOException {
- return getRepository().openBlob(getId());
+ return getRepository().open(getId(), Constants.OBJ_BLOB);
}
public void accept(final TreeVisitor tv, final int flags)
diff --git a/org.eclipse.jgit/src/org/eclipse/jgit/lib/GitIndex.java b/org.eclipse.jgit/src/org/eclipse/jgit/lib/GitIndex.java
index df4906374f..bf293d190b 100644
--- a/org.eclipse.jgit/src/org/eclipse/jgit/lib/GitIndex.java
+++ b/org.eclipse.jgit/src/org/eclipse/jgit/lib/GitIndex.java
@@ -71,6 +71,7 @@ import org.eclipse.jgit.JGitText;
import org.eclipse.jgit.dircache.DirCache;
import org.eclipse.jgit.errors.CorruptObjectException;
import org.eclipse.jgit.errors.NotSupportedException;
+import org.eclipse.jgit.events.IndexChangedEvent;
import org.eclipse.jgit.util.RawParseUtils;
/**
@@ -155,7 +156,7 @@ public class GitIndex {
public void rereadIfNecessary() throws IOException {
if (cacheFile.exists() && cacheFile.lastModified() != lastCacheTime) {
read();
- db.fireIndexChanged();
+ db.fireEvent(new IndexChangedEvent());
}
}
@@ -329,7 +330,7 @@ public class GitIndex {
changed = false;
statDirty = false;
lastCacheTime = cacheFile.lastModified();
- db.fireIndexChanged();
+ db.fireEvent(new IndexChangedEvent());
} finally {
if (!lock.delete())
throw new IOException(
@@ -374,7 +375,7 @@ public class GitIndex {
// to change this for testing.
if (filemode != null)
return filemode.booleanValue();
- RepositoryConfig config = db.getConfig();
+ Config config = db.getConfig();
filemode = Boolean.valueOf(config.getBoolean("core", null, "filemode", true));
return filemode.booleanValue();
}
@@ -459,7 +460,7 @@ public class GitIndex {
uid = -1;
gid = -1;
try {
- size = (int) db.openBlob(f.getId()).getSize();
+ size = (int) db.open(f.getId(), Constants.OBJ_BLOB).getSize();
} catch (IOException e) {
e.printStackTrace();
size = -1;
@@ -899,17 +900,16 @@ public class GitIndex {
* @throws IOException
*/
public void checkoutEntry(File wd, Entry e) throws IOException {
- ObjectLoader ol = db.openBlob(e.sha1);
- byte[] bytes = ol.getBytes();
+ ObjectLoader ol = db.open(e.sha1, Constants.OBJ_BLOB);
File file = new File(wd, e.getName());
file.delete();
file.getParentFile().mkdirs();
- FileChannel channel = new FileOutputStream(file).getChannel();
- ByteBuffer buffer = ByteBuffer.wrap(bytes);
- int j = channel.write(buffer);
- if (j != bytes.length)
- throw new IOException(MessageFormat.format(JGitText.get().couldNotWriteFile, file));
- channel.close();
+ FileOutputStream dst = new FileOutputStream(file);
+ try {
+ ol.copyTo(dst);
+ } finally {
+ dst.close();
+ }
if (config_filemode() && File_hasExecute()) {
if (FileMode.EXECUTABLE_FILE.equals(e.mode)) {
if (!File_canExecute(file))
diff --git a/org.eclipse.jgit/src/org/eclipse/jgit/lib/IndexDiff.java b/org.eclipse.jgit/src/org/eclipse/jgit/lib/IndexDiff.java
index 030b94214d..db0f942b07 100644
--- a/org.eclipse.jgit/src/org/eclipse/jgit/lib/IndexDiff.java
+++ b/org.eclipse.jgit/src/org/eclipse/jgit/lib/IndexDiff.java
@@ -95,7 +95,7 @@ public class IndexDiff {
* @throws IOException
*/
public boolean diff() throws IOException {
- final File root = index.getRepository().getWorkDir();
+ final File root = index.getRepository().getWorkTree();
new IndexTreeWalker(index, tree, root, new AbstractIndexTreeVisitor() {
public void visitEntry(TreeEntry treeEntry, Entry indexEntry, File file) {
if (treeEntry == null) {
diff --git a/org.eclipse.jgit/src/org/eclipse/jgit/lib/ObjectDatabase.java b/org.eclipse.jgit/src/org/eclipse/jgit/lib/ObjectDatabase.java
index 7eac79fb78..15d118c0e9 100644
--- a/org.eclipse.jgit/src/org/eclipse/jgit/lib/ObjectDatabase.java
+++ b/org.eclipse.jgit/src/org/eclipse/jgit/lib/ObjectDatabase.java
@@ -44,31 +44,20 @@
package org.eclipse.jgit.lib;
import java.io.IOException;
-import java.util.Collection;
-import java.util.concurrent.atomic.AtomicReference;
+
+import org.eclipse.jgit.errors.IncorrectObjectTypeException;
+import org.eclipse.jgit.errors.MissingObjectException;
/**
* Abstraction of arbitrary object storage.
* <p>
* An object database stores one or more Git objects, indexed by their unique
- * {@link ObjectId}. Optionally an object database can reference one or more
- * alternates; other ObjectDatabase instances that are searched in addition to
- * the current database.
- * <p>
- * Databases are usually divided into two halves: a half that is considered to
- * be fast to search, and a half that is considered to be slow to search. When
- * alternates are present the fast half is fully searched (recursively through
- * all alternates) before the slow half is considered.
+ * {@link ObjectId}.
*/
public abstract class ObjectDatabase {
- /** Constant indicating no alternate databases exist. */
- protected static final ObjectDatabase[] NO_ALTERNATES = {};
-
- private final AtomicReference<ObjectDatabase[]> alternates;
-
/** Initialize a new database instance for access. */
protected ObjectDatabase() {
- alternates = new AtomicReference<ObjectDatabase[]>();
+ // Protected to force extension.
}
/**
@@ -92,292 +81,101 @@ public abstract class ObjectDatabase {
}
/**
- * Close any resources held by this database and its active alternates.
- */
- public final void close() {
- closeSelf();
- closeAlternates();
- }
-
- /**
- * Close any resources held by this database only; ignoring alternates.
+ * Create a new {@code ObjectInserter} to insert new objects.
* <p>
- * To fully close this database and its referenced alternates, the caller
- * should instead invoke {@link #close()}.
+ * The returned inserter is not itself thread-safe, but multiple concurrent
+ * inserter instances created from the same {@code ObjectDatabase} must be
+ * thread-safe.
+ *
+ * @return writer the caller can use to create objects in this database.
*/
- public void closeSelf() {
- // Assume no action is required.
- }
-
- /** Fully close all loaded alternates and clear the alternate list. */
- public final void closeAlternates() {
- ObjectDatabase[] alt = alternates.get();
- if (alt != null) {
- alternates.set(null);
- closeAlternates(alt);
- }
- }
+ public abstract ObjectInserter newInserter();
/**
- * Does the requested object exist in this database?
+ * Create a new {@code ObjectReader} to read existing objects.
* <p>
- * Alternates (if present) are searched automatically.
+ * The returned reader is not itself thread-safe, but multiple concurrent
+ * reader instances created from the same {@code ObjectDatabase} must be
+ * thread-safe.
*
- * @param objectId
- * identity of the object to test for existence of.
- * @return true if the specified object is stored in this database, or any
- * of the alternate databases.
+ * @return reader the caller can use to load objects from this database.
*/
- public final boolean hasObject(final AnyObjectId objectId) {
- return hasObjectImpl1(objectId) || hasObjectImpl2(objectId.name());
- }
-
- private final boolean hasObjectImpl1(final AnyObjectId objectId) {
- if (hasObject1(objectId)) {
- return true;
- }
- for (final ObjectDatabase alt : getAlternates()) {
- if (alt.hasObjectImpl1(objectId)) {
- return true;
- }
- }
- return tryAgain1() && hasObject1(objectId);
- }
-
- private final boolean hasObjectImpl2(final String objectId) {
- if (hasObject2(objectId)) {
- return true;
- }
- for (final ObjectDatabase alt : getAlternates()) {
- if (alt.hasObjectImpl2(objectId)) {
- return true;
- }
- }
- return false;
- }
+ public abstract ObjectReader newReader();
/**
- * Fast half of {@link #hasObject(AnyObjectId)}.
- *
- * @param objectId
- * identity of the object to test for existence of.
- * @return true if the specified object is stored in this database.
+ * Close any resources held by this database.
*/
- protected abstract boolean hasObject1(AnyObjectId objectId);
+ public abstract void close();
/**
- * Slow half of {@link #hasObject(AnyObjectId)}.
+ * Does the requested object exist in this database?
+ * <p>
+ * This is a one-shot call interface which may be faster than allocating a
+ * {@link #newReader()} to perform the lookup.
*
- * @param objectName
+ * @param objectId
* identity of the object to test for existence of.
* @return true if the specified object is stored in this database.
- */
- protected boolean hasObject2(String objectName) {
- // Assume the search took place during hasObject1.
- return false;
+ * @throws IOException
+ * the object store cannot be accessed.
+ */
+ public boolean has(final AnyObjectId objectId) throws IOException {
+ final ObjectReader or = newReader();
+ try {
+ return or.has(objectId);
+ } finally {
+ or.release();
+ }
}
/**
* Open an object from this database.
* <p>
- * Alternates (if present) are searched automatically.
+ * This is a one-shot call interface which may be faster than allocating a
+ * {@link #newReader()} to perform the lookup.
*
- * @param curs
- * temporary working space associated with the calling thread.
* @param objectId
* identity of the object to open.
- * @return a {@link ObjectLoader} for accessing the data of the named
- * object, or null if the object does not exist.
+ * @return a {@link ObjectLoader} for accessing the object.
+ * @throws MissingObjectException
+ * the object does not exist.
* @throws IOException
+ * the object store cannot be accessed.
*/
- public final ObjectLoader openObject(final WindowCursor curs,
- final AnyObjectId objectId) throws IOException {
- ObjectLoader ldr;
-
- ldr = openObjectImpl1(curs, objectId);
- if (ldr != null) {
- return ldr;
- }
-
- ldr = openObjectImpl2(curs, objectId.name(), objectId);
- if (ldr != null) {
- return ldr;
- }
- return null;
- }
-
- private ObjectLoader openObjectImpl1(final WindowCursor curs,
- final AnyObjectId objectId) throws IOException {
- ObjectLoader ldr;
-
- ldr = openObject1(curs, objectId);
- if (ldr != null) {
- return ldr;
- }
- for (final ObjectDatabase alt : getAlternates()) {
- ldr = alt.openObjectImpl1(curs, objectId);
- if (ldr != null) {
- return ldr;
- }
- }
- if (tryAgain1()) {
- ldr = openObject1(curs, objectId);
- if (ldr != null) {
- return ldr;
- }
- }
- return null;
- }
-
- private ObjectLoader openObjectImpl2(final WindowCursor curs,
- final String objectName, final AnyObjectId objectId)
+ public ObjectLoader open(final AnyObjectId objectId)
throws IOException {
- ObjectLoader ldr;
-
- ldr = openObject2(curs, objectName, objectId);
- if (ldr != null) {
- return ldr;
- }
- for (final ObjectDatabase alt : getAlternates()) {
- ldr = alt.openObjectImpl2(curs, objectName, objectId);
- if (ldr != null) {
- return ldr;
- }
- }
- return null;
+ return open(objectId, ObjectReader.OBJ_ANY);
}
/**
- * Fast half of {@link #openObject(WindowCursor, AnyObjectId)}.
- *
- * @param curs
- * temporary working space associated with the calling thread.
- * @param objectId
- * identity of the object to open.
- * @return a {@link ObjectLoader} for accessing the data of the named
- * object, or null if the object does not exist.
- * @throws IOException
- */
- protected abstract ObjectLoader openObject1(WindowCursor curs,
- AnyObjectId objectId) throws IOException;
-
- /**
- * Slow half of {@link #openObject(WindowCursor, AnyObjectId)}.
- *
- * @param curs
- * temporary working space associated with the calling thread.
- * @param objectName
- * name of the object to open.
- * @param objectId
- * identity of the object to open.
- * @return a {@link ObjectLoader} for accessing the data of the named
- * object, or null if the object does not exist.
- * @throws IOException
- */
- protected ObjectLoader openObject2(WindowCursor curs, String objectName,
- AnyObjectId objectId) throws IOException {
- // Assume the search took place during openObject1.
- return null;
- }
-
- /**
- * Open the object from all packs containing it.
+ * Open an object from this database.
* <p>
- * If any alternates are present, their packs are also considered.
- *
- * @param out
- * result collection of loaders for this object, filled with
- * loaders from all packs containing specified object
- * @param curs
- * temporary working space associated with the calling thread.
- * @param objectId
- * id of object to search for
- * @throws IOException
- */
- final void openObjectInAllPacks(final Collection<PackedObjectLoader> out,
- final WindowCursor curs, final AnyObjectId objectId)
- throws IOException {
- openObjectInAllPacks1(out, curs, objectId);
- for (final ObjectDatabase alt : getAlternates()) {
- alt.openObjectInAllPacks1(out, curs, objectId);
- }
- }
-
- /**
- * Open the object from all packs containing it.
+ * This is a one-shot call interface which may be faster than allocating a
+ * {@link #newReader()} to perform the lookup.
*
- * @param out
- * result collection of loaders for this object, filled with
- * loaders from all packs containing specified object
- * @param curs
- * temporary working space associated with the calling thread.
* @param objectId
- * id of object to search for
- * @throws IOException
- */
- void openObjectInAllPacks1(Collection<PackedObjectLoader> out,
- WindowCursor curs, AnyObjectId objectId) throws IOException {
- // Assume no pack support
- }
-
- /**
- * @return true if the fast-half search should be tried again.
- */
- protected boolean tryAgain1() {
- return false;
- }
-
- /**
- * Get the alternate databases known to this database.
- *
- * @return the alternate list. Never null, but may be an empty array.
- */
- public final ObjectDatabase[] getAlternates() {
- ObjectDatabase[] r = alternates.get();
- if (r == null) {
- synchronized (alternates) {
- r = alternates.get();
- if (r == null) {
- try {
- r = loadAlternates();
- } catch (IOException e) {
- r = NO_ALTERNATES;
- }
- alternates.set(r);
- }
- }
- }
- return r;
- }
-
- /**
- * Load the list of alternate databases into memory.
- * <p>
- * This method is invoked by {@link #getAlternates()} if the alternate list
- * has not yet been populated, or if {@link #closeAlternates()} has been
- * called on this instance and the alternate list is needed again.
- * <p>
- * If the alternate array is empty, implementors should consider using the
- * constant {@link #NO_ALTERNATES}.
- *
- * @return the alternate list for this database.
+ * identity of the object to open.
+ * @param typeHint
+ * hint about the type of object being requested;
+ * {@link ObjectReader#OBJ_ANY} if the object type is not known,
+ * or does not matter to the caller.
+ * @return a {@link ObjectLoader} for accessing the object.
+ * @throws MissingObjectException
+ * the object does not exist.
+ * @throws IncorrectObjectTypeException
+ * typeHint was not OBJ_ANY, and the object's actual type does
+ * not match typeHint.
* @throws IOException
- * the alternate list could not be accessed. The empty alternate
- * array {@link #NO_ALTERNATES} will be assumed by the caller.
- */
- protected ObjectDatabase[] loadAlternates() throws IOException {
- return NO_ALTERNATES;
- }
-
- /**
- * Close the list of alternates returned by {@link #loadAlternates()}.
- *
- * @param alt
- * the alternate list, from {@link #loadAlternates()}.
- */
- protected void closeAlternates(ObjectDatabase[] alt) {
- for (final ObjectDatabase d : alt) {
- d.close();
+ * the object store cannot be accessed.
+ */
+ public ObjectLoader open(AnyObjectId objectId, int typeHint)
+ throws MissingObjectException, IncorrectObjectTypeException,
+ IOException {
+ final ObjectReader or = newReader();
+ try {
+ return or.open(objectId, typeHint);
+ } finally {
+ or.release();
}
}
@@ -387,9 +185,8 @@ public abstract class ObjectDatabase {
* done after instance creation might fail to be noticed.
*
* @return new cached database instance
- * @see CachedObjectDatabase
*/
public ObjectDatabase newCachedDatabase() {
- return new CachedObjectDatabase(this);
+ return this;
}
}
diff --git a/org.eclipse.jgit/src/org/eclipse/jgit/lib/ObjectInserter.java b/org.eclipse.jgit/src/org/eclipse/jgit/lib/ObjectInserter.java
new file mode 100644
index 0000000000..fd99d39e78
--- /dev/null
+++ b/org.eclipse.jgit/src/org/eclipse/jgit/lib/ObjectInserter.java
@@ -0,0 +1,395 @@
+/*
+ * Copyright (C) 2007, Robin Rosenberg <robin.rosenberg@dewire.com>
+ * Copyright (C) 2008, Shawn O. Pearce <spearce@spearce.org>
+ * Copyright (C) 2009, Google Inc.
+ * and other copyright owners as documented in the project's IP log.
+ *
+ * This program and the accompanying materials are made available
+ * under the terms of the Eclipse Distribution License v1.0 which
+ * accompanies this distribution, is reproduced below, and is
+ * available at http://www.eclipse.org/org/documents/edl-v10.php
+ *
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or
+ * without modification, are permitted provided that the following
+ * conditions are met:
+ *
+ * - Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ *
+ * - Redistributions in binary form must reproduce the above
+ * copyright notice, this list of conditions and the following
+ * disclaimer in the documentation and/or other materials provided
+ * with the distribution.
+ *
+ * - Neither the name of the Eclipse Foundation, Inc. nor the
+ * names of its contributors may be used to endorse or promote
+ * products derived from this software without specific prior
+ * written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND
+ * CONTRIBUTORS "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES,
+ * INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
+ * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR
+ * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
+ * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT
+ * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
+ * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER
+ * CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT,
+ * STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
+ * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF
+ * ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ */
+
+package org.eclipse.jgit.lib;
+
+import java.io.ByteArrayInputStream;
+import java.io.ByteArrayOutputStream;
+import java.io.EOFException;
+import java.io.IOException;
+import java.io.InputStream;
+import java.io.OutputStreamWriter;
+import java.io.UnsupportedEncodingException;
+import java.security.MessageDigest;
+import java.text.MessageFormat;
+
+import org.eclipse.jgit.JGitText;
+import org.eclipse.jgit.errors.ObjectWritingException;
+
+/**
+ * Inserts objects into an existing {@code ObjectDatabase}.
+ * <p>
+ * An inserter is not thread-safe. Individual threads should each obtain their
+ * own unique inserter instance, or must arrange for locking at a higher level
+ * to ensure the inserter is in use by no more than one thread at a time.
+ * <p>
+ * Objects written by an inserter may not be immediately visible for reading
+ * after the insert method completes. Callers must invoke either
+ * {@link #release()} or {@link #flush()} prior to updating references or
+ * otherwise making the returned ObjectIds visible to other code.
+ */
+public abstract class ObjectInserter {
+ private static final byte[] htree = Constants.encodeASCII("tree");
+
+ private static final byte[] hparent = Constants.encodeASCII("parent");
+
+ private static final byte[] hauthor = Constants.encodeASCII("author");
+
+ private static final byte[] hcommitter = Constants.encodeASCII("committer");
+
+ private static final byte[] hencoding = Constants.encodeASCII("encoding");
+
+ /** An inserter that can be used for formatting and id generation only. */
+ public static class Formatter extends ObjectInserter {
+ @Override
+ public ObjectId insert(int objectType, long length, InputStream in)
+ throws IOException {
+ throw new UnsupportedOperationException();
+ }
+
+ @Override
+ public void flush() throws IOException {
+ // Do nothing.
+ }
+
+ @Override
+ public void release() {
+ // Do nothing.
+ }
+ }
+
+ /** Digest to compute the name of an object. */
+ private final MessageDigest digest;
+
+ /** Temporary working buffer for streaming data through. */
+ private byte[] tempBuffer;
+
+ /** Create a new inserter for a database. */
+ protected ObjectInserter() {
+ digest = Constants.newMessageDigest();
+ }
+
+ /** @return a temporary byte array for use by the caller. */
+ protected byte[] buffer() {
+ if (tempBuffer == null)
+ tempBuffer = new byte[8192];
+ return tempBuffer;
+ }
+
+ /** @return digest to help compute an ObjectId */
+ protected MessageDigest digest() {
+ digest.reset();
+ return digest;
+ }
+
+ /**
+ * Compute the name of an object, without inserting it.
+ *
+ * @param type
+ * type code of the object to store.
+ * @param data
+ * complete content of the object.
+ * @return the name of the object.
+ */
+ public ObjectId idFor(int type, byte[] data) {
+ return idFor(type, data, 0, data.length);
+ }
+
+ /**
+ * Compute the name of an object, without inserting it.
+ *
+ * @param type
+ * type code of the object to store.
+ * @param data
+ * complete content of the object.
+ * @param off
+ * first position within {@code data}.
+ * @param len
+ * number of bytes to copy from {@code data}.
+ * @return the name of the object.
+ */
+ public ObjectId idFor(int type, byte[] data, int off, int len) {
+ MessageDigest md = digest();
+ md.update(Constants.encodedTypeString(type));
+ md.update((byte) ' ');
+ md.update(Constants.encodeASCII(len));
+ md.update((byte) 0);
+ md.update(data, off, len);
+ return ObjectId.fromRaw(md.digest());
+ }
+
+ /**
+ * Compute the name of an object, without inserting it.
+ *
+ * @param objectType
+ * type code of the object to store.
+ * @param length
+ * number of bytes to scan from {@code in}.
+ * @param in
+ * stream providing the object content. The caller is responsible
+ * for closing the stream.
+ * @return the name of the object.
+ * @throws IOException
+ * the source stream could not be read.
+ */
+ public ObjectId idFor(int objectType, long length, InputStream in)
+ throws IOException {
+ MessageDigest md = digest();
+ md.update(Constants.encodedTypeString(objectType));
+ md.update((byte) ' ');
+ md.update(Constants.encodeASCII(length));
+ md.update((byte) 0);
+ byte[] buf = buffer();
+ while (length > 0) {
+ int n = in.read(buf, 0, (int) Math.min(length, buf.length));
+ if (n < 0)
+ throw new EOFException("Unexpected end of input");
+ md.update(buf, 0, n);
+ length -= n;
+ }
+ return ObjectId.fromRaw(md.digest());
+ }
+
+ /**
+ * Insert a single object into the store, returning its unique name.
+ *
+ * @param type
+ * type code of the object to store.
+ * @param data
+ * complete content of the object.
+ * @return the name of the object.
+ * @throws IOException
+ * the object could not be stored.
+ */
+ public ObjectId insert(final int type, final byte[] data)
+ throws IOException {
+ return insert(type, data, 0, data.length);
+ }
+
+ /**
+ * Insert a single object into the store, returning its unique name.
+ *
+ * @param type
+ * type code of the object to store.
+ * @param data
+ * complete content of the object.
+ * @param off
+ * first position within {@code data}.
+ * @param len
+ * number of bytes to copy from {@code data}.
+ * @return the name of the object.
+ * @throws IOException
+ * the object could not be stored.
+ */
+ public ObjectId insert(int type, byte[] data, int off, int len)
+ throws IOException {
+ return insert(type, len, new ByteArrayInputStream(data, off, len));
+ }
+
+ /**
+ * Insert a single object into the store, returning its unique name.
+ *
+ * @param objectType
+ * type code of the object to store.
+ * @param length
+ * number of bytes to copy from {@code in}.
+ * @param in
+ * stream providing the object content. The caller is responsible
+ * for closing the stream.
+ * @return the name of the object.
+ * @throws IOException
+ * the object could not be stored, or the source stream could
+ * not be read.
+ */
+ public abstract ObjectId insert(int objectType, long length, InputStream in)
+ throws IOException;
+
+ /**
+ * Make all inserted objects visible.
+ * <p>
+ * The flush may take some period of time to make the objects available to
+ * other threads.
+ *
+ * @throws IOException
+ * the flush could not be completed; objects inserted thus far
+ * are in an indeterminate state.
+ */
+ public abstract void flush() throws IOException;
+
+ /**
+ * Release any resources used by this inserter.
+ * <p>
+ * An inserter that has been released can be used again, but may need to be
+ * released after the subsequent usage.
+ */
+ public abstract void release();
+
+ /**
+ * Format a Tree in canonical format.
+ *
+ * @param tree
+ * the tree object to format
+ * @return canonical encoding of the tree object.
+ * @throws IOException
+ * the tree cannot be loaded, or its not in a writable state.
+ */
+ public final byte[] format(Tree tree) throws IOException {
+ ByteArrayOutputStream o = new ByteArrayOutputStream();
+ for (TreeEntry e : tree.members()) {
+ ObjectId id = e.getId();
+ if (id == null)
+ throw new ObjectWritingException(MessageFormat.format(JGitText
+ .get().objectAtPathDoesNotHaveId, e.getFullName()));
+
+ e.getMode().copyTo(o);
+ o.write(' ');
+ o.write(e.getNameUTF8());
+ o.write(0);
+ id.copyRawTo(o);
+ }
+ return o.toByteArray();
+ }
+
+ /**
+ * Format a Commit in canonical format.
+ *
+ * @param commit
+ * the commit object to format
+ * @return canonical encoding of the commit object.
+ * @throws UnsupportedEncodingException
+ * the commit's chosen encoding isn't supported on this JVM.
+ */
+ public final byte[] format(Commit commit)
+ throws UnsupportedEncodingException {
+ String encoding = commit.getEncoding();
+ if (encoding == null)
+ encoding = Constants.CHARACTER_ENCODING;
+
+ ByteArrayOutputStream os = new ByteArrayOutputStream();
+ OutputStreamWriter w = new OutputStreamWriter(os, encoding);
+ try {
+ os.write(htree);
+ os.write(' ');
+ commit.getTreeId().copyTo(os);
+ os.write('\n');
+
+ ObjectId[] ps = commit.getParentIds();
+ for (int i = 0; i < ps.length; ++i) {
+ os.write(hparent);
+ os.write(' ');
+ ps[i].copyTo(os);
+ os.write('\n');
+ }
+
+ os.write(hauthor);
+ os.write(' ');
+ w.write(commit.getAuthor().toExternalString());
+ w.flush();
+ os.write('\n');
+
+ os.write(hcommitter);
+ os.write(' ');
+ w.write(commit.getCommitter().toExternalString());
+ w.flush();
+ os.write('\n');
+
+ if (!encoding.equals(Constants.CHARACTER_ENCODING)) {
+ os.write(hencoding);
+ os.write(' ');
+ os.write(Constants.encodeASCII(encoding));
+ os.write('\n');
+ }
+
+ os.write('\n');
+ w.write(commit.getMessage());
+ w.flush();
+ } catch (IOException err) {
+ // This should never occur, the only way to get it above is
+ // for the ByteArrayOutputStream to throw, but it doesn't.
+ //
+ throw new RuntimeException(err);
+ }
+ return os.toByteArray();
+ }
+
+ /**
+ * Format a Tag in canonical format.
+ *
+ * @param tag
+ * the tag object to format
+ * @return canonical encoding of the tag object.
+ */
+ public final byte[] format(Tag tag) {
+ ByteArrayOutputStream os = new ByteArrayOutputStream();
+ OutputStreamWriter w = new OutputStreamWriter(os, Constants.CHARSET);
+ try {
+ w.write("object ");
+ tag.getObjId().copyTo(w);
+ w.write('\n');
+
+ w.write("type ");
+ w.write(tag.getType());
+ w.write("\n");
+
+ w.write("tag ");
+ w.write(tag.getTag());
+ w.write("\n");
+
+ w.write("tagger ");
+ w.write(tag.getAuthor().toExternalString());
+ w.write('\n');
+
+ w.write('\n');
+ w.write(tag.getMessage());
+ w.close();
+ } catch (IOException err) {
+ // This should never occur, the only way to get it above is
+ // for the ByteArrayOutputStream to throw, but it doesn't.
+ //
+ throw new RuntimeException(err);
+ }
+ return os.toByteArray();
+ }
+}
diff --git a/org.eclipse.jgit/src/org/eclipse/jgit/lib/ObjectLoader.java b/org.eclipse.jgit/src/org/eclipse/jgit/lib/ObjectLoader.java
index 4839a1c28b..e19bfc4fba 100644
--- a/org.eclipse.jgit/src/org/eclipse/jgit/lib/ObjectLoader.java
+++ b/org.eclipse.jgit/src/org/eclipse/jgit/lib/ObjectLoader.java
@@ -47,6 +47,12 @@
package org.eclipse.jgit.lib;
+import java.io.EOFException;
+import java.io.IOException;
+import java.io.OutputStream;
+
+import org.eclipse.jgit.errors.LargeObjectException;
+import org.eclipse.jgit.errors.MissingObjectException;
/**
* Base class for a set of loaders for different representations of Git objects.
@@ -54,6 +60,14 @@ package org.eclipse.jgit.lib;
*/
public abstract class ObjectLoader {
/**
+ * Default setting for the large object threshold.
+ * <p>
+ * Objects larger than this size must be accessed as a stream through the
+ * loader's {@link #openStream()} method.
+ */
+ public static final int STREAM_THRESHOLD = 1024 * 1024;
+
+ /**
* @return Git in pack object type, see {@link Constants}.
*/
public abstract int getType();
@@ -64,14 +78,32 @@ public abstract class ObjectLoader {
public abstract long getSize();
/**
+ * @return true if this object is too large to obtain as a byte array.
+ * Objects over a certain threshold should be accessed only by their
+ * {@link #openStream()} to prevent overflowing the JVM heap.
+ */
+ public boolean isLarge() {
+ try {
+ getCachedBytes();
+ return false;
+ } catch (LargeObjectException tooBig) {
+ return true;
+ }
+ }
+
+ /**
* Obtain a copy of the bytes of this object.
* <p>
* Unlike {@link #getCachedBytes()} this method returns an array that might
* be modified by the caller.
*
* @return the bytes of this object.
+ * @throws LargeObjectException
+ * if the object won't fit into a byte array, because
+ * {@link #isLarge()} returns true. Callers should use
+ * {@link #openStream()} instead to access the contents.
*/
- public final byte[] getBytes() {
+ public final byte[] getBytes() throws LargeObjectException {
final byte[] data = getCachedBytes();
final byte[] copy = new byte[data.length];
System.arraycopy(data, 0, copy, 0, data.length);
@@ -87,19 +119,120 @@ public abstract class ObjectLoader {
* Changes (if made) will affect the cache but not the repository itself.
*
* @return the cached bytes of this object. Do not modify it.
+ * @throws LargeObjectException
+ * if the object won't fit into a byte array, because
+ * {@link #isLarge()} returns true. Callers should use
+ * {@link #openStream()} instead to access the contents.
+ */
+ public abstract byte[] getCachedBytes() throws LargeObjectException;
+
+ /**
+ * Obtain an input stream to read this object's data.
+ *
+ * @return a stream of this object's data. Caller must close the stream when
+ * through with it. The returned stream is buffered with a
+ * reasonable buffer size.
+ * @throws MissingObjectException
+ * the object no longer exists.
+ * @throws IOException
+ * the object store cannot be accessed.
*/
- public abstract byte[] getCachedBytes();
+ public abstract ObjectStream openStream() throws MissingObjectException,
+ IOException;
/**
- * @return raw object type from object header, as stored in storage (pack,
- * loose file). This may be different from {@link #getType()} result
- * for packs (see {@link Constants}).
+ * Copy this object to the output stream.
+ * <p>
+ * For some object store implementations, this method may be more efficient
+ * than reading from {@link #openStream()} into a temporary byte array, then
+ * writing to the destination stream.
+ * <p>
+ * The default implementation of this method is to copy with a temporary
+ * byte array for large objects, or to pass through the cached byte array
+ * for small objects.
+ *
+ * @param out
+ * stream to receive the complete copy of this object's data.
+ * Caller is responsible for flushing or closing this stream
+ * after this method returns.
+ * @throws MissingObjectException
+ * the object no longer exists.
+ * @throws IOException
+ * the object store cannot be accessed, or the stream cannot be
+ * written to.
*/
- public abstract int getRawType();
+ public void copyTo(OutputStream out) throws MissingObjectException,
+ IOException {
+ if (isLarge()) {
+ ObjectStream in = openStream();
+ try {
+ byte[] tmp = new byte[1024];
+ long copied = 0;
+ for (;;) {
+ int n = in.read(tmp);
+ if (n < 0)
+ break;
+ out.write(tmp, 0, n);
+ copied += n;
+ }
+ if (copied != getSize())
+ throw new EOFException();
+ } finally {
+ in.close();
+ }
+ } else {
+ out.write(getCachedBytes());
+ }
+ }
/**
- * @return raw size of object from object header (pack, loose file).
- * Interpretation of this value depends on {@link #getRawType()}.
+ * Simple loader around the cached byte array.
+ * <p>
+ * ObjectReader implementations can use this stream type when the object's
+ * content is small enough to be accessed as a single byte array.
*/
- public abstract long getRawSize();
+ public static class SmallObject extends ObjectLoader {
+ private final int type;
+
+ private final byte[] data;
+
+ /**
+ * Construct a small object loader.
+ *
+ * @param type
+ * type of the object.
+ * @param data
+ * the object's data array. This array will be returned as-is
+ * for the {@link #getCachedBytes()} method.
+ */
+ public SmallObject(int type, byte[] data) {
+ this.type = type;
+ this.data = data;
+ }
+
+ @Override
+ public int getType() {
+ return type;
+ }
+
+ @Override
+ public long getSize() {
+ return getCachedBytes().length;
+ }
+
+ @Override
+ public boolean isLarge() {
+ return false;
+ }
+
+ @Override
+ public byte[] getCachedBytes() {
+ return data;
+ }
+
+ @Override
+ public ObjectStream openStream() {
+ return new ObjectStream.SmallStream(this);
+ }
+ }
}
diff --git a/org.eclipse.jgit/src/org/eclipse/jgit/lib/ObjectReader.java b/org.eclipse.jgit/src/org/eclipse/jgit/lib/ObjectReader.java
new file mode 100644
index 0000000000..ae70638672
--- /dev/null
+++ b/org.eclipse.jgit/src/org/eclipse/jgit/lib/ObjectReader.java
@@ -0,0 +1,185 @@
+/*
+ * Copyright (C) 2010, Google Inc.
+ * and other copyright owners as documented in the project's IP log.
+ *
+ * This program and the accompanying materials are made available
+ * under the terms of the Eclipse Distribution License v1.0 which
+ * accompanies this distribution, is reproduced below, and is
+ * available at http://www.eclipse.org/org/documents/edl-v10.php
+ *
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or
+ * without modification, are permitted provided that the following
+ * conditions are met:
+ *
+ * - Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ *
+ * - Redistributions in binary form must reproduce the above
+ * copyright notice, this list of conditions and the following
+ * disclaimer in the documentation and/or other materials provided
+ * with the distribution.
+ *
+ * - Neither the name of the Eclipse Foundation, Inc. nor the
+ * names of its contributors may be used to endorse or promote
+ * products derived from this software without specific prior
+ * written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND
+ * CONTRIBUTORS "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES,
+ * INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
+ * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR
+ * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
+ * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT
+ * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
+ * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER
+ * CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT,
+ * STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
+ * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF
+ * ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ */
+
+package org.eclipse.jgit.lib;
+
+import java.io.IOException;
+
+import org.eclipse.jgit.errors.IncorrectObjectTypeException;
+import org.eclipse.jgit.errors.MissingObjectException;
+import org.eclipse.jgit.storage.pack.ObjectReuseAsIs;
+
+/**
+ * Reads an {@link ObjectDatabase} for a single thread.
+ * <p>
+ * Readers that can support efficient reuse of pack encoded objects should also
+ * implement the companion interface {@link ObjectReuseAsIs}.
+ */
+public abstract class ObjectReader {
+ /** Type hint indicating the caller doesn't know the type. */
+ protected static final int OBJ_ANY = -1;
+
+ /**
+ * Construct a new reader from the same data.
+ * <p>
+ * Applications can use this method to build a new reader from the same data
+ * source, but for an different thread.
+ *
+ * @return a brand new reader, using the same data source.
+ */
+ public abstract ObjectReader newReader();
+
+ /**
+ * Does the requested object exist in this database?
+ *
+ * @param objectId
+ * identity of the object to test for existence of.
+ * @return true if the specified object is stored in this database.
+ * @throws IOException
+ * the object store cannot be accessed.
+ */
+ public boolean has(AnyObjectId objectId) throws IOException {
+ return has(objectId, OBJ_ANY);
+ }
+
+ /**
+ * Does the requested object exist in this database?
+ *
+ * @param objectId
+ * identity of the object to test for existence of.
+ * @param typeHint
+ * hint about the type of object being requested;
+ * {@link #OBJ_ANY} if the object type is not known, or does not
+ * matter to the caller.
+ * @return true if the specified object is stored in this database.
+ * @throws IncorrectObjectTypeException
+ * typeHint was not OBJ_ANY, and the object's actual type does
+ * not match typeHint.
+ * @throws IOException
+ * the object store cannot be accessed.
+ */
+ public boolean has(AnyObjectId objectId, int typeHint) throws IOException {
+ try {
+ open(objectId, typeHint);
+ return true;
+ } catch (MissingObjectException notFound) {
+ return false;
+ }
+ }
+
+ /**
+ * Open an object from this database.
+ *
+ * @param objectId
+ * identity of the object to open.
+ * @return a {@link ObjectLoader} for accessing the object.
+ * @throws MissingObjectException
+ * the object does not exist.
+ * @throws IOException
+ * the object store cannot be accessed.
+ */
+ public ObjectLoader open(AnyObjectId objectId)
+ throws MissingObjectException, IOException {
+ return open(objectId, OBJ_ANY);
+ }
+
+ /**
+ * Open an object from this database.
+ *
+ * @param objectId
+ * identity of the object to open.
+ * @param typeHint
+ * hint about the type of object being requested;
+ * {@link #OBJ_ANY} if the object type is not known, or does not
+ * matter to the caller.
+ * @return a {@link ObjectLoader} for accessing the object.
+ * @throws MissingObjectException
+ * the object does not exist.
+ * @throws IncorrectObjectTypeException
+ * typeHint was not OBJ_ANY, and the object's actual type does
+ * not match typeHint.
+ * @throws IOException
+ * the object store cannot be accessed.
+ */
+ public abstract ObjectLoader open(AnyObjectId objectId, int typeHint)
+ throws MissingObjectException, IncorrectObjectTypeException,
+ IOException;
+
+ /**
+ * Get only the size of an object.
+ * <p>
+ * The default implementation of this method opens an ObjectLoader.
+ * Databases are encouraged to override this if a faster access method is
+ * available to them.
+ *
+ * @param objectId
+ * identity of the object to open.
+ * @param typeHint
+ * hint about the type of object being requested;
+ * {@link #OBJ_ANY} if the object type is not known, or does not
+ * matter to the caller.
+ * @return size of object in bytes.
+ * @throws MissingObjectException
+ * the object does not exist.
+ * @throws IncorrectObjectTypeException
+ * typeHint was not OBJ_ANY, and the object's actual type does
+ * not match typeHint.
+ * @throws IOException
+ * the object store cannot be accessed.
+ */
+ public long getObjectSize(AnyObjectId objectId, int typeHint)
+ throws MissingObjectException, IncorrectObjectTypeException,
+ IOException {
+ return open(objectId, typeHint).getSize();
+ }
+
+ /**
+ * Release any resources used by this reader.
+ * <p>
+ * A reader that has been released can be used again, but may need to be
+ * released after the subsequent usage.
+ */
+ public void release() {
+ // Do nothing.
+ }
+}
diff --git a/org.eclipse.jgit/src/org/eclipse/jgit/lib/ObjectStream.java b/org.eclipse.jgit/src/org/eclipse/jgit/lib/ObjectStream.java
new file mode 100644
index 0000000000..86d66439d0
--- /dev/null
+++ b/org.eclipse.jgit/src/org/eclipse/jgit/lib/ObjectStream.java
@@ -0,0 +1,220 @@
+/*
+ * Copyright (C) 2010, Google Inc.
+ * and other copyright owners as documented in the project's IP log.
+ *
+ * This program and the accompanying materials are made available
+ * under the terms of the Eclipse Distribution License v1.0 which
+ * accompanies this distribution, is reproduced below, and is
+ * available at http://www.eclipse.org/org/documents/edl-v10.php
+ *
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or
+ * without modification, are permitted provided that the following
+ * conditions are met:
+ *
+ * - Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ *
+ * - Redistributions in binary form must reproduce the above
+ * copyright notice, this list of conditions and the following
+ * disclaimer in the documentation and/or other materials provided
+ * with the distribution.
+ *
+ * - Neither the name of the Eclipse Foundation, Inc. nor the
+ * names of its contributors may be used to endorse or promote
+ * products derived from this software without specific prior
+ * written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND
+ * CONTRIBUTORS "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES,
+ * INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
+ * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR
+ * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
+ * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT
+ * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
+ * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER
+ * CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT,
+ * STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
+ * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF
+ * ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ */
+
+package org.eclipse.jgit.lib;
+
+import java.io.IOException;
+import java.io.InputStream;
+
+/** Stream of data coming from an object loaded by {@link ObjectLoader}. */
+public abstract class ObjectStream extends InputStream {
+ /** @return Git object type, see {@link Constants}. */
+ public abstract int getType();
+
+ /** @return total size of object in bytes */
+ public abstract long getSize();
+
+ /**
+ * Simple stream around the cached byte array created by a loader.
+ * <p>
+ * ObjectLoader implementations can use this stream type when the object's
+ * content is small enough to be accessed as a single byte array, but the
+ * application has still requested it in stream format.
+ */
+ public static class SmallStream extends ObjectStream {
+ private final int type;
+
+ private final byte[] data;
+
+ private int ptr;
+
+ private int mark;
+
+ /**
+ * Create the stream from an existing loader's cached bytes.
+ *
+ * @param loader
+ * the loader.
+ */
+ public SmallStream(ObjectLoader loader) {
+ this.type = loader.getType();
+ this.data = loader.getCachedBytes();
+ }
+
+ @Override
+ public int getType() {
+ return type;
+ }
+
+ @Override
+ public long getSize() {
+ return data.length;
+ }
+
+ @Override
+ public int available() {
+ return data.length - ptr;
+ }
+
+ @Override
+ public long skip(long n) {
+ int s = (int) Math.min(available(), Math.max(0, n));
+ ptr += s;
+ return s;
+ }
+
+ @Override
+ public int read() {
+ if (ptr == data.length)
+ return -1;
+ return data[ptr++] & 0xff;
+ }
+
+ @Override
+ public int read(byte[] b, int off, int len) {
+ if (ptr == data.length)
+ return -1;
+ int n = Math.min(available(), len);
+ System.arraycopy(data, ptr, b, off, n);
+ ptr += n;
+ return n;
+ }
+
+ @Override
+ public boolean markSupported() {
+ return true;
+ }
+
+ @Override
+ public void mark(int readlimit) {
+ mark = ptr;
+ }
+
+ @Override
+ public void reset() {
+ ptr = mark;
+ }
+ }
+
+ /**
+ * Simple filter stream around another stream.
+ * <p>
+ * ObjectLoader implementations can use this stream type when the object's
+ * content is available from a standard InputStream.
+ */
+ public static class Filter extends ObjectStream {
+ private final int type;
+
+ private final long size;
+
+ private final InputStream in;
+
+ /**
+ * Create a filter stream for an object.
+ *
+ * @param type
+ * the type of the object.
+ * @param size
+ * total size of the object, in bytes.
+ * @param in
+ * stream the object's raw data is available from. This
+ * stream should be buffered with some reasonable amount of
+ * buffering.
+ */
+ public Filter(int type, long size, InputStream in) {
+ this.type = type;
+ this.size = size;
+ this.in = in;
+ }
+
+ @Override
+ public int getType() {
+ return type;
+ }
+
+ @Override
+ public long getSize() {
+ return size;
+ }
+
+ @Override
+ public int available() throws IOException {
+ return in.available();
+ }
+
+ @Override
+ public long skip(long n) throws IOException {
+ return in.skip(n);
+ }
+
+ @Override
+ public int read() throws IOException {
+ return in.read();
+ }
+
+ @Override
+ public int read(byte[] b, int off, int len) throws IOException {
+ return in.read(b, off, len);
+ }
+
+ @Override
+ public boolean markSupported() {
+ return in.markSupported();
+ }
+
+ @Override
+ public void mark(int readlimit) {
+ in.mark(readlimit);
+ }
+
+ @Override
+ public void reset() throws IOException {
+ in.reset();
+ }
+
+ @Override
+ public void close() throws IOException {
+ in.close();
+ }
+ }
+}
diff --git a/org.eclipse.jgit/src/org/eclipse/jgit/lib/ObjectWriter.java b/org.eclipse.jgit/src/org/eclipse/jgit/lib/ObjectWriter.java
index 20147ed6ce..ce91efb8e4 100644
--- a/org.eclipse.jgit/src/org/eclipse/jgit/lib/ObjectWriter.java
+++ b/org.eclipse.jgit/src/org/eclipse/jgit/lib/ObjectWriter.java
@@ -1,6 +1,7 @@
/*
* Copyright (C) 2007, Robin Rosenberg <robin.rosenberg@dewire.com>
* Copyright (C) 2006-2008, Shawn O. Pearce <spearce@spearce.org>
+ * Copyright (C) 2009, Google Inc.
* and other copyright owners as documented in the project's IP log.
*
* This program and the accompanying materials are made available
@@ -44,61 +45,49 @@
package org.eclipse.jgit.lib;
-import java.io.ByteArrayInputStream;
-import java.io.ByteArrayOutputStream;
+import static org.eclipse.jgit.lib.Constants.OBJ_BLOB;
+import static org.eclipse.jgit.lib.Constants.OBJ_COMMIT;
+import static org.eclipse.jgit.lib.Constants.OBJ_TAG;
+import static org.eclipse.jgit.lib.Constants.OBJ_TREE;
+
import java.io.File;
import java.io.FileInputStream;
-import java.io.FileOutputStream;
import java.io.IOException;
import java.io.InputStream;
-import java.io.OutputStreamWriter;
-import java.security.MessageDigest;
-import java.text.MessageFormat;
-import java.util.zip.Deflater;
-import java.util.zip.DeflaterOutputStream;
-
-import org.eclipse.jgit.JGitText;
-import org.eclipse.jgit.errors.ObjectWritingException;
/**
* A class for writing loose objects.
+ *
+ * @deprecated Use {@link Repository#newObjectInserter()}.
*/
public class ObjectWriter {
- private static final byte[] htree = Constants.encodeASCII("tree");
-
- private static final byte[] hparent = Constants.encodeASCII("parent");
-
- private static final byte[] hauthor = Constants.encodeASCII("author");
-
- private static final byte[] hcommitter = Constants.encodeASCII("committer");
-
- private static final byte[] hencoding = Constants.encodeASCII("encoding");
-
- private final Repository r;
-
- private final byte[] buf;
-
- private final MessageDigest md;
+ private final ObjectInserter inserter;
/**
* Construct an Object writer for the specified repository
+ *
* @param d
*/
public ObjectWriter(final Repository d) {
- r = d;
- buf = new byte[8192];
- md = Constants.newMessageDigest();
+ inserter = d.newObjectInserter();
}
/**
* Write a blob with the specified data
*
- * @param b bytes of the blob
+ * @param b
+ * bytes of the blob
* @return SHA-1 of the blob
* @throws IOException
*/
public ObjectId writeBlob(final byte[] b) throws IOException {
- return writeBlob(b.length, new ByteArrayInputStream(b));
+ try {
+ ObjectId id = inserter.insert(OBJ_BLOB, b);
+ inserter.flush();
+ return id;
+ } finally {
+ inserter.release();
+ }
}
/**
@@ -130,174 +119,101 @@ public class ObjectWriter {
*/
public ObjectId writeBlob(final long len, final InputStream is)
throws IOException {
- return writeObject(Constants.OBJ_BLOB, len, is, true);
+ try {
+ ObjectId id = inserter.insert(OBJ_BLOB, len, is);
+ inserter.flush();
+ return id;
+ } finally {
+ inserter.release();
+ }
}
/**
* Write a Tree to the object database.
*
- * @param t
+ * @param tree
* Tree
* @return SHA-1 of the tree
* @throws IOException
*/
- public ObjectId writeTree(final Tree t) throws IOException {
- final ByteArrayOutputStream o = new ByteArrayOutputStream();
- final TreeEntry[] items = t.members();
- for (int k = 0; k < items.length; k++) {
- final TreeEntry e = items[k];
- final ObjectId id = e.getId();
-
- if (id == null)
- throw new ObjectWritingException(MessageFormat.format(
- JGitText.get().objectAtPathDoesNotHaveId, e.getFullName()));
-
- e.getMode().copyTo(o);
- o.write(' ');
- o.write(e.getNameUTF8());
- o.write(0);
- id.copyRawTo(o);
+ public ObjectId writeTree(Tree tree) throws IOException {
+ try {
+ ObjectId id = inserter.insert(OBJ_TREE, inserter.format(tree));
+ inserter.flush();
+ return id;
+ } finally {
+ inserter.release();
}
- return writeCanonicalTree(o.toByteArray());
}
/**
* Write a canonical tree to the object database.
*
- * @param b
+ * @param treeData
* the canonical encoding of the tree object.
* @return SHA-1 of the tree
* @throws IOException
*/
- public ObjectId writeCanonicalTree(final byte[] b) throws IOException {
- return writeTree(b.length, new ByteArrayInputStream(b));
- }
-
- private ObjectId writeTree(final long len, final InputStream is)
- throws IOException {
- return writeObject(Constants.OBJ_TREE, len, is, true);
+ public ObjectId writeCanonicalTree(byte[] treeData) throws IOException {
+ try {
+ ObjectId id = inserter.insert(OBJ_TREE, treeData);
+ inserter.flush();
+ return id;
+ } finally {
+ inserter.release();
+ }
}
/**
* Write a Commit to the object database
*
- * @param c
+ * @param commit
* Commit to store
* @return SHA-1 of the commit
* @throws IOException
*/
- public ObjectId writeCommit(final Commit c) throws IOException {
- final ByteArrayOutputStream os = new ByteArrayOutputStream();
- String encoding = c.getEncoding();
- if (encoding == null)
- encoding = Constants.CHARACTER_ENCODING;
- final OutputStreamWriter w = new OutputStreamWriter(os, encoding);
-
- os.write(htree);
- os.write(' ');
- c.getTreeId().copyTo(os);
- os.write('\n');
-
- ObjectId[] ps = c.getParentIds();
- for (int i=0; i<ps.length; ++i) {
- os.write(hparent);
- os.write(' ');
- ps[i].copyTo(os);
- os.write('\n');
- }
-
- os.write(hauthor);
- os.write(' ');
- w.write(c.getAuthor().toExternalString());
- w.flush();
- os.write('\n');
-
- os.write(hcommitter);
- os.write(' ');
- w.write(c.getCommitter().toExternalString());
- w.flush();
- os.write('\n');
-
- if (!encoding.equals(Constants.CHARACTER_ENCODING)) {
- os.write(hencoding);
- os.write(' ');
- os.write(Constants.encodeASCII(encoding));
- os.write('\n');
+ public ObjectId writeCommit(Commit commit) throws IOException {
+ try {
+ ObjectId id = inserter.insert(OBJ_COMMIT, inserter.format(commit));
+ inserter.flush();
+ return id;
+ } finally {
+ inserter.release();
}
-
- os.write('\n');
- w.write(c.getMessage());
- w.flush();
-
- return writeCommit(os.toByteArray());
- }
-
- private ObjectId writeTag(final byte[] b) throws IOException {
- return writeTag(b.length, new ByteArrayInputStream(b));
}
/**
* Write an annotated Tag to the object database
*
- * @param c
+ * @param tag
* Tag
* @return SHA-1 of the tag
* @throws IOException
*/
- public ObjectId writeTag(final Tag c) throws IOException {
- final ByteArrayOutputStream os = new ByteArrayOutputStream();
- final OutputStreamWriter w = new OutputStreamWriter(os,
- Constants.CHARSET);
-
- w.write("object ");
- c.getObjId().copyTo(w);
- w.write('\n');
-
- w.write("type ");
- w.write(c.getType());
- w.write("\n");
-
- w.write("tag ");
- w.write(c.getTag());
- w.write("\n");
-
- w.write("tagger ");
- w.write(c.getAuthor().toExternalString());
- w.write('\n');
-
- w.write('\n');
- w.write(c.getMessage());
- w.close();
-
- return writeTag(os.toByteArray());
- }
-
- private ObjectId writeCommit(final byte[] b) throws IOException {
- return writeCommit(b.length, new ByteArrayInputStream(b));
- }
-
- private ObjectId writeCommit(final long len, final InputStream is)
- throws IOException {
- return writeObject(Constants.OBJ_COMMIT, len, is, true);
- }
-
- private ObjectId writeTag(final long len, final InputStream is)
- throws IOException {
- return writeObject(Constants.OBJ_TAG, len, is, true);
+ public ObjectId writeTag(Tag tag) throws IOException {
+ try {
+ ObjectId id = inserter.insert(OBJ_TAG, inserter.format(tag));
+ inserter.flush();
+ return id;
+ } finally {
+ inserter.release();
+ }
}
/**
* Compute the SHA-1 of a blob without creating an object. This is for
* figuring out if we already have a blob or not.
*
- * @param len number of bytes to consume
- * @param is stream for read blob data from
+ * @param len
+ * number of bytes to consume
+ * @param is
+ * stream for read blob data from
* @return SHA-1 of a looked for blob
* @throws IOException
*/
- public ObjectId computeBlobSha1(final long len, final InputStream is)
+ public ObjectId computeBlobSha1(long len, InputStream is)
throws IOException {
- return writeObject(Constants.OBJ_BLOB, len, is, false);
+ return computeObjectSha1(OBJ_BLOB, len, is);
}
/**
@@ -313,119 +229,12 @@ public class ObjectWriter {
* @return SHA-1 of data combined with type information
* @throws IOException
*/
- public ObjectId computeObjectSha1(final int type, final long len, final InputStream is)
+ public ObjectId computeObjectSha1(int type, long len, InputStream is)
throws IOException {
- return writeObject(type, len, is, false);
- }
-
- ObjectId writeObject(final int type, long len, final InputStream is,
- boolean store) throws IOException {
- final File t;
- final DeflaterOutputStream deflateStream;
- final FileOutputStream fileStream;
- ObjectId id = null;
- Deflater def = null;
-
- if (store) {
- t = File.createTempFile("noz", null, r.getObjectsDirectory());
- fileStream = new FileOutputStream(t);
- } else {
- t = null;
- fileStream = null;
- }
-
- md.reset();
- if (store) {
- def = new Deflater(r.getConfig().getCore().getCompression());
- deflateStream = new DeflaterOutputStream(fileStream, def);
- } else
- deflateStream = null;
-
try {
- byte[] header;
- int n;
-
- header = Constants.encodedTypeString(type);
- md.update(header);
- if (deflateStream != null)
- deflateStream.write(header);
-
- md.update((byte) ' ');
- if (deflateStream != null)
- deflateStream.write((byte) ' ');
-
- header = Constants.encodeASCII(len);
- md.update(header);
- if (deflateStream != null)
- deflateStream.write(header);
-
- md.update((byte) 0);
- if (deflateStream != null)
- deflateStream.write((byte) 0);
-
- while (len > 0
- && (n = is.read(buf, 0, (int) Math.min(len, buf.length))) > 0) {
- md.update(buf, 0, n);
- if (deflateStream != null)
- deflateStream.write(buf, 0, n);
- len -= n;
- }
-
- if (len != 0)
- throw new IOException("Input did not match supplied length. "
- + len + " bytes are missing.");
-
- if (deflateStream != null ) {
- deflateStream.close();
- if (t != null)
- t.setReadOnly();
- }
-
- id = ObjectId.fromRaw(md.digest());
+ return inserter.idFor(type, len, is);
} finally {
- if (id == null && deflateStream != null) {
- try {
- deflateStream.close();
- } finally {
- t.delete();
- }
- }
- if (def != null) {
- def.end();
- }
+ inserter.release();
}
-
- if (t == null)
- return id;
-
- if (r.hasObject(id)) {
- // Object is already in the repository so remove
- // the temporary file.
- //
- t.delete();
- } else {
- final File o = r.toFile(id);
- if (!t.renameTo(o)) {
- // Maybe the directory doesn't exist yet as the object
- // directories are always lazily created. Note that we
- // try the rename first as the directory likely does exist.
- //
- o.getParentFile().mkdir();
- if (!t.renameTo(o)) {
- if (!r.hasObject(id)) {
- // The object failed to be renamed into its proper
- // location and it doesn't exist in the repository
- // either. We really don't know what went wrong, so
- // fail.
- //
- t.delete();
- throw new ObjectWritingException("Unable to"
- + " create new object: " + o);
- }
- }
- }
- }
-
- return id;
}
}
diff --git a/org.eclipse.jgit/src/org/eclipse/jgit/lib/PackFile.java b/org.eclipse.jgit/src/org/eclipse/jgit/lib/PackFile.java
deleted file mode 100644
index 829832e6a5..0000000000
--- a/org.eclipse.jgit/src/org/eclipse/jgit/lib/PackFile.java
+++ /dev/null
@@ -1,526 +0,0 @@
-/*
- * Copyright (C) 2008-2009, Google Inc.
- * Copyright (C) 2007, Robin Rosenberg <robin.rosenberg@dewire.com>
- * Copyright (C) 2006-2008, Shawn O. Pearce <spearce@spearce.org>
- * and other copyright owners as documented in the project's IP log.
- *
- * This program and the accompanying materials are made available
- * under the terms of the Eclipse Distribution License v1.0 which
- * accompanies this distribution, is reproduced below, and is
- * available at http://www.eclipse.org/org/documents/edl-v10.php
- *
- * All rights reserved.
- *
- * Redistribution and use in source and binary forms, with or
- * without modification, are permitted provided that the following
- * conditions are met:
- *
- * - Redistributions of source code must retain the above copyright
- * notice, this list of conditions and the following disclaimer.
- *
- * - Redistributions in binary form must reproduce the above
- * copyright notice, this list of conditions and the following
- * disclaimer in the documentation and/or other materials provided
- * with the distribution.
- *
- * - Neither the name of the Eclipse Foundation, Inc. nor the
- * names of its contributors may be used to endorse or promote
- * products derived from this software without specific prior
- * written permission.
- *
- * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND
- * CONTRIBUTORS "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES,
- * INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
- * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
- * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR
- * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
- * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT
- * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
- * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER
- * CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT,
- * STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
- * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF
- * ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
- */
-
-package org.eclipse.jgit.lib;
-
-import java.io.EOFException;
-import java.io.File;
-import java.io.IOException;
-import java.io.OutputStream;
-import java.io.RandomAccessFile;
-import java.nio.MappedByteBuffer;
-import java.nio.channels.FileChannel.MapMode;
-import java.text.MessageFormat;
-import java.util.Arrays;
-import java.util.Collections;
-import java.util.Comparator;
-import java.util.Iterator;
-import java.util.zip.CRC32;
-import java.util.zip.CheckedOutputStream;
-import java.util.zip.DataFormatException;
-
-import org.eclipse.jgit.JGitText;
-import org.eclipse.jgit.errors.CorruptObjectException;
-import org.eclipse.jgit.errors.PackInvalidException;
-import org.eclipse.jgit.errors.PackMismatchException;
-import org.eclipse.jgit.util.NB;
-import org.eclipse.jgit.util.RawParseUtils;
-
-/**
- * A Git version 2 pack file representation. A pack file contains Git objects in
- * delta packed format yielding high compression of lots of object where some
- * objects are similar.
- */
-public class PackFile implements Iterable<PackIndex.MutableEntry> {
- /** Sorts PackFiles to be most recently created to least recently created. */
- public static Comparator<PackFile> SORT = new Comparator<PackFile>() {
- public int compare(final PackFile a, final PackFile b) {
- return b.packLastModified - a.packLastModified;
- }
- };
-
- private final File idxFile;
-
- private final File packFile;
-
- final int hash;
-
- private RandomAccessFile fd;
-
- /** Serializes reads performed against {@link #fd}. */
- private final Object readLock = new Object();
-
- long length;
-
- private int activeWindows;
-
- private int activeCopyRawData;
-
- private int packLastModified;
-
- private volatile boolean invalid;
-
- private byte[] packChecksum;
-
- private PackIndex loadedIdx;
-
- private PackReverseIndex reverseIdx;
-
- /**
- * Construct a reader for an existing, pre-indexed packfile.
- *
- * @param idxFile
- * path of the <code>.idx</code> file listing the contents.
- * @param packFile
- * path of the <code>.pack</code> file holding the data.
- */
- public PackFile(final File idxFile, final File packFile) {
- this.idxFile = idxFile;
- this.packFile = packFile;
- this.packLastModified = (int) (packFile.lastModified() >> 10);
-
- // Multiply by 31 here so we can more directly combine with another
- // value in WindowCache.hash(), without doing the multiply there.
- //
- hash = System.identityHashCode(this) * 31;
- length = Long.MAX_VALUE;
- }
-
- private synchronized PackIndex idx() throws IOException {
- if (loadedIdx == null) {
- if (invalid)
- throw new PackInvalidException(packFile);
-
- try {
- final PackIndex idx = PackIndex.open(idxFile);
-
- if (packChecksum == null)
- packChecksum = idx.packChecksum;
- else if (!Arrays.equals(packChecksum, idx.packChecksum))
- throw new PackMismatchException(JGitText.get().packChecksumMismatch);
-
- loadedIdx = idx;
- } catch (IOException e) {
- invalid = true;
- throw e;
- }
- }
- return loadedIdx;
- }
-
- final PackedObjectLoader resolveBase(final WindowCursor curs, final long ofs)
- throws IOException {
- return reader(curs, ofs);
- }
-
- /** @return the File object which locates this pack on disk. */
- public File getPackFile() {
- return packFile;
- }
-
- /**
- * Determine if an object is contained within the pack file.
- * <p>
- * For performance reasons only the index file is searched; the main pack
- * content is ignored entirely.
- * </p>
- *
- * @param id
- * the object to look for. Must not be null.
- * @return true if the object is in this pack; false otherwise.
- * @throws IOException
- * the index file cannot be loaded into memory.
- */
- public boolean hasObject(final AnyObjectId id) throws IOException {
- return idx().hasObject(id);
- }
-
- /**
- * Get an object from this pack.
- *
- * @param curs
- * temporary working space associated with the calling thread.
- * @param id
- * the object to obtain from the pack. Must not be null.
- * @return the object loader for the requested object if it is contained in
- * this pack; null if the object was not found.
- * @throws IOException
- * the pack file or the index could not be read.
- */
- public PackedObjectLoader get(final WindowCursor curs, final AnyObjectId id)
- throws IOException {
- final long offset = idx().findOffset(id);
- return 0 < offset ? reader(curs, offset) : null;
- }
-
- /**
- * Close the resources utilized by this repository
- */
- public void close() {
- UnpackedObjectCache.purge(this);
- WindowCache.purge(this);
- synchronized (this) {
- loadedIdx = null;
- reverseIdx = null;
- }
- }
-
- /**
- * Provide iterator over entries in associated pack index, that should also
- * exist in this pack file. Objects returned by such iterator are mutable
- * during iteration.
- * <p>
- * Iterator returns objects in SHA-1 lexicographical order.
- * </p>
- *
- * @return iterator over entries of associated pack index
- *
- * @see PackIndex#iterator()
- */
- public Iterator<PackIndex.MutableEntry> iterator() {
- try {
- return idx().iterator();
- } catch (IOException e) {
- return Collections.<PackIndex.MutableEntry> emptyList().iterator();
- }
- }
-
- /**
- * Obtain the total number of objects available in this pack. This method
- * relies on pack index, giving number of effectively available objects.
- *
- * @return number of objects in index of this pack, likewise in this pack
- * @throws IOException
- * the index file cannot be loaded into memory.
- */
- long getObjectCount() throws IOException {
- return idx().getObjectCount();
- }
-
- /**
- * Search for object id with the specified start offset in associated pack
- * (reverse) index.
- *
- * @param offset
- * start offset of object to find
- * @return object id for this offset, or null if no object was found
- * @throws IOException
- * the index file cannot be loaded into memory.
- */
- ObjectId findObjectForOffset(final long offset) throws IOException {
- return getReverseIdx().findObject(offset);
- }
-
- final UnpackedObjectCache.Entry readCache(final long position) {
- return UnpackedObjectCache.get(this, position);
- }
-
- final void saveCache(final long position, final byte[] data, final int type) {
- UnpackedObjectCache.store(this, position, data, type);
- }
-
- final byte[] decompress(final long position, final int totalSize,
- final WindowCursor curs) throws DataFormatException, IOException {
- final byte[] dstbuf = new byte[totalSize];
- if (curs.inflate(this, position, dstbuf, 0) != totalSize)
- throw new EOFException(MessageFormat.format(JGitText.get().shortCompressedStreamAt, position));
- return dstbuf;
- }
-
- final void copyRawData(final PackedObjectLoader loader,
- final OutputStream out, final byte buf[], final WindowCursor curs)
- throws IOException {
- final long objectOffset = loader.objectOffset;
- final long dataOffset = objectOffset + loader.headerSize;
- final long sz = findEndOffset(objectOffset) - dataOffset;
- final PackIndex idx = idx();
-
- if (idx.hasCRC32Support()) {
- final CRC32 crc = new CRC32();
- int headerCnt = loader.headerSize;
- while (headerCnt > 0) {
- final int toRead = Math.min(headerCnt, buf.length);
- readFully(objectOffset, buf, 0, toRead, curs);
- crc.update(buf, 0, toRead);
- headerCnt -= toRead;
- }
- final CheckedOutputStream crcOut = new CheckedOutputStream(out, crc);
- copyToStream(dataOffset, buf, sz, crcOut, curs);
- final long computed = crc.getValue();
-
- final ObjectId id = findObjectForOffset(objectOffset);
- final long expected = idx.findCRC32(id);
- if (computed != expected)
- throw new CorruptObjectException(MessageFormat.format(
- JGitText.get().objectAtHasBadZlibStream, objectOffset, getPackFile()));
- } else {
- try {
- curs.inflateVerify(this, dataOffset);
- } catch (DataFormatException dfe) {
- final CorruptObjectException coe;
- coe = new CorruptObjectException(MessageFormat.format(
- JGitText.get().objectAtHasBadZlibStream, objectOffset, getPackFile()));
- coe.initCause(dfe);
- throw coe;
- }
- copyToStream(dataOffset, buf, sz, out, curs);
- }
- }
-
- boolean supportsFastCopyRawData() throws IOException {
- return idx().hasCRC32Support();
- }
-
- boolean invalid() {
- return invalid;
- }
-
- private void readFully(final long position, final byte[] dstbuf,
- int dstoff, final int cnt, final WindowCursor curs)
- throws IOException {
- if (curs.copy(this, position, dstbuf, dstoff, cnt) != cnt)
- throw new EOFException();
- }
-
- private void copyToStream(long position, final byte[] buf, long cnt,
- final OutputStream out, final WindowCursor curs)
- throws IOException, EOFException {
- while (cnt > 0) {
- final int toRead = (int) Math.min(cnt, buf.length);
- readFully(position, buf, 0, toRead, curs);
- position += toRead;
- cnt -= toRead;
- out.write(buf, 0, toRead);
- }
- }
-
- synchronized void beginCopyRawData() throws IOException {
- if (++activeCopyRawData == 1 && activeWindows == 0)
- doOpen();
- }
-
- synchronized void endCopyRawData() {
- if (--activeCopyRawData == 0 && activeWindows == 0)
- doClose();
- }
-
- synchronized boolean beginWindowCache() throws IOException {
- if (++activeWindows == 1) {
- if (activeCopyRawData == 0)
- doOpen();
- return true;
- }
- return false;
- }
-
- synchronized boolean endWindowCache() {
- final boolean r = --activeWindows == 0;
- if (r && activeCopyRawData == 0)
- doClose();
- return r;
- }
-
- private void doOpen() throws IOException {
- try {
- if (invalid)
- throw new PackInvalidException(packFile);
- synchronized (readLock) {
- fd = new RandomAccessFile(packFile, "r");
- length = fd.length();
- onOpenPack();
- }
- } catch (IOException ioe) {
- openFail();
- throw ioe;
- } catch (RuntimeException re) {
- openFail();
- throw re;
- } catch (Error re) {
- openFail();
- throw re;
- }
- }
-
- private void openFail() {
- activeWindows = 0;
- activeCopyRawData = 0;
- invalid = true;
- doClose();
- }
-
- private void doClose() {
- synchronized (readLock) {
- if (fd != null) {
- try {
- fd.close();
- } catch (IOException err) {
- // Ignore a close event. We had it open only for reading.
- // There should not be errors related to network buffers
- // not flushed, etc.
- }
- fd = null;
- }
- }
- }
-
- ByteArrayWindow read(final long pos, int size) throws IOException {
- synchronized (readLock) {
- if (length < pos + size)
- size = (int) (length - pos);
- final byte[] buf = new byte[size];
- fd.seek(pos);
- fd.readFully(buf, 0, size);
- return new ByteArrayWindow(this, pos, buf);
- }
- }
-
- ByteWindow mmap(final long pos, int size) throws IOException {
- synchronized (readLock) {
- if (length < pos + size)
- size = (int) (length - pos);
-
- MappedByteBuffer map;
- try {
- map = fd.getChannel().map(MapMode.READ_ONLY, pos, size);
- } catch (IOException ioe1) {
- // The most likely reason this failed is the JVM has run out
- // of virtual memory. We need to discard quickly, and try to
- // force the GC to finalize and release any existing mappings.
- //
- System.gc();
- System.runFinalization();
- map = fd.getChannel().map(MapMode.READ_ONLY, pos, size);
- }
-
- if (map.hasArray())
- return new ByteArrayWindow(this, pos, map.array());
- return new ByteBufferWindow(this, pos, map);
- }
- }
-
- private void onOpenPack() throws IOException {
- final PackIndex idx = idx();
- final byte[] buf = new byte[20];
-
- fd.seek(0);
- fd.readFully(buf, 0, 12);
- if (RawParseUtils.match(buf, 0, Constants.PACK_SIGNATURE) != 4)
- throw new IOException(JGitText.get().notAPACKFile);
- final long vers = NB.decodeUInt32(buf, 4);
- final long packCnt = NB.decodeUInt32(buf, 8);
- if (vers != 2 && vers != 3)
- throw new IOException(MessageFormat.format(JGitText.get().unsupportedPackVersion, vers));
-
- if (packCnt != idx.getObjectCount())
- throw new PackMismatchException(MessageFormat.format(
- JGitText.get().packObjectCountMismatch, packCnt, idx.getObjectCount(), getPackFile()));
-
- fd.seek(length - 20);
- fd.read(buf, 0, 20);
- if (!Arrays.equals(buf, packChecksum))
- throw new PackMismatchException(MessageFormat.format(
- JGitText.get().packObjectCountMismatch
- , ObjectId.fromRaw(buf).name()
- , ObjectId.fromRaw(idx.packChecksum).name()
- , getPackFile()));
- }
-
- private PackedObjectLoader reader(final WindowCursor curs,
- final long objOffset) throws IOException {
- int p = 0;
- final byte[] ib = curs.tempId;
- readFully(objOffset, ib, 0, 20, curs);
- int c = ib[p++] & 0xff;
- final int typeCode = (c >> 4) & 7;
- long dataSize = c & 15;
- int shift = 4;
- while ((c & 0x80) != 0) {
- c = ib[p++] & 0xff;
- dataSize += (c & 0x7f) << shift;
- shift += 7;
- }
-
- switch (typeCode) {
- case Constants.OBJ_COMMIT:
- case Constants.OBJ_TREE:
- case Constants.OBJ_BLOB:
- case Constants.OBJ_TAG:
- return new WholePackedObjectLoader(this, objOffset, p, typeCode,
- (int) dataSize);
-
- case Constants.OBJ_OFS_DELTA: {
- c = ib[p++] & 0xff;
- long ofs = c & 127;
- while ((c & 128) != 0) {
- ofs += 1;
- c = ib[p++] & 0xff;
- ofs <<= 7;
- ofs += (c & 127);
- }
- return new DeltaOfsPackedObjectLoader(this, objOffset, p,
- (int) dataSize, objOffset - ofs);
- }
- case Constants.OBJ_REF_DELTA: {
- readFully(objOffset + p, ib, 0, 20, curs);
- return new DeltaRefPackedObjectLoader(this, objOffset, p + 20,
- (int) dataSize, ObjectId.fromRaw(ib));
- }
- default:
- throw new IOException(MessageFormat.format(JGitText.get().unknownObjectType, typeCode));
- }
- }
-
- private long findEndOffset(final long startOffset)
- throws IOException, CorruptObjectException {
- final long maxOffset = length - 20;
- return getReverseIdx().findNextOffset(startOffset, maxOffset);
- }
-
- private synchronized PackReverseIndex getReverseIdx() throws IOException {
- if (reverseIdx == null)
- reverseIdx = new PackReverseIndex(idx());
- return reverseIdx;
- }
-}
diff --git a/org.eclipse.jgit/src/org/eclipse/jgit/lib/PackWriter.java b/org.eclipse.jgit/src/org/eclipse/jgit/lib/PackWriter.java
deleted file mode 100644
index 48f41a5586..0000000000
--- a/org.eclipse.jgit/src/org/eclipse/jgit/lib/PackWriter.java
+++ /dev/null
@@ -1,1040 +0,0 @@
-/*
- * Copyright (C) 2008-2010, Google Inc.
- * Copyright (C) 2008, Marek Zawirski <marek.zawirski@gmail.com>
- * and other copyright owners as documented in the project's IP log.
- *
- * This program and the accompanying materials are made available
- * under the terms of the Eclipse Distribution License v1.0 which
- * accompanies this distribution, is reproduced below, and is
- * available at http://www.eclipse.org/org/documents/edl-v10.php
- *
- * All rights reserved.
- *
- * Redistribution and use in source and binary forms, with or
- * without modification, are permitted provided that the following
- * conditions are met:
- *
- * - Redistributions of source code must retain the above copyright
- * notice, this list of conditions and the following disclaimer.
- *
- * - Redistributions in binary form must reproduce the above
- * copyright notice, this list of conditions and the following
- * disclaimer in the documentation and/or other materials provided
- * with the distribution.
- *
- * - Neither the name of the Eclipse Foundation, Inc. nor the
- * names of its contributors may be used to endorse or promote
- * products derived from this software without specific prior
- * written permission.
- *
- * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND
- * CONTRIBUTORS "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES,
- * INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
- * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
- * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR
- * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
- * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT
- * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
- * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER
- * CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT,
- * STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
- * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF
- * ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
- */
-
-package org.eclipse.jgit.lib;
-
-import java.io.IOException;
-import java.io.OutputStream;
-import java.security.MessageDigest;
-import java.util.ArrayList;
-import java.util.Collection;
-import java.util.Collections;
-import java.util.Iterator;
-import java.util.List;
-import java.util.zip.Deflater;
-
-import org.eclipse.jgit.JGitText;
-import org.eclipse.jgit.errors.IncorrectObjectTypeException;
-import org.eclipse.jgit.errors.MissingObjectException;
-import org.eclipse.jgit.revwalk.ObjectWalk;
-import org.eclipse.jgit.revwalk.RevFlag;
-import org.eclipse.jgit.revwalk.RevObject;
-import org.eclipse.jgit.revwalk.RevSort;
-import org.eclipse.jgit.transport.PackedObjectInfo;
-import org.eclipse.jgit.util.NB;
-
-/**
- * <p>
- * PackWriter class is responsible for generating pack files from specified set
- * of objects from repository. This implementation produce pack files in format
- * version 2.
- * </p>
- * <p>
- * Source of objects may be specified in two ways:
- * <ul>
- * <li>(usually) by providing sets of interesting and uninteresting objects in
- * repository - all interesting objects and their ancestors except uninteresting
- * objects and their ancestors will be included in pack, or</li>
- * <li>by providing iterator of {@link RevObject} specifying exact list and
- * order of objects in pack</li>
- * </ul>
- * Typical usage consists of creating instance intended for some pack,
- * configuring options, preparing the list of objects by calling
- * {@link #preparePack(Iterator)} or
- * {@link #preparePack(Collection, Collection)}, and finally
- * producing the stream with {@link #writePack(OutputStream)}.
- * </p>
- * <p>
- * Class provide set of configurable options and {@link ProgressMonitor}
- * support, as operations may take a long time for big repositories. Deltas
- * searching algorithm is <b>NOT IMPLEMENTED</b> yet - this implementation
- * relies only on deltas and objects reuse.
- * </p>
- * <p>
- * This class is not thread safe, it is intended to be used in one thread, with
- * one instance per created pack. Subsequent calls to writePack result in
- * undefined behavior.
- * </p>
- */
-public class PackWriter {
- /**
- * Title of {@link ProgressMonitor} task used during counting objects to
- * pack.
- *
- * @see #preparePack(Collection, Collection)
- */
- public static final String COUNTING_OBJECTS_PROGRESS = JGitText.get().countingObjects;
-
- /**
- * Title of {@link ProgressMonitor} task used during searching for objects
- * reuse or delta reuse.
- *
- * @see #writePack(OutputStream)
- */
- public static final String SEARCHING_REUSE_PROGRESS = JGitText.get().compressingObjects;
-
- /**
- * Title of {@link ProgressMonitor} task used during writing out pack
- * (objects)
- *
- * @see #writePack(OutputStream)
- */
- public static final String WRITING_OBJECTS_PROGRESS = JGitText.get().writingObjects;
-
- /**
- * Default value of deltas reuse option.
- *
- * @see #setReuseDeltas(boolean)
- */
- public static final boolean DEFAULT_REUSE_DELTAS = true;
-
- /**
- * Default value of objects reuse option.
- *
- * @see #setReuseObjects(boolean)
- */
- public static final boolean DEFAULT_REUSE_OBJECTS = true;
-
- /**
- * Default value of delta base as offset option.
- *
- * @see #setDeltaBaseAsOffset(boolean)
- */
- public static final boolean DEFAULT_DELTA_BASE_AS_OFFSET = false;
-
- /**
- * Default value of maximum delta chain depth.
- *
- * @see #setMaxDeltaDepth(int)
- */
- public static final int DEFAULT_MAX_DELTA_DEPTH = 50;
-
- private static final int PACK_VERSION_GENERATED = 2;
-
- @SuppressWarnings("unchecked")
- private final List<ObjectToPack> objectsLists[] = new List[Constants.OBJ_TAG + 1];
- {
- objectsLists[0] = Collections.<ObjectToPack> emptyList();
- objectsLists[Constants.OBJ_COMMIT] = new ArrayList<ObjectToPack>();
- objectsLists[Constants.OBJ_TREE] = new ArrayList<ObjectToPack>();
- objectsLists[Constants.OBJ_BLOB] = new ArrayList<ObjectToPack>();
- objectsLists[Constants.OBJ_TAG] = new ArrayList<ObjectToPack>();
- }
-
- private final ObjectIdSubclassMap<ObjectToPack> objectsMap = new ObjectIdSubclassMap<ObjectToPack>();
-
- // edge objects for thin packs
- private final ObjectIdSubclassMap<ObjectId> edgeObjects = new ObjectIdSubclassMap<ObjectId>();
-
- private final Repository db;
-
- private PackOutputStream out;
-
- private final Deflater deflater;
-
- private ProgressMonitor initMonitor;
-
- private ProgressMonitor writeMonitor;
-
- private final byte[] buf = new byte[16384]; // 16 KB
-
- private final WindowCursor windowCursor = new WindowCursor();
-
- private List<ObjectToPack> sortedByName;
-
- private byte packcsum[];
-
- private boolean reuseDeltas = DEFAULT_REUSE_DELTAS;
-
- private boolean reuseObjects = DEFAULT_REUSE_OBJECTS;
-
- private boolean deltaBaseAsOffset = DEFAULT_DELTA_BASE_AS_OFFSET;
-
- private int maxDeltaDepth = DEFAULT_MAX_DELTA_DEPTH;
-
- private int outputVersion;
-
- private boolean thin;
-
- private boolean ignoreMissingUninteresting = true;
-
- /**
- * Create writer for specified repository.
- * <p>
- * Objects for packing are specified in {@link #preparePack(Iterator)} or
- * {@link #preparePack(Collection, Collection)}.
- *
- * @param repo
- * repository where objects are stored.
- * @param monitor
- * operations progress monitor, used within
- * {@link #preparePack(Iterator)},
- * {@link #preparePack(Collection, Collection)}
- * , or {@link #writePack(OutputStream)}.
- */
- public PackWriter(final Repository repo, final ProgressMonitor monitor) {
- this(repo, monitor, monitor);
- }
-
- /**
- * Create writer for specified repository.
- * <p>
- * Objects for packing are specified in {@link #preparePack(Iterator)} or
- * {@link #preparePack(Collection, Collection)}.
- *
- * @param repo
- * repository where objects are stored.
- * @param imonitor
- * operations progress monitor, used within
- * {@link #preparePack(Iterator)},
- * {@link #preparePack(Collection, Collection)}
- * @param wmonitor
- * operations progress monitor, used within
- * {@link #writePack(OutputStream)}.
- */
- public PackWriter(final Repository repo, final ProgressMonitor imonitor,
- final ProgressMonitor wmonitor) {
- this.db = repo;
- initMonitor = imonitor == null ? NullProgressMonitor.INSTANCE : imonitor;
- writeMonitor = wmonitor == null ? NullProgressMonitor.INSTANCE : wmonitor;
- this.deflater = new Deflater(db.getConfig().getCore().getCompression());
- outputVersion = repo.getConfig().getCore().getPackIndexVersion();
- }
-
- /**
- * Check whether object is configured to reuse deltas existing in
- * repository.
- * <p>
- * Default setting: {@value #DEFAULT_REUSE_DELTAS}
- * </p>
- *
- * @return true if object is configured to reuse deltas; false otherwise.
- */
- public boolean isReuseDeltas() {
- return reuseDeltas;
- }
-
- /**
- * Set reuse deltas configuration option for this writer. When enabled,
- * writer will search for delta representation of object in repository and
- * use it if possible. Normally, only deltas with base to another object
- * existing in set of objects to pack will be used. Exception is however
- * thin-pack (see
- * {@link #preparePack(Collection, Collection)} and
- * {@link #preparePack(Iterator)}) where base object must exist on other
- * side machine.
- * <p>
- * When raw delta data is directly copied from a pack file, checksum is
- * computed to verify data.
- * </p>
- * <p>
- * Default setting: {@value #DEFAULT_REUSE_DELTAS}
- * </p>
- *
- * @param reuseDeltas
- * boolean indicating whether or not try to reuse deltas.
- */
- public void setReuseDeltas(boolean reuseDeltas) {
- this.reuseDeltas = reuseDeltas;
- }
-
- /**
- * Checks whether object is configured to reuse existing objects
- * representation in repository.
- * <p>
- * Default setting: {@value #DEFAULT_REUSE_OBJECTS}
- * </p>
- *
- * @return true if writer is configured to reuse objects representation from
- * pack; false otherwise.
- */
- public boolean isReuseObjects() {
- return reuseObjects;
- }
-
- /**
- * Set reuse objects configuration option for this writer. If enabled,
- * writer searches for representation in a pack file. If possible,
- * compressed data is directly copied from such a pack file. Data checksum
- * is verified.
- * <p>
- * Default setting: {@value #DEFAULT_REUSE_OBJECTS}
- * </p>
- *
- * @param reuseObjects
- * boolean indicating whether or not writer should reuse existing
- * objects representation.
- */
- public void setReuseObjects(boolean reuseObjects) {
- this.reuseObjects = reuseObjects;
- }
-
- /**
- * Check whether writer can store delta base as an offset (new style
- * reducing pack size) or should store it as an object id (legacy style,
- * compatible with old readers).
- * <p>
- * Default setting: {@value #DEFAULT_DELTA_BASE_AS_OFFSET}
- * </p>
- *
- * @return true if delta base is stored as an offset; false if it is stored
- * as an object id.
- */
- public boolean isDeltaBaseAsOffset() {
- return deltaBaseAsOffset;
- }
-
- /**
- * Set writer delta base format. Delta base can be written as an offset in a
- * pack file (new approach reducing file size) or as an object id (legacy
- * approach, compatible with old readers).
- * <p>
- * Default setting: {@value #DEFAULT_DELTA_BASE_AS_OFFSET}
- * </p>
- *
- * @param deltaBaseAsOffset
- * boolean indicating whether delta base can be stored as an
- * offset.
- */
- public void setDeltaBaseAsOffset(boolean deltaBaseAsOffset) {
- this.deltaBaseAsOffset = deltaBaseAsOffset;
- }
-
- /**
- * Get maximum depth of delta chain set up for this writer. Generated chains
- * are not longer than this value.
- * <p>
- * Default setting: {@value #DEFAULT_MAX_DELTA_DEPTH}
- * </p>
- *
- * @return maximum delta chain depth.
- */
- public int getMaxDeltaDepth() {
- return maxDeltaDepth;
- }
-
- /**
- * Set up maximum depth of delta chain for this writer. Generated chains are
- * not longer than this value. Too low value causes low compression level,
- * while too big makes unpacking (reading) longer.
- * <p>
- * Default setting: {@value #DEFAULT_MAX_DELTA_DEPTH}
- * </p>
- *
- * @param maxDeltaDepth
- * maximum delta chain depth.
- */
- public void setMaxDeltaDepth(int maxDeltaDepth) {
- this.maxDeltaDepth = maxDeltaDepth;
- }
-
- /** @return true if this writer is producing a thin pack. */
- public boolean isThin() {
- return thin;
- }
-
- /**
- * @param packthin
- * a boolean indicating whether writer may pack objects with
- * delta base object not within set of objects to pack, but
- * belonging to party repository (uninteresting/boundary) as
- * determined by set; this kind of pack is used only for
- * transport; true - to produce thin pack, false - otherwise.
- */
- public void setThin(final boolean packthin) {
- thin = packthin;
- }
-
- /**
- * @return true to ignore objects that are uninteresting and also not found
- * on local disk; false to throw a {@link MissingObjectException}
- * out of {@link #preparePack(Collection, Collection)} if an
- * uninteresting object is not in the source repository. By default,
- * true, permitting gracefully ignoring of uninteresting objects.
- */
- public boolean isIgnoreMissingUninteresting() {
- return ignoreMissingUninteresting;
- }
-
- /**
- * @param ignore
- * true if writer should ignore non existing uninteresting
- * objects during construction set of objects to pack; false
- * otherwise - non existing uninteresting objects may cause
- * {@link MissingObjectException}
- */
- public void setIgnoreMissingUninteresting(final boolean ignore) {
- ignoreMissingUninteresting = ignore;
- }
-
- /**
- * Set the pack index file format version this instance will create.
- *
- * @param version
- * the version to write. The special version 0 designates the
- * oldest (most compatible) format available for the objects.
- * @see PackIndexWriter
- */
- public void setIndexVersion(final int version) {
- outputVersion = version;
- }
-
- /**
- * Returns objects number in a pack file that was created by this writer.
- *
- * @return number of objects in pack.
- */
- public int getObjectsNumber() {
- return objectsMap.size();
- }
-
- /**
- * Prepare the list of objects to be written to the pack stream.
- * <p>
- * Iterator <b>exactly</b> determines which objects are included in a pack
- * and order they appear in pack (except that objects order by type is not
- * needed at input). This order should conform general rules of ordering
- * objects in git - by recency and path (type and delta-base first is
- * internally secured) and responsibility for guaranteeing this order is on
- * a caller side. Iterator must return each id of object to write exactly
- * once.
- * </p>
- * <p>
- * When iterator returns object that has {@link RevFlag#UNINTERESTING} flag,
- * this object won't be included in an output pack. Instead, it is recorded
- * as edge-object (known to remote repository) for thin-pack. In such a case
- * writer may pack objects with delta base object not within set of objects
- * to pack, but belonging to party repository - those marked with
- * {@link RevFlag#UNINTERESTING} flag. This type of pack is used only for
- * transport.
- * </p>
- *
- * @param objectsSource
- * iterator of object to store in a pack; order of objects within
- * each type is important, ordering by type is not needed;
- * allowed types for objects are {@link Constants#OBJ_COMMIT},
- * {@link Constants#OBJ_TREE}, {@link Constants#OBJ_BLOB} and
- * {@link Constants#OBJ_TAG}; objects returned by iterator may
- * be later reused by caller as object id and type are internally
- * copied in each iteration; if object returned by iterator has
- * {@link RevFlag#UNINTERESTING} flag set, it won't be included
- * in a pack, but is considered as edge-object for thin-pack.
- * @throws IOException
- * when some I/O problem occur during reading objects.
- */
- public void preparePack(final Iterator<RevObject> objectsSource)
- throws IOException {
- while (objectsSource.hasNext()) {
- addObject(objectsSource.next());
- }
- }
-
- /**
- * Prepare the list of objects to be written to the pack stream.
- * <p>
- * Basing on these 2 sets, another set of objects to put in a pack file is
- * created: this set consists of all objects reachable (ancestors) from
- * interesting objects, except uninteresting objects and their ancestors.
- * This method uses class {@link ObjectWalk} extensively to find out that
- * appropriate set of output objects and their optimal order in output pack.
- * Order is consistent with general git in-pack rules: sort by object type,
- * recency, path and delta-base first.
- * </p>
- *
- * @param interestingObjects
- * collection of objects to be marked as interesting (start
- * points of graph traversal).
- * @param uninterestingObjects
- * collection of objects to be marked as uninteresting (end
- * points of graph traversal).
- * @throws IOException
- * when some I/O problem occur during reading objects.
- */
- public void preparePack(
- final Collection<? extends ObjectId> interestingObjects,
- final Collection<? extends ObjectId> uninterestingObjects)
- throws IOException {
- ObjectWalk walker = setUpWalker(interestingObjects,
- uninterestingObjects);
- findObjectsToPack(walker);
- }
-
- /**
- * Determine if the pack file will contain the requested object.
- *
- * @param id
- * the object to test the existence of.
- * @return true if the object will appear in the output pack file.
- */
- public boolean willInclude(final AnyObjectId id) {
- return objectsMap.get(id) != null;
- }
-
- /**
- * Computes SHA-1 of lexicographically sorted objects ids written in this
- * pack, as used to name a pack file in repository.
- *
- * @return ObjectId representing SHA-1 name of a pack that was created.
- */
- public ObjectId computeName() {
- final MessageDigest md = Constants.newMessageDigest();
- for (ObjectToPack otp : sortByName()) {
- otp.copyRawTo(buf, 0);
- md.update(buf, 0, Constants.OBJECT_ID_LENGTH);
- }
- return ObjectId.fromRaw(md.digest());
- }
-
- /**
- * Create an index file to match the pack file just written.
- * <p>
- * This method can only be invoked after {@link #preparePack(Iterator)} or
- * {@link #preparePack(Collection, Collection)} has been
- * invoked and completed successfully. Writing a corresponding index is an
- * optional feature that not all pack users may require.
- *
- * @param indexStream
- * output for the index data. Caller is responsible for closing
- * this stream.
- * @throws IOException
- * the index data could not be written to the supplied stream.
- */
- public void writeIndex(final OutputStream indexStream) throws IOException {
- final List<ObjectToPack> list = sortByName();
- final PackIndexWriter iw;
- if (outputVersion <= 0)
- iw = PackIndexWriter.createOldestPossible(indexStream, list);
- else
- iw = PackIndexWriter.createVersion(indexStream, outputVersion);
- iw.write(list, packcsum);
- }
-
- private List<ObjectToPack> sortByName() {
- if (sortedByName == null) {
- sortedByName = new ArrayList<ObjectToPack>(objectsMap.size());
- for (List<ObjectToPack> list : objectsLists) {
- for (ObjectToPack otp : list)
- sortedByName.add(otp);
- }
- Collections.sort(sortedByName);
- }
- return sortedByName;
- }
-
- /**
- * Write the prepared pack to the supplied stream.
- * <p>
- * At first, this method collects and sorts objects to pack, then deltas
- * search is performed if set up accordingly, finally pack stream is
- * written. {@link ProgressMonitor} tasks {@value #SEARCHING_REUSE_PROGRESS}
- * (only if reuseDeltas or reuseObjects is enabled) and
- * {@value #WRITING_OBJECTS_PROGRESS} are updated during packing.
- * </p>
- * <p>
- * All reused objects data checksum (Adler32/CRC32) is computed and
- * validated against existing checksum.
- * </p>
- *
- * @param packStream
- * output stream of pack data. The stream should be buffered by
- * the caller. The caller is responsible for closing the stream.
- * @throws IOException
- * an error occurred reading a local object's data to include in
- * the pack, or writing compressed object data to the output
- * stream.
- */
- public void writePack(OutputStream packStream) throws IOException {
- if (reuseDeltas || reuseObjects)
- searchForReuse();
-
- out = new PackOutputStream(packStream);
-
- writeMonitor.beginTask(WRITING_OBJECTS_PROGRESS, getObjectsNumber());
- writeHeader();
- writeObjects();
- writeChecksum();
-
- windowCursor.release();
- writeMonitor.endTask();
- }
-
- private void searchForReuse() throws IOException {
- initMonitor.beginTask(SEARCHING_REUSE_PROGRESS, getObjectsNumber());
- final Collection<PackedObjectLoader> reuseLoaders = new ArrayList<PackedObjectLoader>();
- for (List<ObjectToPack> list : objectsLists) {
- for (ObjectToPack otp : list) {
- if (initMonitor.isCancelled())
- throw new IOException(
- JGitText.get().packingCancelledDuringObjectsWriting);
- reuseLoaders.clear();
- searchForReuse(reuseLoaders, otp);
- initMonitor.update(1);
- }
- }
-
- initMonitor.endTask();
- }
-
- private void searchForReuse(
- final Collection<PackedObjectLoader> reuseLoaders,
- final ObjectToPack otp) throws IOException {
- db.openObjectInAllPacks(otp, reuseLoaders, windowCursor);
- if (reuseDeltas) {
- selectDeltaReuseForObject(otp, reuseLoaders);
- }
- // delta reuse is preferred over object reuse
- if (reuseObjects && !otp.isCopyable()) {
- selectObjectReuseForObject(otp, reuseLoaders);
- }
- }
-
- private void selectDeltaReuseForObject(final ObjectToPack otp,
- final Collection<PackedObjectLoader> loaders) throws IOException {
- PackedObjectLoader bestLoader = null;
- ObjectId bestBase = null;
-
- for (PackedObjectLoader loader : loaders) {
- ObjectId idBase = loader.getDeltaBase();
- if (idBase == null)
- continue;
- ObjectToPack otpBase = objectsMap.get(idBase);
-
- // only if base is in set of objects to write or thin-pack's edge
- if ((otpBase != null || (thin && edgeObjects.get(idBase) != null))
- // select smallest possible delta if > 1 available
- && isBetterDeltaReuseLoader(bestLoader, loader)) {
- bestLoader = loader;
- bestBase = (otpBase != null ? otpBase : idBase);
- }
- }
-
- if (bestLoader != null) {
- otp.setCopyFromPack(bestLoader);
- otp.setDeltaBase(bestBase);
- }
- }
-
- private static boolean isBetterDeltaReuseLoader(
- PackedObjectLoader currentLoader, PackedObjectLoader loader)
- throws IOException {
- if (currentLoader == null)
- return true;
- if (loader.getRawSize() < currentLoader.getRawSize())
- return true;
- return (loader.getRawSize() == currentLoader.getRawSize()
- && loader.supportsFastCopyRawData() && !currentLoader
- .supportsFastCopyRawData());
- }
-
- private void selectObjectReuseForObject(final ObjectToPack otp,
- final Collection<PackedObjectLoader> loaders) {
- for (final PackedObjectLoader loader : loaders) {
- if (loader instanceof WholePackedObjectLoader) {
- otp.setCopyFromPack(loader);
- return;
- }
- }
- }
-
- private void writeHeader() throws IOException {
- System.arraycopy(Constants.PACK_SIGNATURE, 0, buf, 0, 4);
- NB.encodeInt32(buf, 4, PACK_VERSION_GENERATED);
- NB.encodeInt32(buf, 8, getObjectsNumber());
- out.write(buf, 0, 12);
- }
-
- private void writeObjects() throws IOException {
- for (List<ObjectToPack> list : objectsLists) {
- for (ObjectToPack otp : list) {
- if (writeMonitor.isCancelled())
- throw new IOException(
- JGitText.get().packingCancelledDuringObjectsWriting);
- if (!otp.isWritten())
- writeObject(otp);
- }
- }
- }
-
- private void writeObject(final ObjectToPack otp) throws IOException {
- otp.markWantWrite();
- if (otp.isDeltaRepresentation()) {
- ObjectToPack deltaBase = otp.getDeltaBase();
- assert deltaBase != null || thin;
- if (deltaBase != null && !deltaBase.isWritten()) {
- if (deltaBase.wantWrite()) {
- otp.clearDeltaBase(); // cycle detected
- otp.clearSourcePack();
- } else {
- writeObject(deltaBase);
- }
- }
- }
-
- assert !otp.isWritten();
-
- out.resetCRC32();
- otp.setOffset(out.length());
-
- final PackedObjectLoader reuse = open(otp);
- if (reuse != null) {
- try {
- if (otp.isDeltaRepresentation())
- writeDeltaObjectHeader(otp, reuse);
- else
- writeObjectHeader(otp.getType(), reuse.getSize());
- reuse.copyRawData(out, buf, windowCursor);
- } finally {
- reuse.endCopyRawData();
- }
- } else if (otp.isDeltaRepresentation()) {
- throw new IOException(JGitText.get().creatingDeltasIsNotImplemented);
- } else {
- writeWholeObjectDeflate(otp);
- }
- otp.setCRC(out.getCRC32());
-
- writeMonitor.update(1);
- }
-
- private PackedObjectLoader open(final ObjectToPack otp) throws IOException {
- while (otp.isCopyable()) {
- try {
- PackedObjectLoader reuse = otp.getCopyLoader(windowCursor);
- reuse.beginCopyRawData();
- return reuse;
- } catch (IOException err) {
- // The pack we found the object in originally is gone, or
- // it has been overwritten with a different layout.
- //
- otp.clearDeltaBase();
- otp.clearSourcePack();
- searchForReuse(new ArrayList<PackedObjectLoader>(), otp);
- continue;
- }
- }
- return null;
- }
-
- private void writeWholeObjectDeflate(final ObjectToPack otp)
- throws IOException {
- final ObjectLoader loader = db.openObject(windowCursor, otp);
- final byte[] data = loader.getCachedBytes();
- writeObjectHeader(otp.getType(), data.length);
- deflater.reset();
- deflater.setInput(data, 0, data.length);
- deflater.finish();
- do {
- final int n = deflater.deflate(buf, 0, buf.length);
- if (n > 0)
- out.write(buf, 0, n);
- } while (!deflater.finished());
- }
-
- private void writeDeltaObjectHeader(final ObjectToPack otp,
- final PackedObjectLoader reuse) throws IOException {
- if (deltaBaseAsOffset && otp.getDeltaBase() != null) {
- writeObjectHeader(Constants.OBJ_OFS_DELTA, reuse.getRawSize());
-
- final ObjectToPack deltaBase = otp.getDeltaBase();
- long offsetDiff = otp.getOffset() - deltaBase.getOffset();
- int pos = buf.length - 1;
- buf[pos] = (byte) (offsetDiff & 0x7F);
- while ((offsetDiff >>= 7) > 0) {
- buf[--pos] = (byte) (0x80 | (--offsetDiff & 0x7F));
- }
-
- out.write(buf, pos, buf.length - pos);
- } else {
- writeObjectHeader(Constants.OBJ_REF_DELTA, reuse.getRawSize());
- otp.getDeltaBaseId().copyRawTo(buf, 0);
- out.write(buf, 0, Constants.OBJECT_ID_LENGTH);
- }
- }
-
- private void writeObjectHeader(final int objectType, long dataLength)
- throws IOException {
- long nextLength = dataLength >>> 4;
- int size = 0;
- buf[size++] = (byte) ((nextLength > 0 ? 0x80 : 0x00)
- | (objectType << 4) | (dataLength & 0x0F));
- dataLength = nextLength;
- while (dataLength > 0) {
- nextLength >>>= 7;
- buf[size++] = (byte) ((nextLength > 0 ? 0x80 : 0x00) | (dataLength & 0x7F));
- dataLength = nextLength;
- }
- out.write(buf, 0, size);
- }
-
- private void writeChecksum() throws IOException {
- packcsum = out.getDigest();
- out.write(packcsum);
- }
-
- private ObjectWalk setUpWalker(
- final Collection<? extends ObjectId> interestingObjects,
- final Collection<? extends ObjectId> uninterestingObjects)
- throws MissingObjectException, IOException,
- IncorrectObjectTypeException {
- final ObjectWalk walker = new ObjectWalk(db);
- walker.setRetainBody(false);
- walker.sort(RevSort.TOPO);
- walker.sort(RevSort.COMMIT_TIME_DESC, true);
- if (thin)
- walker.sort(RevSort.BOUNDARY, true);
-
- for (ObjectId id : interestingObjects) {
- RevObject o = walker.parseAny(id);
- walker.markStart(o);
- }
- if (uninterestingObjects != null) {
- for (ObjectId id : uninterestingObjects) {
- final RevObject o;
- try {
- o = walker.parseAny(id);
- } catch (MissingObjectException x) {
- if (ignoreMissingUninteresting)
- continue;
- throw x;
- }
- walker.markUninteresting(o);
- }
- }
- return walker;
- }
-
- private void findObjectsToPack(final ObjectWalk walker)
- throws MissingObjectException, IncorrectObjectTypeException,
- IOException {
- initMonitor.beginTask(COUNTING_OBJECTS_PROGRESS,
- ProgressMonitor.UNKNOWN);
- RevObject o;
-
- while ((o = walker.next()) != null) {
- addObject(o);
- initMonitor.update(1);
- }
- while ((o = walker.nextObject()) != null) {
- addObject(o);
- initMonitor.update(1);
- }
- initMonitor.endTask();
- }
-
- /**
- * Include one object to the output file.
- * <p>
- * Objects are written in the order they are added. If the same object is
- * added twice, it may be written twice, creating a larger than necessary
- * file.
- *
- * @param object
- * the object to add.
- * @throws IncorrectObjectTypeException
- * the object is an unsupported type.
- */
- public void addObject(final RevObject object)
- throws IncorrectObjectTypeException {
- if (object.has(RevFlag.UNINTERESTING)) {
- edgeObjects.add(object);
- thin = true;
- return;
- }
-
- final ObjectToPack otp = new ObjectToPack(object, object.getType());
- try {
- objectsLists[object.getType()].add(otp);
- } catch (ArrayIndexOutOfBoundsException x) {
- throw new IncorrectObjectTypeException(object,
- JGitText.get().incorrectObjectType_COMMITnorTREEnorBLOBnorTAG);
- } catch (UnsupportedOperationException x) {
- // index pointing to "dummy" empty list
- throw new IncorrectObjectTypeException(object,
- JGitText.get().incorrectObjectType_COMMITnorTREEnorBLOBnorTAG);
- }
- objectsMap.add(otp);
- }
-
- /**
- * Class holding information about object that is going to be packed by
- * {@link PackWriter}. Information include object representation in a
- * pack-file and object status.
- *
- */
- static class ObjectToPack extends PackedObjectInfo {
- /** Other object being packed that this will delta against. */
- private ObjectId deltaBase;
-
- /** Pack to reuse compressed data from, otherwise null. */
- private PackFile copyFromPack;
-
- /** Offset of the object's header in {@link #copyFromPack}. */
- private long copyOffset;
-
- /**
- * Bit field, from bit 0 to bit 31:
- * <ul>
- * <li>1 bit: wantWrite</li>
- * <li>3 bits: type</li>
- * <li>28 bits: deltaDepth</li>
- * </ul>
- */
- private int flags;
-
- /**
- * Construct object for specified object id. <br/> By default object is
- * marked as not written and non-delta packed (as a whole object).
- *
- * @param src
- * object id of object for packing
- * @param type
- * real type code of the object, not its in-pack type.
- */
- ObjectToPack(AnyObjectId src, final int type) {
- super(src);
- flags |= type << 1;
- }
-
- /**
- * @return delta base object id if object is going to be packed in delta
- * representation; null otherwise - if going to be packed as a
- * whole object.
- */
- ObjectId getDeltaBaseId() {
- return deltaBase;
- }
-
- /**
- * @return delta base object to pack if object is going to be packed in
- * delta representation and delta is specified as object to
- * pack; null otherwise - if going to be packed as a whole
- * object or delta base is specified only as id.
- */
- ObjectToPack getDeltaBase() {
- if (deltaBase instanceof ObjectToPack)
- return (ObjectToPack) deltaBase;
- return null;
- }
-
- /**
- * Set delta base for the object. Delta base set by this method is used
- * by {@link PackWriter} to write object - determines its representation
- * in a created pack.
- *
- * @param deltaBase
- * delta base object or null if object should be packed as a
- * whole object.
- *
- */
- void setDeltaBase(ObjectId deltaBase) {
- this.deltaBase = deltaBase;
- }
-
- void clearDeltaBase() {
- this.deltaBase = null;
- }
-
- /**
- * @return true if object is going to be written as delta; false
- * otherwise.
- */
- boolean isDeltaRepresentation() {
- return deltaBase != null;
- }
-
- /**
- * Check if object is already written in a pack. This information is
- * used to achieve delta-base precedence in a pack file.
- *
- * @return true if object is already written; false otherwise.
- */
- boolean isWritten() {
- return getOffset() != 0;
- }
-
- boolean isCopyable() {
- return copyFromPack != null;
- }
-
- PackedObjectLoader getCopyLoader(WindowCursor curs) throws IOException {
- return copyFromPack.resolveBase(curs, copyOffset);
- }
-
- void setCopyFromPack(PackedObjectLoader loader) {
- this.copyFromPack = loader.pack;
- this.copyOffset = loader.objectOffset;
- }
-
- void clearSourcePack() {
- copyFromPack = null;
- }
-
- int getType() {
- return (flags>>1) & 0x7;
- }
-
- int getDeltaDepth() {
- return flags >>> 4;
- }
-
- void updateDeltaDepth() {
- final int d;
- if (deltaBase instanceof ObjectToPack)
- d = ((ObjectToPack) deltaBase).getDeltaDepth() + 1;
- else if (deltaBase != null)
- d = 1;
- else
- d = 0;
- flags = (d << 4) | flags & 0x15;
- }
-
- boolean wantWrite() {
- return (flags & 1) == 1;
- }
-
- void markWantWrite() {
- flags |= 1;
- }
- }
-}
diff --git a/org.eclipse.jgit/src/org/eclipse/jgit/lib/PackedObjectLoader.java b/org.eclipse.jgit/src/org/eclipse/jgit/lib/PackedObjectLoader.java
deleted file mode 100644
index 026b008f1a..0000000000
--- a/org.eclipse.jgit/src/org/eclipse/jgit/lib/PackedObjectLoader.java
+++ /dev/null
@@ -1,188 +0,0 @@
-/*
- * Copyright (C) 2009, Google Inc.
- * Copyright (C) 2008, Marek Zawirski <marek.zawirski@gmail.com>
- * Copyright (C) 2007, Robin Rosenberg <robin.rosenberg@dewire.com>
- * Copyright (C) 2006-2008, Shawn O. Pearce <spearce@spearce.org>
- * and other copyright owners as documented in the project's IP log.
- *
- * This program and the accompanying materials are made available
- * under the terms of the Eclipse Distribution License v1.0 which
- * accompanies this distribution, is reproduced below, and is
- * available at http://www.eclipse.org/org/documents/edl-v10.php
- *
- * All rights reserved.
- *
- * Redistribution and use in source and binary forms, with or
- * without modification, are permitted provided that the following
- * conditions are met:
- *
- * - Redistributions of source code must retain the above copyright
- * notice, this list of conditions and the following disclaimer.
- *
- * - Redistributions in binary form must reproduce the above
- * copyright notice, this list of conditions and the following
- * disclaimer in the documentation and/or other materials provided
- * with the distribution.
- *
- * - Neither the name of the Eclipse Foundation, Inc. nor the
- * names of its contributors may be used to endorse or promote
- * products derived from this software without specific prior
- * written permission.
- *
- * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND
- * CONTRIBUTORS "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES,
- * INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
- * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
- * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR
- * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
- * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT
- * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
- * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER
- * CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT,
- * STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
- * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF
- * ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
- */
-
-package org.eclipse.jgit.lib;
-
-import java.io.IOException;
-import java.io.OutputStream;
-
-/**
- * Base class for a set of object loader classes for packed objects.
- */
-abstract class PackedObjectLoader extends ObjectLoader {
- protected final PackFile pack;
-
- /** Position of the first byte of the object's header. */
- protected final long objectOffset;
-
- /** Bytes used to express the object header, including delta reference. */
- protected final int headerSize;
-
- protected int objectType;
-
- protected int objectSize;
-
- protected byte[] cachedBytes;
-
- PackedObjectLoader(final PackFile pr, final long objectOffset,
- final int headerSize) {
- pack = pr;
- this.objectOffset = objectOffset;
- this.headerSize = headerSize;
- }
-
- /**
- * Force this object to be loaded into memory and pinned in this loader.
- * <p>
- * Once materialized, subsequent get operations for the following methods
- * will always succeed without raising an exception, as all information is
- * pinned in memory by this loader instance.
- * <ul>
- * <li>{@link #getType()}</li>
- * <li>{@link #getSize()}</li>
- * <li>{@link #getBytes()}, {@link #getCachedBytes}</li>
- * <li>{@link #getRawSize()}</li>
- * <li>{@link #getRawType()}</li>
- * </ul>
- *
- * @param curs
- * temporary thread storage during data access.
- * @throws IOException
- * the object cannot be read.
- */
- public abstract void materialize(WindowCursor curs) throws IOException;
-
- public final int getType() {
- return objectType;
- }
-
- public final long getSize() {
- return objectSize;
- }
-
- @Override
- public final byte[] getCachedBytes() {
- return cachedBytes;
- }
-
- /**
- * @return offset of object header within pack file
- */
- public final long getObjectOffset() {
- return objectOffset;
- }
-
- /**
- * Peg the pack file open to support data copying.
- * <p>
- * Applications trying to copy raw pack data should ensure the pack stays
- * open and available throughout the entire copy. To do that use:
- *
- * <pre>
- * loader.beginCopyRawData();
- * try {
- * loader.copyRawData(out, tmpbuf, curs);
- * } finally {
- * loader.endCopyRawData();
- * }
- * </pre>
- *
- * @throws IOException
- * this loader contains stale information and cannot be used.
- * The most likely cause is the underlying pack file has been
- * deleted, and the object has moved to another pack file.
- */
- public void beginCopyRawData() throws IOException {
- pack.beginCopyRawData();
- }
-
- /**
- * Copy raw object representation from storage to provided output stream.
- * <p>
- * Copied data doesn't include object header. User must provide temporary
- * buffer used during copying by underlying I/O layer.
- * </p>
- *
- * @param out
- * output stream when data is copied. No buffering is guaranteed.
- * @param buf
- * temporary buffer used during copying. Recommended size is at
- * least few kB.
- * @param curs
- * temporary thread storage during data access.
- * @throws IOException
- * when the object cannot be read.
- * @see #beginCopyRawData()
- */
- public void copyRawData(OutputStream out, byte buf[], WindowCursor curs)
- throws IOException {
- pack.copyRawData(this, out, buf, curs);
- }
-
- /** Release resources after {@link #beginCopyRawData()}. */
- public void endCopyRawData() {
- pack.endCopyRawData();
- }
-
- /**
- * @return true if this loader is capable of fast raw-data copying basing on
- * compressed data checksum; false if raw-data copying needs
- * uncompressing and compressing data
- * @throws IOException
- * the index file format cannot be determined.
- */
- public boolean supportsFastCopyRawData() throws IOException {
- return pack.supportsFastCopyRawData();
- }
-
- /**
- * @return id of delta base object for this object representation. null if
- * object is not stored as delta.
- * @throws IOException
- * when delta base cannot read.
- */
- public abstract ObjectId getDeltaBase() throws IOException;
-}
diff --git a/org.eclipse.jgit/src/org/eclipse/jgit/lib/PersonIdent.java b/org.eclipse.jgit/src/org/eclipse/jgit/lib/PersonIdent.java
index 522f8477b1..0406684ea7 100644
--- a/org.eclipse.jgit/src/org/eclipse/jgit/lib/PersonIdent.java
+++ b/org.eclipse.jgit/src/org/eclipse/jgit/lib/PersonIdent.java
@@ -77,7 +77,7 @@ public class PersonIdent {
* @param repo
*/
public PersonIdent(final Repository repo) {
- final RepositoryConfig config = repo.getConfig();
+ final UserConfig config = repo.getConfig().get(UserConfig.KEY);
name = config.getCommitterName();
emailAddress = config.getCommitterEmail();
when = SystemReader.getInstance().getCurrentTime();
diff --git a/org.eclipse.jgit/src/org/eclipse/jgit/lib/RefUpdate.java b/org.eclipse.jgit/src/org/eclipse/jgit/lib/RefUpdate.java
index e04a587ac2..e6f8933389 100644
--- a/org.eclipse.jgit/src/org/eclipse/jgit/lib/RefUpdate.java
+++ b/org.eclipse.jgit/src/org/eclipse/jgit/lib/RefUpdate.java
@@ -440,7 +440,12 @@ public abstract class RefUpdate {
* an unexpected IO error occurred while writing changes.
*/
public Result update() throws IOException {
- return update(new RevWalk(getRepository()));
+ RevWalk rw = new RevWalk(getRepository());
+ try {
+ return update(rw);
+ } finally {
+ rw.release();
+ }
}
/**
@@ -485,7 +490,12 @@ public abstract class RefUpdate {
* @throws IOException
*/
public Result delete() throws IOException {
- return delete(new RevWalk(getRepository()));
+ RevWalk rw = new RevWalk(getRepository());
+ try {
+ return delete(rw);
+ } finally {
+ rw.release();
+ }
}
/**
diff --git a/org.eclipse.jgit/src/org/eclipse/jgit/lib/RefWriter.java b/org.eclipse.jgit/src/org/eclipse/jgit/lib/RefWriter.java
index f2738245d4..4acb3ecd89 100644
--- a/org.eclipse.jgit/src/org/eclipse/jgit/lib/RefWriter.java
+++ b/org.eclipse.jgit/src/org/eclipse/jgit/lib/RefWriter.java
@@ -51,6 +51,7 @@ import java.io.StringWriter;
import java.util.Collection;
import java.util.Map;
+import org.eclipse.jgit.storage.file.RefDirectory;
import org.eclipse.jgit.util.RefList;
import org.eclipse.jgit.util.RefMap;
diff --git a/org.eclipse.jgit/src/org/eclipse/jgit/lib/Repository.java b/org.eclipse.jgit/src/org/eclipse/jgit/lib/Repository.java
index 62e1578350..6dfbc9d2da 100644
--- a/org.eclipse.jgit/src/org/eclipse/jgit/lib/Repository.java
+++ b/org.eclipse.jgit/src/org/eclipse/jgit/lib/Repository.java
@@ -49,9 +49,6 @@ package org.eclipse.jgit.lib;
import java.io.File;
import java.io.FileNotFoundException;
import java.io.IOException;
-import java.text.MessageFormat;
-import java.util.ArrayList;
-import java.util.Collection;
import java.util.Collections;
import java.util.HashMap;
import java.util.HashSet;
@@ -59,279 +56,103 @@ import java.util.LinkedList;
import java.util.List;
import java.util.Map;
import java.util.Set;
-import java.util.Vector;
import java.util.concurrent.atomic.AtomicInteger;
import org.eclipse.jgit.JGitText;
import org.eclipse.jgit.dircache.DirCache;
-import org.eclipse.jgit.errors.ConfigInvalidException;
+import org.eclipse.jgit.errors.CorruptObjectException;
import org.eclipse.jgit.errors.IncorrectObjectTypeException;
+import org.eclipse.jgit.errors.MissingObjectException;
+import org.eclipse.jgit.errors.NoWorkTreeException;
import org.eclipse.jgit.errors.RevisionSyntaxException;
+import org.eclipse.jgit.events.ListenerList;
+import org.eclipse.jgit.events.RepositoryEvent;
+import org.eclipse.jgit.revwalk.RevBlob;
+import org.eclipse.jgit.revwalk.RevCommit;
+import org.eclipse.jgit.revwalk.RevObject;
+import org.eclipse.jgit.revwalk.RevWalk;
+import org.eclipse.jgit.storage.file.ReflogReader;
import org.eclipse.jgit.util.FS;
import org.eclipse.jgit.util.IO;
import org.eclipse.jgit.util.RawParseUtils;
-import org.eclipse.jgit.util.SystemReader;
/**
- * Represents a Git repository. A repository holds all objects and refs used for
- * managing source code (could by any type of file, but source code is what
- * SCM's are typically used for).
- *
- * In Git terms all data is stored in GIT_DIR, typically a directory called
- * .git. A work tree is maintained unless the repository is a bare repository.
- * Typically the .git directory is located at the root of the work dir.
- *
- * <ul>
- * <li>GIT_DIR
- * <ul>
- * <li>objects/ - objects</li>
- * <li>refs/ - tags and heads</li>
- * <li>config - configuration</li>
- * <li>info/ - more configurations</li>
- * </ul>
- * </li>
- * </ul>
+ * Represents a Git repository.
* <p>
- * This class is thread-safe.
+ * A repository holds all objects and refs used for managing source code (could
+ * be any type of file, but source code is what SCM's are typically used for).
* <p>
- * This implementation only handles a subtly undocumented subset of git features.
- *
+ * This class is thread-safe.
*/
-public class Repository {
+public abstract class Repository {
+ private static final ListenerList globalListeners = new ListenerList();
+
+ /** @return the global listener list observing all events in this JVM. */
+ public static ListenerList getGlobalListenerList() {
+ return globalListeners;
+ }
+
private final AtomicInteger useCnt = new AtomicInteger(1);
+ /** Metadata directory holding the repository's critical files. */
private final File gitDir;
+ /** File abstraction used to resolve paths. */
private final FS fs;
- private final FileBasedConfig userConfig;
-
- private final RepositoryConfig config;
-
- private final RefDatabase refs;
-
- private final ObjectDirectory objectDatabase;
-
private GitIndex index;
- private final List<RepositoryListener> listeners = new Vector<RepositoryListener>(); // thread safe
- static private final List<RepositoryListener> allListeners = new Vector<RepositoryListener>(); // thread safe
+ private final ListenerList myListeners = new ListenerList();
- private File workDir;
+ /** If not bare, the top level directory of the working files. */
+ private final File workTree;
- private File indexFile;
+ /** If not bare, the index file caching the working file states. */
+ private final File indexFile;
/**
- * Construct a representation of a Git repository.
- *
- * The work tree, object directory, alternate object directories and index
- * file locations are deduced from the given git directory and the default
- * rules.
+ * Initialize a new repository instance.
*
- * @param d
- * GIT_DIR (the location of the repository metadata).
- * @throws IOException
- * the repository appears to already exist but cannot be
- * accessed.
+ * @param options
+ * options to configure the repository.
*/
- public Repository(final File d) throws IOException {
- this(d, null, null, null, null); // go figure it out
+ protected Repository(final BaseRepositoryBuilder options) {
+ gitDir = options.getGitDir();
+ fs = options.getFS();
+ workTree = options.getWorkTree();
+ indexFile = options.getIndexFile();
}
- /**
- * Construct a representation of a Git repository.
- *
- * The work tree, object directory, alternate object directories and index
- * file locations are deduced from the given git directory and the default
- * rules.
- *
- * @param d
- * GIT_DIR (the location of the repository metadata). May be
- * null work workTree is set
- * @param workTree
- * GIT_WORK_TREE (the root of the checkout). May be null for
- * default value.
- * @throws IOException
- * the repository appears to already exist but cannot be
- * accessed.
- */
- public Repository(final File d, final File workTree) throws IOException {
- this(d, workTree, null, null, null); // go figure it out
+ /** @return listeners observing only events on this repository. */
+ public ListenerList getListenerList() {
+ return myListeners;
}
/**
- * Construct a representation of a Git repository using the given parameters
- * possibly overriding default conventions.
- *
- * @param d
- * GIT_DIR (the location of the repository metadata). May be null
- * for default value in which case it depends on GIT_WORK_TREE.
- * @param workTree
- * GIT_WORK_TREE (the root of the checkout). May be null for
- * default value if GIT_DIR is provided.
- * @param objectDir
- * GIT_OBJECT_DIRECTORY (where objects and are stored). May be
- * null for default value. Relative names ares resolved against
- * GIT_WORK_TREE.
- * @param alternateObjectDir
- * GIT_ALTERNATE_OBJECT_DIRECTORIES (where more objects are read
- * from). May be null for default value. Relative names ares
- * resolved against GIT_WORK_TREE.
- * @param indexFile
- * GIT_INDEX_FILE (the location of the index file). May be null
- * for default value. Relative names ares resolved against
- * GIT_WORK_TREE.
- * @throws IOException
- * the repository appears to already exist but cannot be
- * accessed.
- */
- public Repository(final File d, final File workTree, final File objectDir,
- final File[] alternateObjectDir, final File indexFile) throws IOException {
- this(d, workTree, objectDir, alternateObjectDir, indexFile, FS.DETECTED);
- }
-
- /**
- * Construct a representation of a Git repository using the given parameters
- * possibly overriding default conventions.
+ * Fire an event to all registered listeners.
+ * <p>
+ * The source repository of the event is automatically set to this
+ * repository, before the event is delivered to any listeners.
*
- * @param d
- * GIT_DIR (the location of the repository metadata). May be null
- * for default value in which case it depends on GIT_WORK_TREE.
- * @param workTree
- * GIT_WORK_TREE (the root of the checkout). May be null for
- * default value if GIT_DIR is provided.
- * @param objectDir
- * GIT_OBJECT_DIRECTORY (where objects and are stored). May be
- * null for default value. Relative names ares resolved against
- * GIT_WORK_TREE.
- * @param alternateObjectDir
- * GIT_ALTERNATE_OBJECT_DIRECTORIES (where more objects are read
- * from). May be null for default value. Relative names ares
- * resolved against GIT_WORK_TREE.
- * @param indexFile
- * GIT_INDEX_FILE (the location of the index file). May be null
- * for default value. Relative names ares resolved against
- * GIT_WORK_TREE.
- * @param fs
- * the file system abstraction which will be necessary to
- * perform certain file system operations.
- * @throws IOException
- * the repository appears to already exist but cannot be
- * accessed.
+ * @param event
+ * the event to deliver.
*/
- public Repository(final File d, final File workTree, final File objectDir,
- final File[] alternateObjectDir, final File indexFile, FS fs)
- throws IOException {
-
- if (workTree != null) {
- workDir = workTree;
- if (d == null)
- gitDir = new File(workTree, Constants.DOT_GIT);
- else
- gitDir = d;
- } else {
- if (d != null)
- gitDir = d;
- else
- throw new IllegalArgumentException(
- JGitText.get().eitherGIT_DIRorGIT_WORK_TREEmustBePassed);
- }
-
- this.fs = fs;
-
- userConfig = SystemReader.getInstance().openUserConfig(fs);
- config = new RepositoryConfig(userConfig, fs.resolve(gitDir, "config"));
-
- loadUserConfig();
- loadConfig();
-
- if (workDir == null) {
- // if the working directory was not provided explicitly,
- // we need to decide if this is a "bare" repository or not
- // first, we check the working tree configuration
- String workTreeConfig = getConfig().getString(
- ConfigConstants.CONFIG_CORE_SECTION, null,
- ConfigConstants.CONFIG_KEY_WORKTREE);
- if (workTreeConfig != null) {
- // the working tree configuration wins
- workDir = fs.resolve(d, workTreeConfig);
- } else if (getConfig().getString(
- ConfigConstants.CONFIG_CORE_SECTION, null,
- ConfigConstants.CONFIG_KEY_BARE) != null) {
- // we have asserted that a value for the "bare" flag was set
- if (!getConfig().getBoolean(ConfigConstants.CONFIG_CORE_SECTION,
- ConfigConstants.CONFIG_KEY_BARE, true))
- // the "bare" flag is false -> use the parent of the
- // meta data directory
- workDir = gitDir.getParentFile();
- else
- // the "bare" flag is true
- workDir = null;
- } else if (Constants.DOT_GIT.equals(gitDir.getName())) {
- // no value for the "bare" flag, but the meta data directory
- // is named ".git" -> use the parent of the meta data directory
- workDir = gitDir.getParentFile();
- } else {
- workDir = null;
- }
- }
-
- refs = new RefDirectory(this);
- if (objectDir != null)
- objectDatabase = new ObjectDirectory(fs.resolve(objectDir, ""),
- alternateObjectDir, fs);
- else
- objectDatabase = new ObjectDirectory(fs.resolve(gitDir, "objects"),
- alternateObjectDir, fs);
-
- if (indexFile != null)
- this.indexFile = indexFile;
- else
- this.indexFile = new File(gitDir, "index");
-
- if (objectDatabase.exists()) {
- final String repositoryFormatVersion = getConfig().getString(
- ConfigConstants.CONFIG_CORE_SECTION, null,
- ConfigConstants.CONFIG_KEY_REPO_FORMAT_VERSION);
- if (!"0".equals(repositoryFormatVersion)) {
- throw new IOException(MessageFormat.format(
- JGitText.get().unknownRepositoryFormat2,
- repositoryFormatVersion));
- }
- }
+ public void fireEvent(RepositoryEvent<?> event) {
+ event.setRepository(this);
+ myListeners.dispatch(event);
+ globalListeners.dispatch(event);
}
- private void loadUserConfig() throws IOException {
- try {
- userConfig.load();
- } catch (ConfigInvalidException e1) {
- IOException e2 = new IOException(MessageFormat.format(JGitText
- .get().userConfigFileInvalid, userConfig.getFile()
- .getAbsolutePath(), e1));
- e2.initCause(e1);
- throw e2;
- }
- }
-
- private void loadConfig() throws IOException {
- try {
- config.load();
- } catch (ConfigInvalidException e1) {
- IOException e2 = new IOException(JGitText.get().unknownRepositoryFormat);
- e2.initCause(e1);
- throw e2;
- }
- }
-
-
/**
- * Create a new Git repository initializing the necessary files and
- * directories. Repository with working tree is created using this method.
+ * Create a new Git repository.
+ * <p>
+ * Repository with working tree is created using this method. This method is
+ * the same as {@code create(false)}.
*
* @throws IOException
* @see #create(boolean)
*/
- public synchronized void create() throws IOException {
+ public void create() throws IOException {
create(false);
}
@@ -340,44 +161,14 @@ public class Repository {
* directories.
*
* @param bare
- * if true, a bare repository is created.
- *
+ * if true, a bare repository (a repository without a working
+ * directory) is created.
* @throws IOException
* in case of IO problem
*/
- public void create(boolean bare) throws IOException {
- final RepositoryConfig cfg = getConfig();
- if (cfg.getFile().exists()) {
- throw new IllegalStateException(MessageFormat.format(
- JGitText.get().repositoryAlreadyExists, gitDir));
- }
- gitDir.mkdirs();
- refs.create();
- objectDatabase.create();
-
- new File(gitDir, "branches").mkdir();
-
- RefUpdate head = updateRef(Constants.HEAD);
- head.disableRefLog();
- head.link(Constants.R_HEADS + Constants.MASTER);
-
- cfg.setInt(ConfigConstants.CONFIG_CORE_SECTION, null,
- ConfigConstants.CONFIG_KEY_REPO_FORMAT_VERSION, 0);
- cfg.setBoolean(ConfigConstants.CONFIG_CORE_SECTION, null,
- ConfigConstants.CONFIG_KEY_FILEMODE, true);
- if (bare)
- cfg.setBoolean(ConfigConstants.CONFIG_CORE_SECTION, null,
- ConfigConstants.CONFIG_KEY_BARE, true);
- cfg.setBoolean(ConfigConstants.CONFIG_CORE_SECTION, null,
- ConfigConstants.CONFIG_KEY_LOGALLREFUPDATES, !bare);
- cfg.setBoolean(ConfigConstants.CONFIG_CORE_SECTION, null,
- ConfigConstants.CONFIG_KEY_AUTOCRLF, false);
- cfg.save();
- }
+ public abstract void create(boolean bare) throws IOException;
- /**
- * @return GIT_DIR
- */
+ /** @return local metadata directory; null if repository isn't local. */
public File getDirectory() {
return gitDir;
}
@@ -385,42 +176,30 @@ public class Repository {
/**
* @return the directory containing the objects owned by this repository.
*/
- public File getObjectsDirectory() {
- return objectDatabase.getDirectory();
- }
+ public abstract File getObjectsDirectory();
/**
* @return the object database which stores this repository's data.
*/
- public ObjectDatabase getObjectDatabase() {
- return objectDatabase;
+ public abstract ObjectDatabase getObjectDatabase();
+
+ /** @return a new inserter to create objects in {@link #getObjectDatabase()} */
+ public ObjectInserter newObjectInserter() {
+ return getObjectDatabase().newInserter();
}
- /** @return the reference database which stores the reference namespace. */
- public RefDatabase getRefDatabase() {
- return refs;
+ /** @return a new inserter to create objects in {@link #getObjectDatabase()} */
+ public ObjectReader newObjectReader() {
+ return getObjectDatabase().newReader();
}
+ /** @return the reference database which stores the reference namespace. */
+ public abstract RefDatabase getRefDatabase();
+
/**
* @return the configuration of this repository
*/
- public RepositoryConfig getConfig() {
- if (userConfig.isOutdated()) {
- try {
- loadUserConfig();
- } catch (IOException e) {
- throw new RuntimeException(e);
- }
- }
- if (config.isOutdated()) {
- try {
- loadConfig();
- } catch (IOException e) {
- throw new RuntimeException(e);
- }
- }
- return config;
- }
+ public abstract Config getConfig();
/**
* @return the used file system abstraction
@@ -430,116 +209,63 @@ public class Repository {
}
/**
- * Construct a filename where the loose object having a specified SHA-1
- * should be stored. If the object is stored in a shared repository the path
- * to the alternative repo will be returned. If the object is not yet store
- * a usable path in this repo will be returned. It is assumed that callers
- * will look for objects in a pack first.
- *
- * @param objectId
- * @return suggested file name
- */
- public File toFile(final AnyObjectId objectId) {
- return objectDatabase.fileFor(objectId);
- }
-
- /**
* @param objectId
* @return true if the specified object is stored in this repo or any of the
* known shared repositories.
*/
- public boolean hasObject(final AnyObjectId objectId) {
- return objectDatabase.hasObject(objectId);
- }
-
- /**
- * @param id
- * SHA-1 of an object.
- *
- * @return a {@link ObjectLoader} for accessing the data of the named
- * object, or null if the object does not exist.
- * @throws IOException
- */
- public ObjectLoader openObject(final AnyObjectId id)
- throws IOException {
- final WindowCursor wc = new WindowCursor();
+ public boolean hasObject(AnyObjectId objectId) {
try {
- return openObject(wc, id);
- } finally {
- wc.release();
+ return getObjectDatabase().has(objectId);
+ } catch (IOException e) {
+ // Legacy API, assume error means "no"
+ return false;
}
}
/**
- * @param curs
- * temporary working space associated with the calling thread.
- * @param id
- * SHA-1 of an object.
- *
- * @return a {@link ObjectLoader} for accessing the data of the named
- * object, or null if the object does not exist.
- * @throws IOException
- */
- public ObjectLoader openObject(final WindowCursor curs, final AnyObjectId id)
- throws IOException {
- return objectDatabase.openObject(curs, id);
- }
-
- /**
- * Open object in all packs containing specified object.
+ * Open an object from this repository.
+ * <p>
+ * This is a one-shot call interface which may be faster than allocating a
+ * {@link #newObjectReader()} to perform the lookup.
*
* @param objectId
- * id of object to search for
- * @param curs
- * temporary working space associated with the calling thread.
- * @return collection of loaders for this object, from all packs containing
- * this object
+ * identity of the object to open.
+ * @return a {@link ObjectLoader} for accessing the object.
+ * @throws MissingObjectException
+ * the object does not exist.
* @throws IOException
+ * the object store cannot be accessed.
*/
- public Collection<PackedObjectLoader> openObjectInAllPacks(
- final AnyObjectId objectId, final WindowCursor curs)
- throws IOException {
- Collection<PackedObjectLoader> result = new LinkedList<PackedObjectLoader>();
- openObjectInAllPacks(objectId, result, curs);
- return result;
+ public ObjectLoader open(final AnyObjectId objectId)
+ throws MissingObjectException, IOException {
+ return getObjectDatabase().open(objectId);
}
/**
- * Open object in all packs containing specified object.
+ * Open an object from this repository.
+ * <p>
+ * This is a one-shot call interface which may be faster than allocating a
+ * {@link #newObjectReader()} to perform the lookup.
*
* @param objectId
- * id of object to search for
- * @param resultLoaders
- * result collection of loaders for this object, filled with
- * loaders from all packs containing specified object
- * @param curs
- * temporary working space associated with the calling thread.
- * @throws IOException
- */
- void openObjectInAllPacks(final AnyObjectId objectId,
- final Collection<PackedObjectLoader> resultLoaders,
- final WindowCursor curs) throws IOException {
- objectDatabase.openObjectInAllPacks(resultLoaders, curs, objectId);
- }
-
- /**
- * @param id
- * SHA'1 of a blob
- * @return an {@link ObjectLoader} for accessing the data of a named blob
+ * identity of the object to open.
+ * @param typeHint
+ * hint about the type of object being requested;
+ * {@link ObjectReader#OBJ_ANY} if the object type is not known,
+ * or does not matter to the caller.
+ * @return a {@link ObjectLoader} for accessing the object.
+ * @throws MissingObjectException
+ * the object does not exist.
+ * @throws IncorrectObjectTypeException
+ * typeHint was not OBJ_ANY, and the object's actual type does
+ * not match typeHint.
* @throws IOException
+ * the object store cannot be accessed.
*/
- public ObjectLoader openBlob(final ObjectId id) throws IOException {
- return openObject(id);
- }
-
- /**
- * @param id
- * SHA'1 of a tree
- * @return an {@link ObjectLoader} for accessing the data of a named tree
- * @throws IOException
- */
- public ObjectLoader openTree(final ObjectId id) throws IOException {
- return openObject(id);
+ public ObjectLoader open(AnyObjectId objectId, int typeHint)
+ throws MissingObjectException, IncorrectObjectTypeException,
+ IOException {
+ return getObjectDatabase().open(objectId, typeHint);
}
/**
@@ -572,23 +298,26 @@ public class Repository {
* @deprecated Use {@link org.eclipse.jgit.revwalk.RevWalk#parseCommit(AnyObjectId)},
* or {@link org.eclipse.jgit.revwalk.RevWalk#parseTag(AnyObjectId)}.
* To read a tree, use {@link org.eclipse.jgit.treewalk.TreeWalk#addTree(AnyObjectId)}.
- * To read a blob, open it with {@link #openObject(AnyObjectId)}.
+ * To read a blob, open it with {@link #open(AnyObjectId)}.
*/
@Deprecated
public Object mapObject(final ObjectId id, final String refName) throws IOException {
- final ObjectLoader or = openObject(id);
- if (or == null)
+ final ObjectLoader or;
+ try {
+ or = open(id);
+ } catch (MissingObjectException notFound) {
return null;
- final byte[] raw = or.getBytes();
+ }
+ final byte[] raw = or.getCachedBytes();
switch (or.getType()) {
case Constants.OBJ_TREE:
- return makeTree(id, raw);
+ return new Tree(this, id, raw);
case Constants.OBJ_COMMIT:
- return makeCommit(id, raw);
+ return new Commit(this, id, raw);
case Constants.OBJ_TAG:
- return makeTag(id, refName, raw);
+ return new Tag(this, id, refName, raw);
case Constants.OBJ_BLOB:
return raw;
@@ -608,18 +337,13 @@ public class Repository {
*/
@Deprecated
public Commit mapCommit(final ObjectId id) throws IOException {
- final ObjectLoader or = openObject(id);
- if (or == null)
+ final ObjectLoader or;
+ try {
+ or = open(id, Constants.OBJ_COMMIT);
+ } catch (MissingObjectException notFound) {
return null;
- final byte[] raw = or.getBytes();
- if (Constants.OBJ_COMMIT == or.getType())
- return new Commit(this, id, raw);
- throw new IncorrectObjectTypeException(id, Constants.TYPE_COMMIT);
- }
-
- private Commit makeCommit(final ObjectId id, final byte[] raw) {
- Commit ret = new Commit(this, id, raw);
- return ret;
+ }
+ return new Commit(this, id, or.getCachedBytes());
}
/**
@@ -650,10 +374,13 @@ public class Repository {
*/
@Deprecated
public Tree mapTree(final ObjectId id) throws IOException {
- final ObjectLoader or = openObject(id);
- if (or == null)
+ final ObjectLoader or;
+ try {
+ or = open(id);
+ } catch (MissingObjectException notFound) {
return null;
- final byte[] raw = or.getBytes();
+ }
+ final byte[] raw = or.getCachedBytes();
switch (or.getType()) {
case Constants.OBJ_TREE:
return new Tree(this, id, raw);
@@ -666,16 +393,6 @@ public class Repository {
}
}
- private Tree makeTree(final ObjectId id, final byte[] raw) throws IOException {
- Tree ret = new Tree(this, id, raw);
- return ret;
- }
-
- private Tag makeTag(final ObjectId id, final String refName, final byte[] raw) {
- Tag ret = new Tag(this, id, refName, raw);
- return ret;
- }
-
/**
* Access a tag by symbolic name.
*
@@ -701,12 +418,14 @@ public class Repository {
*/
@Deprecated
public Tag mapTag(final String refName, final ObjectId id) throws IOException {
- final ObjectLoader or = openObject(id);
- if (or == null)
+ final ObjectLoader or;
+ try {
+ or = open(id);
+ } catch (MissingObjectException notFound) {
return null;
- final byte[] raw = or.getBytes();
- if (Constants.OBJ_TAG == or.getType())
- return new Tag(this, id, refName, raw);
+ }
+ if (or.getType() == Constants.OBJ_TAG)
+ return new Tag(this, id, refName, or.getCachedBytes());
return new Tag(this, id, refName, null);
}
@@ -741,7 +460,7 @@ public class Repository {
* to the base ref, as the symbolic ref could not be read.
*/
public RefUpdate updateRef(final String ref, final boolean detach) throws IOException {
- return refs.newUpdate(ref, detach);
+ return getRefDatabase().newUpdate(ref, detach);
}
/**
@@ -757,7 +476,7 @@ public class Repository {
*
*/
public RefRename renameRef(final String fromRef, final String toRef) throws IOException {
- return refs.newRename(fromRef, toRef);
+ return getRefDatabase().newRename(fromRef, toRef);
}
/**
@@ -765,36 +484,45 @@ public class Repository {
*
* Currently supported is combinations of these.
* <ul>
- * <li>SHA-1 - a SHA-1</li>
- * <li>refs/... - a ref name</li>
- * <li>ref^n - nth parent reference</li>
- * <li>ref~n - distance via parent reference</li>
- * <li>ref@{n} - nth version of ref</li>
- * <li>ref^{tree} - tree references by ref</li>
- * <li>ref^{commit} - commit references by ref</li>
+ * <li>SHA-1 - a SHA-1</li>
+ * <li>refs/... - a ref name</li>
+ * <li>ref^n - nth parent reference</li>
+ * <li>ref~n - distance via parent reference</li>
+ * <li>ref@{n} - nth version of ref</li>
+ * <li>ref^{tree} - tree references by ref</li>
+ * <li>ref^{commit} - commit references by ref</li>
* </ul>
*
- * Not supported is
+ * Not supported is:
* <ul>
* <li>timestamps in reflogs, ref@{full or relative timestamp}</li>
* <li>abbreviated SHA-1's</li>
* </ul>
*
- * @param revstr A git object references expression
+ * @param revstr
+ * A git object references expression
* @return an ObjectId or null if revstr can't be resolved to any ObjectId
- * @throws IOException on serious errors
+ * @throws IOException
+ * on serious errors
*/
public ObjectId resolve(final String revstr) throws IOException {
+ RevWalk rw = new RevWalk(this);
+ try {
+ return resolve(rw, revstr);
+ } finally {
+ rw.release();
+ }
+ }
+
+ private ObjectId resolve(final RevWalk rw, final String revstr) throws IOException {
char[] rev = revstr.toCharArray();
- Object ref = null;
- ObjectId refId = null;
+ RevObject ref = null;
for (int i = 0; i < rev.length; ++i) {
switch (rev[i]) {
case '^':
- if (refId == null) {
- String refstr = new String(rev,0,i);
- refId = resolveSimple(refstr);
- if (refId == null)
+ if (ref == null) {
+ ref = parseSimple(rw, new String(rev, 0, i));
+ if (ref == null)
return null;
}
if (i + 1 < rev.length) {
@@ -810,19 +538,12 @@ public class Repository {
case '8':
case '9':
int j;
- ref = mapObject(refId, null);
- while (ref instanceof Tag) {
- Tag tag = (Tag)ref;
- refId = tag.getObjId();
- ref = mapObject(refId, null);
- }
- if (!(ref instanceof Commit))
- throw new IncorrectObjectTypeException(refId, Constants.TYPE_COMMIT);
- for (j=i+1; j<rev.length; ++j) {
+ ref = rw.parseCommit(ref);
+ for (j = i + 1; j < rev.length; ++j) {
if (!Character.isDigit(rev[j]))
break;
}
- String parentnum = new String(rev, i+1, j-i-1);
+ String parentnum = new String(rev, i + 1, j - i - 1);
int pnum;
try {
pnum = Integer.parseInt(parentnum);
@@ -832,123 +553,83 @@ public class Repository {
revstr);
}
if (pnum != 0) {
- final ObjectId parents[] = ((Commit) ref)
- .getParentIds();
- if (pnum > parents.length)
- refId = null;
+ RevCommit commit = (RevCommit) ref;
+ if (pnum > commit.getParentCount())
+ ref = null;
else
- refId = parents[pnum - 1];
+ ref = commit.getParent(pnum - 1);
}
i = j - 1;
break;
case '{':
int k;
String item = null;
- for (k=i+2; k<rev.length; ++k) {
+ for (k = i + 2; k < rev.length; ++k) {
if (rev[k] == '}') {
- item = new String(rev, i+2, k-i-2);
+ item = new String(rev, i + 2, k - i - 2);
break;
}
}
i = k;
if (item != null)
if (item.equals("tree")) {
- ref = mapObject(refId, null);
- while (ref instanceof Tag) {
- Tag t = (Tag)ref;
- refId = t.getObjId();
- ref = mapObject(refId, null);
- }
- if (ref instanceof Treeish)
- refId = ((Treeish)ref).getTreeId();
- else
- throw new IncorrectObjectTypeException(refId, Constants.TYPE_TREE);
- }
- else if (item.equals("commit")) {
- ref = mapObject(refId, null);
- while (ref instanceof Tag) {
- Tag t = (Tag)ref;
- refId = t.getObjId();
- ref = mapObject(refId, null);
- }
- if (!(ref instanceof Commit))
- throw new IncorrectObjectTypeException(refId, Constants.TYPE_COMMIT);
- }
- else if (item.equals("blob")) {
- ref = mapObject(refId, null);
- while (ref instanceof Tag) {
- Tag t = (Tag)ref;
- refId = t.getObjId();
- ref = mapObject(refId, null);
- }
- if (!(ref instanceof byte[]))
- throw new IncorrectObjectTypeException(refId, Constants.TYPE_BLOB);
- }
- else if (item.equals("")) {
- ref = mapObject(refId, null);
- while (ref instanceof Tag) {
- Tag t = (Tag)ref;
- refId = t.getObjId();
- ref = mapObject(refId, null);
- }
- }
- else
+ ref = rw.parseTree(ref);
+ } else if (item.equals("commit")) {
+ ref = rw.parseCommit(ref);
+ } else if (item.equals("blob")) {
+ ref = rw.peel(ref);
+ if (!(ref instanceof RevBlob))
+ throw new IncorrectObjectTypeException(ref,
+ Constants.TYPE_BLOB);
+ } else if (item.equals("")) {
+ ref = rw.peel(ref);
+ } else
throw new RevisionSyntaxException(revstr);
else
throw new RevisionSyntaxException(revstr);
break;
default:
- ref = mapObject(refId, null);
- if (ref instanceof Commit) {
- final ObjectId parents[] = ((Commit) ref)
- .getParentIds();
- if (parents.length == 0)
- refId = null;
+ ref = rw.parseAny(ref);
+ if (ref instanceof RevCommit) {
+ RevCommit commit = ((RevCommit) ref);
+ if (commit.getParentCount() == 0)
+ ref = null;
else
- refId = parents[0];
+ ref = commit.getParent(0);
} else
- throw new IncorrectObjectTypeException(refId, Constants.TYPE_COMMIT);
+ throw new IncorrectObjectTypeException(ref,
+ Constants.TYPE_COMMIT);
}
} else {
- ref = mapObject(refId, null);
- while (ref instanceof Tag) {
- Tag tag = (Tag)ref;
- refId = tag.getObjId();
- ref = mapObject(refId, null);
- }
- if (ref instanceof Commit) {
- final ObjectId parents[] = ((Commit) ref)
- .getParentIds();
- if (parents.length == 0)
- refId = null;
+ ref = rw.peel(ref);
+ if (ref instanceof RevCommit) {
+ RevCommit commit = ((RevCommit) ref);
+ if (commit.getParentCount() == 0)
+ ref = null;
else
- refId = parents[0];
+ ref = commit.getParent(0);
} else
- throw new IncorrectObjectTypeException(refId, Constants.TYPE_COMMIT);
+ throw new IncorrectObjectTypeException(ref,
+ Constants.TYPE_COMMIT);
}
break;
case '~':
if (ref == null) {
- String refstr = new String(rev,0,i);
- refId = resolveSimple(refstr);
- if (refId == null)
+ ref = parseSimple(rw, new String(rev, 0, i));
+ if (ref == null)
return null;
- ref = mapObject(refId, null);
}
- while (ref instanceof Tag) {
- Tag tag = (Tag)ref;
- refId = tag.getObjId();
- ref = mapObject(refId, null);
- }
- if (!(ref instanceof Commit))
- throw new IncorrectObjectTypeException(refId, Constants.TYPE_COMMIT);
+ ref = rw.peel(ref);
+ if (!(ref instanceof RevCommit))
+ throw new IncorrectObjectTypeException(ref,
+ Constants.TYPE_COMMIT);
int l;
for (l = i + 1; l < rev.length; ++l) {
if (!Character.isDigit(rev[l]))
break;
}
- String distnum = new String(rev, i+1, l-i-1);
+ String distnum = new String(rev, i + 1, l - i - 1);
int dist;
try {
dist = Integer.parseInt(distnum);
@@ -957,13 +638,14 @@ public class Repository {
JGitText.get().invalidAncestryLength, revstr);
}
while (dist > 0) {
- final ObjectId[] parents = ((Commit) ref).getParentIds();
- if (parents.length == 0) {
- refId = null;
+ RevCommit commit = (RevCommit) ref;
+ if (commit.getParentCount() == 0) {
+ ref = null;
break;
}
- refId = parents[0];
- ref = mapCommit(refId);
+ commit = commit.getParent(0);
+ rw.parseHeaders(commit);
+ ref = commit;
--dist;
}
i = l - 1;
@@ -971,30 +653,35 @@ public class Repository {
case '@':
int m;
String time = null;
- for (m=i+2; m<rev.length; ++m) {
+ for (m = i + 2; m < rev.length; ++m) {
if (rev[m] == '}') {
- time = new String(rev, i+2, m-i-2);
+ time = new String(rev, i + 2, m - i - 2);
break;
}
}
if (time != null)
- throw new RevisionSyntaxException(JGitText.get().reflogsNotYetSupportedByRevisionParser, revstr);
+ throw new RevisionSyntaxException(
+ JGitText.get().reflogsNotYetSupportedByRevisionParser,
+ revstr);
i = m - 1;
break;
default:
- if (refId != null)
+ if (ref != null)
throw new RevisionSyntaxException(revstr);
}
}
- if (refId == null)
- refId = resolveSimple(revstr);
- return refId;
+ return ref != null ? ref.copy() : resolveSimple(revstr);
+ }
+
+ private RevObject parseSimple(RevWalk rw, String revstr) throws IOException {
+ ObjectId id = resolveSimple(revstr);
+ return id != null ? rw.parseAny(id) : null;
}
private ObjectId resolveSimple(final String revstr) throws IOException {
if (ObjectId.isId(revstr))
return ObjectId.fromString(revstr);
- final Ref r = refs.getRef(revstr);
+ final Ref r = getRefDatabase().getRef(revstr);
return r != null ? r.getObjectId() : null;
}
@@ -1003,17 +690,24 @@ public class Repository {
useCnt.incrementAndGet();
}
- /**
- * Close all resources used by this repository
- */
+ /** Decrement the use count, and maybe close resources. */
public void close() {
if (useCnt.decrementAndGet() == 0) {
- objectDatabase.close();
- refs.close();
+ doClose();
}
}
/**
+ * Invoked when the use count drops to zero during {@link #close()}.
+ * <p>
+ * The default implementation closes the object and ref databases.
+ */
+ protected void doClose() {
+ getObjectDatabase().close();
+ getRefDatabase().close();
+ }
+
+ /**
* Add a single existing pack to the list of available pack files.
*
* @param pack
@@ -1024,12 +718,16 @@ public class Repository {
* index file could not be opened, read, or is not recognized as
* a Git pack file index.
*/
- public void openPack(final File pack, final File idx) throws IOException {
- objectDatabase.openPack(pack, idx);
- }
+ public abstract void openPack(File pack, File idx) throws IOException;
public String toString() {
- return "Repository[" + getDirectory() + "]";
+ String desc;
+ if (getDirectory() != null)
+ desc = getDirectory().getPath();
+ else
+ desc = getClass().getSimpleName() + "-"
+ + System.identityHashCode(this);
+ return "Repository[" + desc + "]";
}
/**
@@ -1078,6 +776,20 @@ public class Repository {
}
/**
+ * Objects known to exist but not expressed by {@link #getAllRefs()}.
+ * <p>
+ * When a repository borrows objects from another repository, it can
+ * advertise that it safely has that other repository's references, without
+ * exposing any other details about the other repository. This may help
+ * a client trying to push changes avoid pushing more than it needs to.
+ *
+ * @return unmodifiable collection of other known objects.
+ */
+ public Set<ObjectId> getAdditionalHaves() {
+ return Collections.emptySet();
+ }
+
+ /**
* Get a ref by name.
*
* @param name
@@ -1088,7 +800,7 @@ public class Repository {
* @throws IOException
*/
public Ref getRef(final String name) throws IOException {
- return refs.getRef(name);
+ return getRefDatabase().getRef(name);
}
/**
@@ -1096,7 +808,7 @@ public class Repository {
*/
public Map<String, Ref> getAllRefs() {
try {
- return refs.getRefs(RefDatabase.ALL);
+ return getRefDatabase().getRefs(RefDatabase.ALL);
} catch (IOException e) {
return new HashMap<String, Ref>();
}
@@ -1109,7 +821,7 @@ public class Repository {
*/
public Map<String, Ref> getTags() {
try {
- return refs.getRefs(Constants.R_TAGS);
+ return getRefDatabase().getRefs(Constants.R_TAGS);
} catch (IOException e) {
return new HashMap<String, Ref>();
}
@@ -1130,7 +842,7 @@ public class Repository {
*/
public Ref peel(final Ref ref) {
try {
- return refs.peel(ref);
+ return getRefDatabase().peel(ref);
} catch (IOException e) {
// Historical accident; if the reference cannot be peeled due
// to some sort of repository access problem we claim that the
@@ -1170,13 +882,13 @@ public class Repository {
* {@link Repository}
* @throws IOException
* if the index can not be read
- * @throws IllegalStateException
- * if this is bare (see {@link #isBare()})
+ * @throws NoWorkTreeException
+ * if this is bare, which implies it has no working directory.
+ * See {@link #isBare()}.
*/
- public GitIndex getIndex() throws IOException, IllegalStateException {
+ public GitIndex getIndex() throws IOException, NoWorkTreeException {
if (isBare())
- throw new IllegalStateException(
- JGitText.get().bareRepositoryNoWorkdirAndIndex);
+ throw new NoWorkTreeException();
if (index == null) {
index = new GitIndex(this);
index.read();
@@ -1188,16 +900,63 @@ public class Repository {
/**
* @return the index file location
- * @throws IllegalStateException
- * if this is bare (see {@link #isBare()})
+ * @throws NoWorkTreeException
+ * if this is bare, which implies it has no working directory.
+ * See {@link #isBare()}.
*/
- public File getIndexFile() throws IllegalStateException {
+ public File getIndexFile() throws NoWorkTreeException {
if (isBare())
- throw new IllegalStateException(
- JGitText.get().bareRepositoryNoWorkdirAndIndex);
+ throw new NoWorkTreeException();
return indexFile;
}
+ /**
+ * Create a new in-core index representation and read an index from disk.
+ * <p>
+ * The new index will be read before it is returned to the caller. Read
+ * failures are reported as exceptions and therefore prevent the method from
+ * returning a partially populated index.
+ *
+ * @return a cache representing the contents of the specified index file (if
+ * it exists) or an empty cache if the file does not exist.
+ * @throws NoWorkTreeException
+ * if this is bare, which implies it has no working directory.
+ * See {@link #isBare()}.
+ * @throws IOException
+ * the index file is present but could not be read.
+ * @throws CorruptObjectException
+ * the index file is using a format or extension that this
+ * library does not support.
+ */
+ public DirCache readDirCache() throws NoWorkTreeException,
+ CorruptObjectException, IOException {
+ return DirCache.read(getIndexFile());
+ }
+
+ /**
+ * Create a new in-core index representation, lock it, and read from disk.
+ * <p>
+ * The new index will be locked and then read before it is returned to the
+ * caller. Read failures are reported as exceptions and therefore prevent
+ * the method from returning a partially populated index.
+ *
+ * @return a cache representing the contents of the specified index file (if
+ * it exists) or an empty cache if the file does not exist.
+ * @throws NoWorkTreeException
+ * if this is bare, which implies it has no working directory.
+ * See {@link #isBare()}.
+ * @throws IOException
+ * the index file is present but could not be read, or the lock
+ * could not be obtained.
+ * @throws CorruptObjectException
+ * the index file is using a format or extension that this
+ * library does not support.
+ */
+ public DirCache lockDirCache() throws NoWorkTreeException,
+ CorruptObjectException, IOException {
+ return DirCache.lock(getIndexFile());
+ }
+
static byte[] gitInternalSlash(byte[] bytes) {
if (File.separatorChar == '/')
return bytes;
@@ -1211,10 +970,13 @@ public class Repository {
* @return an important state
*/
public RepositoryState getRepositoryState() {
+ if (isBare() || getDirectory() == null)
+ return RepositoryState.BARE;
+
// Pre Git-1.6 logic
- if (new File(getWorkDir(), ".dotest").exists())
+ if (new File(getWorkTree(), ".dotest").exists())
return RepositoryState.REBASING;
- if (new File(gitDir,".dotest-merge").exists())
+ if (new File(getDirectory(), ".dotest-merge").exists())
return RepositoryState.REBASING_INTERACTIVE;
// From 1.6 onwards
@@ -1231,10 +993,10 @@ public class Repository {
return RepositoryState.REBASING_MERGE;
// Both versions
- if (new File(gitDir, "MERGE_HEAD").exists()) {
+ if (new File(getDirectory(), "MERGE_HEAD").exists()) {
// we are merging - now check whether we have unmerged paths
try {
- if (!DirCache.read(this).hasUnmergedPaths()) {
+ if (!readDirCache().hasUnmergedPaths()) {
// no unmerged paths -> return the MERGING_RESOLVED state
return RepositoryState.MERGING_RESOLVED;
}
@@ -1247,7 +1009,7 @@ public class Repository {
return RepositoryState.MERGING;
}
- if (new File(gitDir,"BISECT_LOG").exists())
+ if (new File(getDirectory(), "BISECT_LOG").exists())
return RepositoryState.BISECTING;
return RepositoryState.SAFE;
@@ -1334,96 +1096,23 @@ public class Repository {
}
/**
- * @return the "bare"-ness of this Repository
+ * @return true if this is bare, which implies it has no working directory.
*/
public boolean isBare() {
- return workDir == null;
+ return workTree == null;
}
/**
- * @return the workdir file, i.e. where the files are checked out
- * @throws IllegalStateException
- * if the repository is "bare"
+ * @return the root directory of the working tree, where files are checked
+ * out for viewing and editing.
+ * @throws NoWorkTreeException
+ * if this is bare, which implies it has no working directory.
+ * See {@link #isBare()}.
*/
- public File getWorkDir() throws IllegalStateException {
+ public File getWorkTree() throws NoWorkTreeException {
if (isBare())
- throw new IllegalStateException(
- JGitText.get().bareRepositoryNoWorkdirAndIndex);
- return workDir;
- }
-
- /**
- * Override default workdir
- *
- * @param workTree
- * the work tree directory
- */
- public void setWorkDir(File workTree) {
- this.workDir = workTree;
- }
-
- /**
- * Register a {@link RepositoryListener} which will be notified
- * when ref changes are detected.
- *
- * @param l
- */
- public void addRepositoryChangedListener(final RepositoryListener l) {
- listeners.add(l);
- }
-
- /**
- * Remove a registered {@link RepositoryListener}
- * @param l
- */
- public void removeRepositoryChangedListener(final RepositoryListener l) {
- listeners.remove(l);
- }
-
- /**
- * Register a global {@link RepositoryListener} which will be notified
- * when a ref changes in any repository are detected.
- *
- * @param l
- */
- public static void addAnyRepositoryChangedListener(final RepositoryListener l) {
- allListeners.add(l);
- }
-
- /**
- * Remove a globally registered {@link RepositoryListener}
- * @param l
- */
- public static void removeAnyRepositoryChangedListener(final RepositoryListener l) {
- allListeners.remove(l);
- }
-
- void fireRefsChanged() {
- final RefsChangedEvent event = new RefsChangedEvent(this);
- List<RepositoryListener> all;
- synchronized (listeners) {
- all = new ArrayList<RepositoryListener>(listeners);
- }
- synchronized (allListeners) {
- all.addAll(allListeners);
- }
- for (final RepositoryListener l : all) {
- l.refsChanged(event);
- }
- }
-
- void fireIndexChanged() {
- final IndexChangedEvent event = new IndexChangedEvent(this);
- List<RepositoryListener> all;
- synchronized (listeners) {
- all = new ArrayList<RepositoryListener>(listeners);
- }
- synchronized (allListeners) {
- all.addAll(allListeners);
- }
- for (final RepositoryListener l : all) {
- l.indexChanged(event);
- }
+ throw new NoWorkTreeException();
+ return workTree;
}
/**
@@ -1431,11 +1120,7 @@ public class Repository {
*
* @throws IOException
*/
- public void scanForRepoChanges() throws IOException {
- getAllRefs(); // This will look for changes to refs
- if (!isBare())
- getIndex(); // This will detect changes in the index
- }
+ public abstract void scanForRepoChanges() throws IOException;
/**
* @param refName
@@ -1458,12 +1143,8 @@ public class Repository {
* named ref does not exist.
* @throws IOException the ref could not be accessed.
*/
- public ReflogReader getReflogReader(String refName) throws IOException {
- Ref ref = getRef(refName);
- if (ref != null)
- return new ReflogReader(this, ref.getName());
- return null;
- }
+ public abstract ReflogReader getReflogReader(String refName)
+ throws IOException;
/**
* Return the information stored in the file $GIT_DIR/MERGE_MSG. In this
@@ -1473,9 +1154,15 @@ public class Repository {
* @return a String containing the content of the MERGE_MSG file or
* {@code null} if this file doesn't exist
* @throws IOException
+ * @throws NoWorkTreeException
+ * if this is bare, which implies it has no working directory.
+ * See {@link #isBare()}.
*/
- public String readMergeCommitMsg() throws IOException {
- File mergeMsgFile = new File(gitDir, Constants.MERGE_MSG);
+ public String readMergeCommitMsg() throws IOException, NoWorkTreeException {
+ if (isBare() || getDirectory() == null)
+ throw new NoWorkTreeException();
+
+ File mergeMsgFile = new File(getDirectory(), Constants.MERGE_MSG);
try {
return new String(IO.readFully(mergeMsgFile));
} catch (FileNotFoundException e) {
@@ -1494,9 +1181,15 @@ public class Repository {
* file or {@code null} if this file doesn't exist. Also if the file
* exists but is empty {@code null} will be returned
* @throws IOException
+ * @throws NoWorkTreeException
+ * if this is bare, which implies it has no working directory.
+ * See {@link #isBare()}.
*/
- public List<ObjectId> readMergeHeads() throws IOException {
- File mergeHeadFile = new File(gitDir, Constants.MERGE_HEAD);
+ public List<ObjectId> readMergeHeads() throws IOException, NoWorkTreeException {
+ if (isBare() || getDirectory() == null)
+ throw new NoWorkTreeException();
+
+ File mergeHeadFile = new File(getDirectory(), Constants.MERGE_HEAD);
byte[] raw;
try {
raw = IO.readFully(mergeHeadFile);
diff --git a/org.eclipse.jgit/src/org/eclipse/jgit/lib/RepositoryBuilder.java b/org.eclipse.jgit/src/org/eclipse/jgit/lib/RepositoryBuilder.java
new file mode 100644
index 0000000000..f9185e8f26
--- /dev/null
+++ b/org.eclipse.jgit/src/org/eclipse/jgit/lib/RepositoryBuilder.java
@@ -0,0 +1,74 @@
+/*
+ * Copyright (C) 2010, Google Inc.
+ * and other copyright owners as documented in the project's IP log.
+ *
+ * This program and the accompanying materials are made available
+ * under the terms of the Eclipse Distribution License v1.0 which
+ * accompanies this distribution, is reproduced below, and is
+ * available at http://www.eclipse.org/org/documents/edl-v10.php
+ *
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or
+ * without modification, are permitted provided that the following
+ * conditions are met:
+ *
+ * - Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ *
+ * - Redistributions in binary form must reproduce the above
+ * copyright notice, this list of conditions and the following
+ * disclaimer in the documentation and/or other materials provided
+ * with the distribution.
+ *
+ * - Neither the name of the Eclipse Foundation, Inc. nor the
+ * names of its contributors may be used to endorse or promote
+ * products derived from this software without specific prior
+ * written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND
+ * CONTRIBUTORS "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES,
+ * INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
+ * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR
+ * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
+ * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT
+ * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
+ * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER
+ * CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT,
+ * STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
+ * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF
+ * ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ */
+
+package org.eclipse.jgit.lib;
+
+import java.io.File;
+
+import org.eclipse.jgit.storage.file.FileRepositoryBuilder;
+
+/**
+ * Base class to support constructing a {@link Repository}.
+ * <p>
+ * Applications must set one of {@link #setGitDir(File)} or
+ * {@link #setWorkTree(File)}, or use {@link #readEnvironment()} or
+ * {@link #findGitDir()} in order to configure the minimum property set
+ * necessary to open a repository.
+ * <p>
+ * Single repository applications trying to be compatible with other Git
+ * implementations are encouraged to use a model such as:
+ *
+ * <pre>
+ * new RepositoryBuilder() //
+ * .setGitDir(gitDirArgument) // --git-dir if supplied, no-op if null
+ * .readEnviroment() // scan environment GIT_* variables
+ * .findGitDir() // scan up the file system tree
+ * .build()
+ * </pre>
+ *
+ * @see FileRepositoryBuilder
+ */
+public class RepositoryBuilder extends
+ BaseRepositoryBuilder<RepositoryBuilder, Repository> {
+ // Empty implementation, everything is inherited.
+}
diff --git a/org.eclipse.jgit/src/org/eclipse/jgit/lib/RepositoryCache.java b/org.eclipse.jgit/src/org/eclipse/jgit/lib/RepositoryCache.java
index 0b0260a4cc..dc5eae5173 100644
--- a/org.eclipse.jgit/src/org/eclipse/jgit/lib/RepositoryCache.java
+++ b/org.eclipse.jgit/src/org/eclipse/jgit/lib/RepositoryCache.java
@@ -52,6 +52,7 @@ import java.util.Map;
import java.util.concurrent.ConcurrentHashMap;
import org.eclipse.jgit.errors.RepositoryNotFoundException;
+import org.eclipse.jgit.storage.file.FileRepository;
import org.eclipse.jgit.util.FS;
import org.eclipse.jgit.util.IO;
import org.eclipse.jgit.util.RawParseUtils;
@@ -120,7 +121,10 @@ public class RepositoryCache {
* repository to register.
*/
public static void register(final Repository db) {
- cache.registerRepository(FileKey.exact(db.getDirectory(), db.getFS()), db);
+ if (db.getDirectory() != null) {
+ FileKey key = FileKey.exact(db.getDirectory(), db.getFS());
+ cache.registerRepository(key, db);
+ }
}
/**
@@ -133,7 +137,10 @@ public class RepositoryCache {
* repository to unregister.
*/
public static void close(final Repository db) {
- cache.unregisterRepository(FileKey.exact(db.getDirectory(), db.getFS()));
+ if (db.getDirectory() != null) {
+ FileKey key = FileKey.exact(db.getDirectory(), db.getFS());
+ cache.unregisterRepository(key);
+ }
}
/** Unregister all repositories from the cache. */
@@ -313,7 +320,7 @@ public class RepositoryCache {
public Repository open(final boolean mustExist) throws IOException {
if (mustExist && !isGitRepository(path, fs))
throw new RepositoryNotFoundException(path);
- return new Repository(path);
+ return new FileRepository(path);
}
@Override
diff --git a/org.eclipse.jgit/src/org/eclipse/jgit/lib/RepositoryConfig.java b/org.eclipse.jgit/src/org/eclipse/jgit/lib/RepositoryConfig.java
deleted file mode 100644
index 805975a8d1..0000000000
--- a/org.eclipse.jgit/src/org/eclipse/jgit/lib/RepositoryConfig.java
+++ /dev/null
@@ -1,135 +0,0 @@
-/*
- * Copyright (C) 2009, Constantine Plotnikov <constantine.plotnikov@gmail.com>
- * Copyright (C) 2007, Dave Watson <dwatson@mimvista.com>
- * Copyright (C) 2008-2009, Google Inc.
- * Copyright (C) 2009, JetBrains s.r.o.
- * Copyright (C) 2007-2008, Robin Rosenberg <robin.rosenberg@dewire.com>
- * Copyright (C) 2006-2008, Shawn O. Pearce <spearce@spearce.org>
- * Copyright (C) 2008, Thad Hughes <thadh@thad.corp.google.com>
- * Copyright (C) 2009, Yann Simon <yann.simon.fr@gmail.com>
- * and other copyright owners as documented in the project's IP log.
- *
- * This program and the accompanying materials are made available
- * under the terms of the Eclipse Distribution License v1.0 which
- * accompanies this distribution, is reproduced below, and is
- * available at http://www.eclipse.org/org/documents/edl-v10.php
- *
- * All rights reserved.
- *
- * Redistribution and use in source and binary forms, with or
- * without modification, are permitted provided that the following
- * conditions are met:
- *
- * - Redistributions of source code must retain the above copyright
- * notice, this list of conditions and the following disclaimer.
- *
- * - Redistributions in binary form must reproduce the above
- * copyright notice, this list of conditions and the following
- * disclaimer in the documentation and/or other materials provided
- * with the distribution.
- *
- * - Neither the name of the Eclipse Foundation, Inc. nor the
- * names of its contributors may be used to endorse or promote
- * products derived from this software without specific prior
- * written permission.
- *
- * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND
- * CONTRIBUTORS "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES,
- * INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
- * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
- * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR
- * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
- * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT
- * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
- * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER
- * CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT,
- * STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
- * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF
- * ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
- */
-
-package org.eclipse.jgit.lib;
-
-import java.io.File;
-
-/**
- * An object representing the Git config file.
- *
- * This can be either the repository specific file or the user global
- * file depending on how it is instantiated.
- */
-public class RepositoryConfig extends FileBasedConfig {
- /** Section name for a branch configuration. */
- public static final String BRANCH_SECTION = "branch";
-
- /**
- * Create a Git configuration file reader/writer/cache for a specific file.
- *
- * @param base
- * configuration that provides default values if this file does
- * not set/override a particular key. Often this is the user's
- * global configuration file, or the system level configuration.
- * @param cfgLocation
- * path of the file to load (or save).
- */
- public RepositoryConfig(final Config base, final File cfgLocation) {
- super(base, cfgLocation);
- }
-
- /**
- * @return Core configuration values
- */
- public CoreConfig getCore() {
- return get(CoreConfig.KEY);
- }
-
- /**
- * @return transfer, fetch and receive configuration values
- */
- public TransferConfig getTransfer() {
- return get(TransferConfig.KEY);
- }
-
- /** @return standard user configuration data */
- public UserConfig getUserConfig() {
- return get(UserConfig.KEY);
- }
-
- /**
- * @return the author name as defined in the git variables
- * and configurations. If no name could be found, try
- * to use the system user name instead.
- */
- public String getAuthorName() {
- return getUserConfig().getAuthorName();
- }
-
- /**
- * @return the committer name as defined in the git variables
- * and configurations. If no name could be found, try
- * to use the system user name instead.
- */
- public String getCommitterName() {
- return getUserConfig().getCommitterName();
- }
-
- /**
- * @return the author email as defined in git variables and
- * configurations. If no email could be found, try to
- * propose one default with the user name and the
- * host name.
- */
- public String getAuthorEmail() {
- return getUserConfig().getAuthorEmail();
- }
-
- /**
- * @return the committer email as defined in git variables and
- * configurations. If no email could be found, try to
- * propose one default with the user name and the
- * host name.
- */
- public String getCommitterEmail() {
- return getUserConfig().getCommitterEmail();
- }
-}
diff --git a/org.eclipse.jgit/src/org/eclipse/jgit/lib/RepositoryState.java b/org.eclipse.jgit/src/org/eclipse/jgit/lib/RepositoryState.java
index 2cf5225793..0a59906e94 100644
--- a/org.eclipse.jgit/src/org/eclipse/jgit/lib/RepositoryState.java
+++ b/org.eclipse.jgit/src/org/eclipse/jgit/lib/RepositoryState.java
@@ -55,6 +55,14 @@ import org.eclipse.jgit.JGitText;
* on the state are the only supported means of deciding what to do.
*/
public enum RepositoryState {
+ /** Has no work tree and cannot be used for normal editing. */
+ BARE {
+ public boolean canCheckout() { return false; }
+ public boolean canResetHead() { return false; }
+ public boolean canCommit() { return false; }
+ public String getDescription() { return "Bare"; }
+ },
+
/**
* A safe state for working normally
* */
diff --git a/org.eclipse.jgit/src/org/eclipse/jgit/lib/Tag.java b/org.eclipse.jgit/src/org/eclipse/jgit/lib/Tag.java
index 5b3531eb1f..25a06c9c6c 100644
--- a/org.eclipse.jgit/src/org/eclipse/jgit/lib/Tag.java
+++ b/org.eclipse.jgit/src/org/eclipse/jgit/lib/Tag.java
@@ -203,9 +203,14 @@ public class Tag {
final RefUpdate ru;
if (tagger!=null || message!=null || type!=null) {
- ObjectId tagid = new ObjectWriter(objdb).writeTag(this);
- setTagId(tagid);
- id = tagid;
+ ObjectInserter odi = objdb.newObjectInserter();
+ try {
+ id = odi.insert(Constants.OBJ_TAG, odi.format(this));
+ odi.flush();
+ setTagId(id);
+ } finally {
+ odi.release();
+ }
} else {
id = objId;
}
diff --git a/org.eclipse.jgit/src/org/eclipse/jgit/lib/DeltaRefPackedObjectLoader.java b/org.eclipse.jgit/src/org/eclipse/jgit/lib/ThreadSafeProgressMonitor.java
index 9f7589c291..9708bb2f92 100644
--- a/org.eclipse.jgit/src/org/eclipse/jgit/lib/DeltaRefPackedObjectLoader.java
+++ b/org.eclipse.jgit/src/org/eclipse/jgit/lib/ThreadSafeProgressMonitor.java
@@ -1,8 +1,5 @@
/*
- * Copyright (C) 2008-2009, Google Inc.
- * Copyright (C) 2008, Marek Zawirski <marek.zawirski@gmail.com>
- * Copyright (C) 2007, Robin Rosenberg <robin.rosenberg@dewire.com>
- * Copyright (C) 2006-2008, Shawn O. Pearce <spearce@spearce.org>
+ * Copyright (C) 2010, Google Inc.
* and other copyright owners as documented in the project's IP log.
*
* This program and the accompanying materials are made available
@@ -46,35 +43,69 @@
package org.eclipse.jgit.lib;
-import java.io.IOException;
+import java.util.concurrent.locks.ReentrantLock;
-import org.eclipse.jgit.errors.MissingObjectException;
+/**
+ * Wrapper around the general {@link ProgressMonitor} to make it thread safe.
+ */
+public class ThreadSafeProgressMonitor implements ProgressMonitor {
+ private final ProgressMonitor pm;
+
+ private final ReentrantLock lock;
-/** Reads a deltified object which uses an {@link ObjectId} to find its base. */
-class DeltaRefPackedObjectLoader extends DeltaPackedObjectLoader {
- private final ObjectId deltaBase;
+ /**
+ * Wrap a ProgressMonitor to be thread safe.
+ *
+ * @param pm
+ * the underlying monitor to receive events.
+ */
+ public ThreadSafeProgressMonitor(ProgressMonitor pm) {
+ this.pm = pm;
+ this.lock = new ReentrantLock();
+ }
+
+ public void start(int totalTasks) {
+ lock.lock();
+ try {
+ pm.start(totalTasks);
+ } finally {
+ lock.unlock();
+ }
+ }
- DeltaRefPackedObjectLoader(final PackFile pr, final long objectOffset,
- final int headerSz, final int deltaSz, final ObjectId base) {
- super(pr, objectOffset, headerSz, deltaSz);
- deltaBase = base;
+ public void beginTask(String title, int totalWork) {
+ lock.lock();
+ try {
+ pm.beginTask(title, totalWork);
+ } finally {
+ lock.unlock();
+ }
}
- protected PackedObjectLoader getBaseLoader(final WindowCursor curs)
- throws IOException {
- final PackedObjectLoader or = pack.get(curs, deltaBase);
- if (or == null)
- throw new MissingObjectException(deltaBase, "delta base");
- return or;
+ public void update(int completed) {
+ lock.lock();
+ try {
+ pm.update(completed);
+ } finally {
+ lock.unlock();
+ }
}
- @Override
- public int getRawType() {
- return Constants.OBJ_REF_DELTA;
+ public boolean isCancelled() {
+ lock.lock();
+ try {
+ return pm.isCancelled();
+ } finally {
+ lock.unlock();
+ }
}
- @Override
- public ObjectId getDeltaBase() throws IOException {
- return deltaBase;
+ public void endTask() {
+ lock.lock();
+ try {
+ pm.endTask();
+ } finally {
+ lock.unlock();
+ }
}
}
diff --git a/org.eclipse.jgit/src/org/eclipse/jgit/lib/Tree.java b/org.eclipse.jgit/src/org/eclipse/jgit/lib/Tree.java
index 0872c96250..d68b9f6380 100644
--- a/org.eclipse.jgit/src/org/eclipse/jgit/lib/Tree.java
+++ b/org.eclipse.jgit/src/org/eclipse/jgit/lib/Tree.java
@@ -537,10 +537,8 @@ public class Tree extends TreeEntry implements Treeish {
private void ensureLoaded() throws IOException, MissingObjectException {
if (!isLoaded()) {
- final ObjectLoader or = db.openTree(getId());
- if (or == null)
- throw new MissingObjectException(getId(), Constants.TYPE_TREE);
- readTree(or.getBytes());
+ ObjectLoader ldr = db.open(getId(), Constants.OBJ_TREE);
+ readTree(ldr.getCachedBytes());
}
}
diff --git a/org.eclipse.jgit/src/org/eclipse/jgit/lib/UnpackedObjectLoader.java b/org.eclipse.jgit/src/org/eclipse/jgit/lib/UnpackedObjectLoader.java
deleted file mode 100644
index cd2eb38ef1..0000000000
--- a/org.eclipse.jgit/src/org/eclipse/jgit/lib/UnpackedObjectLoader.java
+++ /dev/null
@@ -1,223 +0,0 @@
-/*
- * Copyright (C) 2007, Robin Rosenberg <robin.rosenberg@dewire.com>
- * Copyright (C) 2006-2008, Shawn O. Pearce <spearce@spearce.org>
- * and other copyright owners as documented in the project's IP log.
- *
- * This program and the accompanying materials are made available
- * under the terms of the Eclipse Distribution License v1.0 which
- * accompanies this distribution, is reproduced below, and is
- * available at http://www.eclipse.org/org/documents/edl-v10.php
- *
- * All rights reserved.
- *
- * Redistribution and use in source and binary forms, with or
- * without modification, are permitted provided that the following
- * conditions are met:
- *
- * - Redistributions of source code must retain the above copyright
- * notice, this list of conditions and the following disclaimer.
- *
- * - Redistributions in binary form must reproduce the above
- * copyright notice, this list of conditions and the following
- * disclaimer in the documentation and/or other materials provided
- * with the distribution.
- *
- * - Neither the name of the Eclipse Foundation, Inc. nor the
- * names of its contributors may be used to endorse or promote
- * products derived from this software without specific prior
- * written permission.
- *
- * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND
- * CONTRIBUTORS "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES,
- * INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
- * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
- * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR
- * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
- * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT
- * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
- * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER
- * CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT,
- * STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
- * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF
- * ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
- */
-
-package org.eclipse.jgit.lib;
-
-import java.io.File;
-import java.io.FileNotFoundException;
-import java.io.IOException;
-import java.util.zip.DataFormatException;
-import java.util.zip.Inflater;
-
-import org.eclipse.jgit.JGitText;
-import org.eclipse.jgit.errors.CorruptObjectException;
-import org.eclipse.jgit.util.IO;
-import org.eclipse.jgit.util.MutableInteger;
-import org.eclipse.jgit.util.RawParseUtils;
-
-/**
- * Loose object loader. This class loads an object not stored in a pack.
- */
-public class UnpackedObjectLoader extends ObjectLoader {
- private final int objectType;
-
- private final int objectSize;
-
- private final byte[] bytes;
-
- /**
- * Construct an ObjectLoader to read from the file.
- *
- * @param path
- * location of the loose object to read.
- * @param id
- * expected identity of the object being loaded, if known.
- * @throws FileNotFoundException
- * the loose object file does not exist.
- * @throws IOException
- * the loose object file exists, but is corrupt.
- */
- public UnpackedObjectLoader(final File path, final AnyObjectId id)
- throws IOException {
- this(IO.readFully(path), id);
- }
-
- /**
- * Construct an ObjectLoader from a loose object's compressed form.
- *
- * @param compressed
- * entire content of the loose object file.
- * @throws CorruptObjectException
- * The compressed data supplied does not match the format for a
- * valid loose object.
- */
- public UnpackedObjectLoader(final byte[] compressed)
- throws CorruptObjectException {
- this(compressed, null);
- }
-
- private UnpackedObjectLoader(final byte[] compressed, final AnyObjectId id)
- throws CorruptObjectException {
- // Try to determine if this is a legacy format loose object or
- // a new style loose object. The legacy format was completely
- // compressed with zlib so the first byte must be 0x78 (15-bit
- // window size, deflated) and the first 16 bit word must be
- // evenly divisible by 31. Otherwise its a new style loose
- // object.
- //
- final Inflater inflater = InflaterCache.get();
- try {
- final int fb = compressed[0] & 0xff;
- if (fb == 0x78 && (((fb << 8) | compressed[1] & 0xff) % 31) == 0) {
- inflater.setInput(compressed);
- final byte[] hdr = new byte[64];
- int avail = 0;
- while (!inflater.finished() && avail < hdr.length)
- try {
- int uncompressed = inflater.inflate(hdr, avail,
- hdr.length - avail);
- if (uncompressed == 0) {
- throw new CorruptObjectException(id,
- JGitText.get().corruptObjectBadStreamCorruptHeader);
- }
- avail += uncompressed;
- } catch (DataFormatException dfe) {
- final CorruptObjectException coe;
- coe = new CorruptObjectException(id, JGitText.get().corruptObjectBadStream);
- coe.initCause(dfe);
- throw coe;
- }
- if (avail < 5)
- throw new CorruptObjectException(id, JGitText.get().corruptObjectNoHeader);
-
- final MutableInteger p = new MutableInteger();
- objectType = Constants.decodeTypeString(id, hdr, (byte) ' ', p);
- objectSize = RawParseUtils.parseBase10(hdr, p.value, p);
- if (objectSize < 0)
- throw new CorruptObjectException(id, JGitText.get().corruptObjectNegativeSize);
- if (hdr[p.value++] != 0)
- throw new CorruptObjectException(id, JGitText.get().corruptObjectGarbageAfterSize);
- bytes = new byte[objectSize];
- if (p.value < avail)
- System.arraycopy(hdr, p.value, bytes, 0, avail - p.value);
- decompress(id, inflater, avail - p.value);
- } else {
- int p = 0;
- int c = compressed[p++] & 0xff;
- final int typeCode = (c >> 4) & 7;
- int size = c & 15;
- int shift = 4;
- while ((c & 0x80) != 0) {
- c = compressed[p++] & 0xff;
- size += (c & 0x7f) << shift;
- shift += 7;
- }
-
- switch (typeCode) {
- case Constants.OBJ_COMMIT:
- case Constants.OBJ_TREE:
- case Constants.OBJ_BLOB:
- case Constants.OBJ_TAG:
- objectType = typeCode;
- break;
- default:
- throw new CorruptObjectException(id, JGitText.get().corruptObjectInvalidType);
- }
-
- objectSize = size;
- bytes = new byte[objectSize];
- inflater.setInput(compressed, p, compressed.length - p);
- decompress(id, inflater, 0);
- }
- } finally {
- InflaterCache.release(inflater);
- }
- }
-
- private void decompress(final AnyObjectId id, final Inflater inf, int p)
- throws CorruptObjectException {
- try {
- while (!inf.finished()) {
- int uncompressed = inf.inflate(bytes, p, objectSize - p);
- p += uncompressed;
- if (uncompressed == 0 && !inf.finished()) {
- throw new CorruptObjectException(id,
- JGitText.get().corruptObjectBadStreamCorruptHeader);
- }
- }
- } catch (DataFormatException dfe) {
- final CorruptObjectException coe;
- coe = new CorruptObjectException(id, JGitText.get().corruptObjectBadStream);
- coe.initCause(dfe);
- throw coe;
- }
- if (p != objectSize)
- throw new CorruptObjectException(id, JGitText.get().corruptObjectIncorrectLength);
- }
-
- @Override
- public int getType() {
- return objectType;
- }
-
- @Override
- public long getSize() {
- return objectSize;
- }
-
- @Override
- public byte[] getCachedBytes() {
- return bytes;
- }
-
- @Override
- public int getRawType() {
- return objectType;
- }
-
- @Override
- public long getRawSize() {
- return objectSize;
- }
-}
diff --git a/org.eclipse.jgit/src/org/eclipse/jgit/lib/WholePackedObjectLoader.java b/org.eclipse.jgit/src/org/eclipse/jgit/lib/WholePackedObjectLoader.java
deleted file mode 100644
index fcfa57339e..0000000000
--- a/org.eclipse.jgit/src/org/eclipse/jgit/lib/WholePackedObjectLoader.java
+++ /dev/null
@@ -1,110 +0,0 @@
-/*
- * Copyright (C) 2008-2009, Google Inc.
- * Copyright (C) 2008, Marek Zawirski <marek.zawirski@gmail.com>
- * Copyright (C) 2006-2008, Shawn O. Pearce <spearce@spearce.org>
- * and other copyright owners as documented in the project's IP log.
- *
- * This program and the accompanying materials are made available
- * under the terms of the Eclipse Distribution License v1.0 which
- * accompanies this distribution, is reproduced below, and is
- * available at http://www.eclipse.org/org/documents/edl-v10.php
- *
- * All rights reserved.
- *
- * Redistribution and use in source and binary forms, with or
- * without modification, are permitted provided that the following
- * conditions are met:
- *
- * - Redistributions of source code must retain the above copyright
- * notice, this list of conditions and the following disclaimer.
- *
- * - Redistributions in binary form must reproduce the above
- * copyright notice, this list of conditions and the following
- * disclaimer in the documentation and/or other materials provided
- * with the distribution.
- *
- * - Neither the name of the Eclipse Foundation, Inc. nor the
- * names of its contributors may be used to endorse or promote
- * products derived from this software without specific prior
- * written permission.
- *
- * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND
- * CONTRIBUTORS "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES,
- * INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
- * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
- * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR
- * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
- * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT
- * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
- * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER
- * CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT,
- * STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
- * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF
- * ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
- */
-
-package org.eclipse.jgit.lib;
-
-import java.io.IOException;
-import java.text.MessageFormat;
-import java.util.zip.DataFormatException;
-
-import org.eclipse.jgit.JGitText;
-import org.eclipse.jgit.errors.CorruptObjectException;
-
-/** Reader for a non-delta (just deflated) object in a pack file. */
-class WholePackedObjectLoader extends PackedObjectLoader {
- private static final int OBJ_COMMIT = Constants.OBJ_COMMIT;
-
- WholePackedObjectLoader(final PackFile pr, final long objectOffset,
- final int headerSize, final int type, final int size) {
- super(pr, objectOffset, headerSize);
- objectType = type;
- objectSize = size;
- }
-
- @Override
- public void materialize(final WindowCursor curs) throws IOException {
- if (cachedBytes != null) {
- return;
- }
-
- if (objectType != OBJ_COMMIT) {
- UnpackedObjectCache.Entry cache = pack.readCache(objectOffset);
- if (cache != null) {
- curs.release();
- cachedBytes = cache.data;
- return;
- }
- }
-
- try {
- cachedBytes = pack.decompress(objectOffset + headerSize,
- objectSize, curs);
- curs.release();
- if (objectType != OBJ_COMMIT)
- pack.saveCache(objectOffset, cachedBytes, objectType);
- } catch (DataFormatException dfe) {
- final CorruptObjectException coe;
- coe = new CorruptObjectException(MessageFormat.format(JGitText.get().objectAtHasBadZlibStream,
- objectOffset, pack.getPackFile()));
- coe.initCause(dfe);
- throw coe;
- }
- }
-
- @Override
- public int getRawType() {
- return objectType;
- }
-
- @Override
- public long getRawSize() {
- return objectSize;
- }
-
- @Override
- public ObjectId getDeltaBase() {
- return null;
- }
-}
diff --git a/org.eclipse.jgit/src/org/eclipse/jgit/merge/Merger.java b/org.eclipse.jgit/src/org/eclipse/jgit/merge/Merger.java
index 38af20fb8f..68d60c0077 100644
--- a/org.eclipse.jgit/src/org/eclipse/jgit/merge/Merger.java
+++ b/org.eclipse.jgit/src/org/eclipse/jgit/merge/Merger.java
@@ -51,9 +51,9 @@ import org.eclipse.jgit.errors.IncorrectObjectTypeException;
import org.eclipse.jgit.lib.AnyObjectId;
import org.eclipse.jgit.lib.Constants;
import org.eclipse.jgit.lib.ObjectId;
-import org.eclipse.jgit.lib.ObjectWriter;
+import org.eclipse.jgit.lib.ObjectInserter;
import org.eclipse.jgit.lib.Repository;
-import org.eclipse.jgit.lib.WindowCursor;
+import org.eclipse.jgit.lib.ObjectReader;
import org.eclipse.jgit.revwalk.RevCommit;
import org.eclipse.jgit.revwalk.RevObject;
import org.eclipse.jgit.revwalk.RevTree;
@@ -70,10 +70,13 @@ public abstract class Merger {
/** The repository this merger operates on. */
protected final Repository db;
+ /** Reader to support {@link #walk} and other object loading. */
+ protected final ObjectReader reader;
+
/** A RevWalk for computing merge bases, or listing incoming commits. */
protected final RevWalk walk;
- private ObjectWriter writer;
+ private ObjectInserter inserter;
/** The original objects supplied in the merge; this can be any tree-ish. */
protected RevObject[] sourceObjects;
@@ -92,7 +95,8 @@ public abstract class Merger {
*/
protected Merger(final Repository local) {
db = local;
- walk = new RevWalk(db);
+ reader = db.newObjectReader();
+ walk = new RevWalk(reader);
}
/**
@@ -105,10 +109,10 @@ public abstract class Merger {
/**
* @return an object writer to create objects in {@link #getRepository()}.
*/
- public ObjectWriter getObjectWriter() {
- if (writer == null)
- writer = new ObjectWriter(getRepository());
- return writer;
+ public ObjectInserter getObjectInserter() {
+ if (inserter == null)
+ inserter = getRepository().newObjectInserter();
+ return inserter;
}
/**
@@ -148,7 +152,13 @@ public abstract class Merger {
for (int i = 0; i < sourceObjects.length; i++)
sourceTrees[i] = walk.parseTree(sourceObjects[i]);
- return mergeImpl();
+ try {
+ return mergeImpl();
+ } finally {
+ if (inserter != null)
+ inserter.release();
+ reader.release();
+ }
}
/**
@@ -202,12 +212,7 @@ public abstract class Merger {
*/
protected AbstractTreeIterator openTree(final AnyObjectId treeId)
throws IncorrectObjectTypeException, IOException {
- final WindowCursor curs = new WindowCursor();
- try {
- return new CanonicalTreeParser(null, db, treeId, curs);
- } finally {
- curs.release();
- }
+ return new CanonicalTreeParser(null, reader, treeId);
}
/**
diff --git a/org.eclipse.jgit/src/org/eclipse/jgit/merge/StrategySimpleTwoWayInCore.java b/org.eclipse.jgit/src/org/eclipse/jgit/merge/StrategySimpleTwoWayInCore.java
index 6cd244599e..29342a7308 100644
--- a/org.eclipse.jgit/src/org/eclipse/jgit/merge/StrategySimpleTwoWayInCore.java
+++ b/org.eclipse.jgit/src/org/eclipse/jgit/merge/StrategySimpleTwoWayInCore.java
@@ -51,6 +51,7 @@ import org.eclipse.jgit.dircache.DirCacheEntry;
import org.eclipse.jgit.errors.UnmergedPathException;
import org.eclipse.jgit.lib.FileMode;
import org.eclipse.jgit.lib.ObjectId;
+import org.eclipse.jgit.lib.ObjectInserter;
import org.eclipse.jgit.lib.Repository;
import org.eclipse.jgit.treewalk.AbstractTreeIterator;
import org.eclipse.jgit.treewalk.NameConflictTreeWalk;
@@ -99,7 +100,7 @@ public class StrategySimpleTwoWayInCore extends ThreeWayMergeStrategy {
InCoreMerger(final Repository local) {
super(local);
- tw = new NameConflictTreeWalk(db);
+ tw = new NameConflictTreeWalk(reader);
cache = DirCache.newInCore();
}
@@ -152,7 +153,9 @@ public class StrategySimpleTwoWayInCore extends ThreeWayMergeStrategy {
if (hasConflict)
return false;
try {
- resultTree = cache.writeTree(getObjectWriter());
+ ObjectInserter odi = getObjectInserter();
+ resultTree = cache.writeTree(odi);
+ odi.flush();
return true;
} catch (UnmergedPathException upe) {
resultTree = null;
@@ -168,7 +171,7 @@ public class StrategySimpleTwoWayInCore extends ThreeWayMergeStrategy {
final AbstractTreeIterator i = getTree(tree);
if (i != null) {
if (FileMode.TREE.equals(tw.getRawMode(tree))) {
- builder.addTree(tw.getRawPath(), stage, db, tw
+ builder.addTree(tw.getRawPath(), stage, reader, tw
.getObjectId(tree));
} else {
final DirCacheEntry e;
diff --git a/org.eclipse.jgit/src/org/eclipse/jgit/revplot/PlotWalk.java b/org.eclipse.jgit/src/org/eclipse/jgit/revplot/PlotWalk.java
index 6b4ed80e15..476592f5d9 100644
--- a/org.eclipse.jgit/src/org/eclipse/jgit/revplot/PlotWalk.java
+++ b/org.eclipse.jgit/src/org/eclipse/jgit/revplot/PlotWalk.java
@@ -53,12 +53,13 @@ import java.util.Set;
import org.eclipse.jgit.JGitText;
import org.eclipse.jgit.lib.AnyObjectId;
-import org.eclipse.jgit.lib.Commit;
+import org.eclipse.jgit.lib.PersonIdent;
import org.eclipse.jgit.lib.Ref;
import org.eclipse.jgit.lib.Repository;
-import org.eclipse.jgit.lib.Tag;
import org.eclipse.jgit.revwalk.RevCommit;
+import org.eclipse.jgit.revwalk.RevObject;
import org.eclipse.jgit.revwalk.RevSort;
+import org.eclipse.jgit.revwalk.RevTag;
import org.eclipse.jgit.revwalk.RevWalk;
/** Specialized RevWalk for visualization of a commit graph. */
@@ -115,8 +116,8 @@ public class PlotWalk extends RevWalk {
class PlotRefComparator implements Comparator<Ref> {
public int compare(Ref o1, Ref o2) {
try {
- Object obj1 = getRepository().mapObject(o1.getObjectId(), o1.getName());
- Object obj2 = getRepository().mapObject(o2.getObjectId(), o2.getName());
+ RevObject obj1 = parseAny(o1.getObjectId());
+ RevObject obj2 = parseAny(o2.getObjectId());
long t1 = timeof(obj1);
long t2 = timeof(obj2);
if (t1 > t2)
@@ -129,11 +130,15 @@ public class PlotWalk extends RevWalk {
return 0;
}
}
- long timeof(Object o) {
- if (o instanceof Commit)
- return ((Commit)o).getCommitter().getWhen().getTime();
- if (o instanceof Tag)
- return ((Tag)o).getTagger().getWhen().getTime();
+
+ long timeof(RevObject o) {
+ if (o instanceof RevCommit)
+ return ((RevCommit) o).getCommitTime();
+ if (o instanceof RevTag) {
+ RevTag tag = (RevTag) o;
+ PersonIdent who = tag.getTaggerIdent();
+ return who != null ? who.getWhen().getTime() : 0;
+ }
return 0;
}
}
diff --git a/org.eclipse.jgit/src/org/eclipse/jgit/revwalk/MergeBaseGenerator.java b/org.eclipse.jgit/src/org/eclipse/jgit/revwalk/MergeBaseGenerator.java
index edb883714b..76510ce387 100644
--- a/org.eclipse.jgit/src/org/eclipse/jgit/revwalk/MergeBaseGenerator.java
+++ b/org.eclipse.jgit/src/org/eclipse/jgit/revwalk/MergeBaseGenerator.java
@@ -137,7 +137,7 @@ class MergeBaseGenerator extends Generator {
for (;;) {
final RevCommit c = pending.next();
if (c == null) {
- walker.curs.release();
+ walker.reader.release();
return null;
}
diff --git a/org.eclipse.jgit/src/org/eclipse/jgit/revwalk/ObjectWalk.java b/org.eclipse.jgit/src/org/eclipse/jgit/revwalk/ObjectWalk.java
index 11d40012c5..a6ecfe2192 100644
--- a/org.eclipse.jgit/src/org/eclipse/jgit/revwalk/ObjectWalk.java
+++ b/org.eclipse.jgit/src/org/eclipse/jgit/revwalk/ObjectWalk.java
@@ -53,6 +53,7 @@ import org.eclipse.jgit.errors.MissingObjectException;
import org.eclipse.jgit.lib.AnyObjectId;
import org.eclipse.jgit.lib.Constants;
import org.eclipse.jgit.lib.FileMode;
+import org.eclipse.jgit.lib.ObjectReader;
import org.eclipse.jgit.lib.Repository;
import org.eclipse.jgit.treewalk.CanonicalTreeParser;
@@ -97,7 +98,19 @@ public class ObjectWalk extends RevWalk {
* the repository the walker will obtain data from.
*/
public ObjectWalk(final Repository repo) {
- super(repo);
+ this(repo.newObjectReader());
+ }
+
+ /**
+ * Create a new revision and object walker for a given repository.
+ *
+ * @param or
+ * the reader the walker will obtain data from. The reader should
+ * be released by the caller when the walker is no longer
+ * required.
+ */
+ public ObjectWalk(ObjectReader or) {
+ super(or);
pendingObjects = new BlockObjQueue();
treeWalk = new CanonicalTreeParser();
}
@@ -294,14 +307,14 @@ public class ObjectWalk extends RevWalk {
continue;
if (o instanceof RevTree) {
currentTree = (RevTree) o;
- treeWalk = treeWalk.resetRoot(db, currentTree, curs);
+ treeWalk = treeWalk.resetRoot(reader, currentTree);
}
return o;
}
}
private CanonicalTreeParser enter(RevObject tree) throws IOException {
- CanonicalTreeParser p = treeWalk.createSubtreeIterator0(db, tree, curs);
+ CanonicalTreeParser p = treeWalk.createSubtreeIterator0(reader, tree);
if (p.eof()) {
// We can't tolerate the subtree being an empty tree, as
// that will break us out early before we visit all names.
@@ -349,7 +362,7 @@ public class ObjectWalk extends RevWalk {
final RevObject o = nextObject();
if (o == null)
break;
- if (o instanceof RevBlob && !db.hasObject(o))
+ if (o instanceof RevBlob && !reader.has(o))
throw new MissingObjectException(o, Constants.TYPE_BLOB);
}
}
@@ -371,6 +384,18 @@ public class ObjectWalk extends RevWalk {
return last != null ? treeWalk.getEntryPathString() : null;
}
+ /**
+ * Get the current object's path hash code.
+ * <p>
+ * This method computes a hash code on the fly for this path, the hash is
+ * suitable to cluster objects that may have similar paths together.
+ *
+ * @return path hash code; any integer may be returned.
+ */
+ public int getPathHashCode() {
+ return last != null ? treeWalk.getEntryPathHashCode() : 0;
+ }
+
@Override
public void dispose() {
super.dispose();
@@ -403,7 +428,7 @@ public class ObjectWalk extends RevWalk {
return;
tree.flags |= UNINTERESTING;
- treeWalk = treeWalk.resetRoot(db, tree, curs);
+ treeWalk = treeWalk.resetRoot(reader, tree);
while (!treeWalk.eof()) {
final FileMode mode = treeWalk.getEntryFileMode();
final int sType = mode.getObjectType();
@@ -419,7 +444,7 @@ public class ObjectWalk extends RevWalk {
final RevTree t = lookupTree(idBuffer);
if ((t.flags & UNINTERESTING) == 0) {
t.flags |= UNINTERESTING;
- treeWalk = treeWalk.createSubtreeIterator0(db, t, curs);
+ treeWalk = treeWalk.createSubtreeIterator0(reader, t);
continue;
}
break;
diff --git a/org.eclipse.jgit/src/org/eclipse/jgit/revwalk/PendingGenerator.java b/org.eclipse.jgit/src/org/eclipse/jgit/revwalk/PendingGenerator.java
index e723bce51b..0e2bb98320 100644
--- a/org.eclipse.jgit/src/org/eclipse/jgit/revwalk/PendingGenerator.java
+++ b/org.eclipse.jgit/src/org/eclipse/jgit/revwalk/PendingGenerator.java
@@ -128,7 +128,7 @@ class PendingGenerator extends Generator {
for (;;) {
final RevCommit c = pending.next();
if (c == null) {
- walker.curs.release();
+ walker.reader.release();
return null;
}
@@ -174,7 +174,7 @@ class PendingGenerator extends Generator {
c.disposeBody();
}
} catch (StopWalkException swe) {
- walker.curs.release();
+ walker.reader.release();
pending.clear();
return null;
}
diff --git a/org.eclipse.jgit/src/org/eclipse/jgit/revwalk/RevCommit.java b/org.eclipse.jgit/src/org/eclipse/jgit/revwalk/RevCommit.java
index 2d96bbf676..84cc704c34 100644
--- a/org.eclipse.jgit/src/org/eclipse/jgit/revwalk/RevCommit.java
+++ b/org.eclipse.jgit/src/org/eclipse/jgit/revwalk/RevCommit.java
@@ -214,7 +214,8 @@ public class RevCommit extends RevObject {
* @return parsed commit.
*/
public final Commit asCommit(final RevWalk walk) {
- return new Commit(walk.db, this, buffer);
+ // TODO(spearce) Remove repository when this method dies.
+ return new Commit(walk.repository, this, buffer);
}
/**
diff --git a/org.eclipse.jgit/src/org/eclipse/jgit/revwalk/RevObject.java b/org.eclipse.jgit/src/org/eclipse/jgit/revwalk/RevObject.java
index 5dde43b271..a19f4d83ea 100644
--- a/org.eclipse.jgit/src/org/eclipse/jgit/revwalk/RevObject.java
+++ b/org.eclipse.jgit/src/org/eclipse/jgit/revwalk/RevObject.java
@@ -51,7 +51,6 @@ import org.eclipse.jgit.errors.MissingObjectException;
import org.eclipse.jgit.lib.AnyObjectId;
import org.eclipse.jgit.lib.Constants;
import org.eclipse.jgit.lib.ObjectId;
-import org.eclipse.jgit.lib.ObjectLoader;
/** Base object type accessed during revision walking. */
public abstract class RevObject extends ObjectId {
@@ -78,13 +77,7 @@ public abstract class RevObject extends ObjectId {
final byte[] loadCanonical(final RevWalk walk) throws IOException,
MissingObjectException, IncorrectObjectTypeException,
CorruptObjectException {
- final ObjectLoader ldr = walk.db.openObject(walk.curs, this);
- if (ldr == null)
- throw new MissingObjectException(this, getType());
- final byte[] data = ldr.getCachedBytes();
- if (getType() != ldr.getType())
- throw new IncorrectObjectTypeException(this, getType());
- return data;
+ return walk.reader.open(this, getType()).getCachedBytes();
}
/**
diff --git a/org.eclipse.jgit/src/org/eclipse/jgit/revwalk/RevTag.java b/org.eclipse.jgit/src/org/eclipse/jgit/revwalk/RevTag.java
index d2a665e078..a04ea7154e 100644
--- a/org.eclipse.jgit/src/org/eclipse/jgit/revwalk/RevTag.java
+++ b/org.eclipse.jgit/src/org/eclipse/jgit/revwalk/RevTag.java
@@ -194,7 +194,7 @@ public class RevTag extends RevObject {
* @return parsed tag.
*/
public Tag asTag(final RevWalk walk) {
- return new Tag(walk.db, this, tagName, buffer);
+ return new Tag(walk.repository, this, tagName, buffer);
}
/**
diff --git a/org.eclipse.jgit/src/org/eclipse/jgit/revwalk/RevWalk.java b/org.eclipse.jgit/src/org/eclipse/jgit/revwalk/RevWalk.java
index a8c67c60a8..7406bb60cd 100644
--- a/org.eclipse.jgit/src/org/eclipse/jgit/revwalk/RevWalk.java
+++ b/org.eclipse.jgit/src/org/eclipse/jgit/revwalk/RevWalk.java
@@ -62,7 +62,7 @@ import org.eclipse.jgit.lib.ObjectId;
import org.eclipse.jgit.lib.ObjectIdSubclassMap;
import org.eclipse.jgit.lib.ObjectLoader;
import org.eclipse.jgit.lib.Repository;
-import org.eclipse.jgit.lib.WindowCursor;
+import org.eclipse.jgit.lib.ObjectReader;
import org.eclipse.jgit.revwalk.filter.RevFilter;
import org.eclipse.jgit.treewalk.filter.TreeFilter;
@@ -157,9 +157,10 @@ public class RevWalk implements Iterable<RevCommit> {
private static final int APP_FLAGS = -1 & ~((1 << RESERVED_FLAGS) - 1);
- final Repository db;
+ /** Exists <b>ONLY</b> to support legacy Tag and Commit objects. */
+ final Repository repository;
- final WindowCursor curs;
+ final ObjectReader reader;
final MutableObjectId idBuffer;
@@ -189,11 +190,29 @@ public class RevWalk implements Iterable<RevCommit> {
* Create a new revision walker for a given repository.
*
* @param repo
- * the repository the walker will obtain data from.
+ * the repository the walker will obtain data from. An
+ * ObjectReader will be created by the walker, and must be
+ * released by the caller.
*/
public RevWalk(final Repository repo) {
- db = repo;
- curs = new WindowCursor();
+ this(repo, repo.newObjectReader());
+ }
+
+ /**
+ * Create a new revision walker for a given repository.
+ *
+ * @param or
+ * the reader the walker will obtain data from. The reader should
+ * be released by the caller when the walker is no longer
+ * required.
+ */
+ public RevWalk(ObjectReader or) {
+ this(null, or);
+ }
+
+ private RevWalk(final Repository repo, final ObjectReader or) {
+ repository = repo;
+ reader = or;
idBuffer = new MutableObjectId();
objects = new ObjectIdSubclassMap<RevObject>();
roots = new ArrayList<RevCommit>();
@@ -205,13 +224,19 @@ public class RevWalk implements Iterable<RevCommit> {
retainBody = true;
}
+ /** @return the reader this walker is using to load objects. */
+ public ObjectReader getObjectReader() {
+ return reader;
+ }
+
/**
- * Get the repository this walker loads objects from.
- *
- * @return the repository this walker was created to read.
+ * Release any resources used by this walker's reader.
+ * <p>
+ * A walker that has been released can be used again, but may need to be
+ * released after the subsequent usage.
*/
- public Repository getRepository() {
- return db;
+ public void release() {
+ reader.release();
}
/**
@@ -678,11 +703,7 @@ public class RevWalk implements Iterable<RevCommit> {
public RevCommit parseCommit(final AnyObjectId id)
throws MissingObjectException, IncorrectObjectTypeException,
IOException {
- RevObject c = parseAny(id);
- while (c instanceof RevTag) {
- c = ((RevTag) c).getObject();
- parseHeaders(c);
- }
+ RevObject c = peel(parseAny(id));
if (!(c instanceof RevCommit))
throw new IncorrectObjectTypeException(id.toObjectId(),
Constants.TYPE_COMMIT);
@@ -709,11 +730,7 @@ public class RevWalk implements Iterable<RevCommit> {
public RevTree parseTree(final AnyObjectId id)
throws MissingObjectException, IncorrectObjectTypeException,
IOException {
- RevObject c = parseAny(id);
- while (c instanceof RevTag) {
- c = ((RevTag) c).getObject();
- parseHeaders(c);
- }
+ RevObject c = peel(parseAny(id));
final RevTree t;
if (c instanceof RevCommit)
@@ -773,15 +790,12 @@ public class RevWalk implements Iterable<RevCommit> {
throws MissingObjectException, IOException {
RevObject r = objects.get(id);
if (r == null) {
- final ObjectLoader ldr = db.openObject(curs, id);
- if (ldr == null)
- throw new MissingObjectException(id.toObjectId(), "unknown");
- final byte[] data = ldr.getCachedBytes();
+ final ObjectLoader ldr = reader.open(id);
final int type = ldr.getType();
switch (type) {
case Constants.OBJ_COMMIT: {
final RevCommit c = createCommit(id);
- c.parseCanonical(this, data);
+ c.parseCanonical(this, ldr.getCachedBytes());
r = c;
break;
}
@@ -797,7 +811,7 @@ public class RevWalk implements Iterable<RevCommit> {
}
case Constants.OBJ_TAG: {
final RevTag t = new RevTag(id);
- t.parseCanonical(this, data);
+ t.parseCanonical(this, ldr.getCachedBytes());
r = t;
break;
}
@@ -848,6 +862,29 @@ public class RevWalk implements Iterable<RevCommit> {
}
/**
+ * Peel back annotated tags until a non-tag object is found.
+ *
+ * @param obj
+ * the starting object.
+ * @return If {@code obj} is not an annotated tag, {@code obj}. Otherwise
+ * the first non-tag object that {@code obj} references. The
+ * returned object's headers have been parsed.
+ * @throws MissingObjectException
+ * a referenced object cannot be found.
+ * @throws IOException
+ * a pack file or loose object could not be read.
+ */
+ public RevObject peel(RevObject obj) throws MissingObjectException,
+ IOException {
+ while (obj instanceof RevTag) {
+ parseHeaders(obj);
+ obj = ((RevTag) obj).getObject();
+ }
+ parseHeaders(obj);
+ return obj;
+ }
+
+ /**
* Create a new flag for application use during walking.
* <p>
* Applications are only assured to be able to create 24 unique flags on any
@@ -1023,7 +1060,7 @@ public class RevWalk implements Iterable<RevCommit> {
}
}
- curs.release();
+ reader.release();
roots.clear();
queue = new DateRevQueue();
pending = new StartGenerator(this);
@@ -1038,11 +1075,12 @@ public class RevWalk implements Iterable<RevCommit> {
* All RevFlag instances are also invalidated, and must not be reused.
*/
public void dispose() {
+ reader.release();
freeFlags = APP_FLAGS;
delayFreeFlags = 0;
carryFlags = UNINTERESTING;
objects.clear();
- curs.release();
+ reader.release();
roots.clear();
queue = new DateRevQueue();
pending = new StartGenerator(this);
diff --git a/org.eclipse.jgit/src/org/eclipse/jgit/revwalk/RewriteTreeFilter.java b/org.eclipse.jgit/src/org/eclipse/jgit/revwalk/RewriteTreeFilter.java
index 4c5a2a77eb..8ec2d2b091 100644
--- a/org.eclipse.jgit/src/org/eclipse/jgit/revwalk/RewriteTreeFilter.java
+++ b/org.eclipse.jgit/src/org/eclipse/jgit/revwalk/RewriteTreeFilter.java
@@ -82,11 +82,11 @@ class RewriteTreeFilter extends RevFilter {
private final TreeWalk pathFilter;
- private final Repository repo;
+ private final Repository repository;
RewriteTreeFilter(final RevWalk walker, final TreeFilter t) {
- repo = walker.db;
- pathFilter = new TreeWalk(repo);
+ repository = walker.repository;
+ pathFilter = new TreeWalk(walker.reader);
pathFilter.setFilter(t);
pathFilter.setRecursive(t.shouldBeRecursive());
}
@@ -239,7 +239,7 @@ class RewriteTreeFilter extends RevFilter {
tw.reset(trees);
List<DiffEntry> files = DiffEntry.scan(tw);
- RenameDetector rd = new RenameDetector(repo);
+ RenameDetector rd = new RenameDetector(repository);
rd.addAll(files);
files = rd.compute();
diff --git a/org.eclipse.jgit/src/org/eclipse/jgit/lib/ByteArrayWindow.java b/org.eclipse.jgit/src/org/eclipse/jgit/storage/file/ByteArrayWindow.java
index 8042610310..457c8dc90a 100644
--- a/org.eclipse.jgit/src/org/eclipse/jgit/lib/ByteArrayWindow.java
+++ b/org.eclipse.jgit/src/org/eclipse/jgit/storage/file/ByteArrayWindow.java
@@ -43,8 +43,11 @@
* ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
*/
-package org.eclipse.jgit.lib;
+package org.eclipse.jgit.storage.file;
+import java.io.IOException;
+import java.io.OutputStream;
+import java.util.zip.CRC32;
import java.util.zip.DataFormatException;
import java.util.zip.Inflater;
@@ -67,31 +70,25 @@ final class ByteArrayWindow extends ByteWindow {
}
@Override
- protected int inflate(final int pos, final byte[] b, int o,
- final Inflater inf) throws DataFormatException {
- while (!inf.finished()) {
- if (inf.needsInput()) {
- inf.setInput(array, pos, array.length - pos);
- break;
- }
- o += inf.inflate(b, o, b.length - o);
- }
- while (!inf.finished() && !inf.needsInput())
- o += inf.inflate(b, o, b.length - o);
- return o;
+ protected int setInput(final int pos, final Inflater inf)
+ throws DataFormatException {
+ int n = array.length - pos;
+ inf.setInput(array, pos, n);
+ return n;
}
- @Override
- protected void inflateVerify(final int pos, final Inflater inf)
+ void crc32(CRC32 out, long pos, int cnt) {
+ out.update(array, (int) (pos - start), cnt);
+ }
+
+ void write(OutputStream out, long pos, int cnt) throws IOException {
+ out.write(array, (int) (pos - start), cnt);
+ }
+
+ void check(Inflater inf, byte[] tmp, long pos, int cnt)
throws DataFormatException {
- while (!inf.finished()) {
- if (inf.needsInput()) {
- inf.setInput(array, pos, array.length - pos);
- break;
- }
- inf.inflate(verifyGarbageBuffer, 0, verifyGarbageBuffer.length);
- }
- while (!inf.finished() && !inf.needsInput())
- inf.inflate(verifyGarbageBuffer, 0, verifyGarbageBuffer.length);
+ inf.setInput(array, (int) (pos - start), cnt);
+ while (inf.inflate(tmp, 0, tmp.length) > 0)
+ continue;
}
}
diff --git a/org.eclipse.jgit/src/org/eclipse/jgit/lib/ByteBufferWindow.java b/org.eclipse.jgit/src/org/eclipse/jgit/storage/file/ByteBufferWindow.java
index 1b29934d28..29a0159950 100644
--- a/org.eclipse.jgit/src/org/eclipse/jgit/lib/ByteBufferWindow.java
+++ b/org.eclipse.jgit/src/org/eclipse/jgit/storage/file/ByteBufferWindow.java
@@ -43,7 +43,7 @@
* ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
*/
-package org.eclipse.jgit.lib;
+package org.eclipse.jgit.storage.file;
import java.nio.ByteBuffer;
import java.util.zip.DataFormatException;
@@ -72,39 +72,13 @@ final class ByteBufferWindow extends ByteWindow {
}
@Override
- protected int inflate(final int pos, final byte[] b, int o,
- final Inflater inf) throws DataFormatException {
- final byte[] tmp = new byte[512];
- final ByteBuffer s = buffer.slice();
- s.position(pos);
- while (s.remaining() > 0 && !inf.finished()) {
- if (inf.needsInput()) {
- final int n = Math.min(s.remaining(), tmp.length);
- s.get(tmp, 0, n);
- inf.setInput(tmp, 0, n);
- }
- o += inf.inflate(b, o, b.length - o);
- }
- while (!inf.finished() && !inf.needsInput())
- o += inf.inflate(b, o, b.length - o);
- return o;
- }
-
- @Override
- protected void inflateVerify(final int pos, final Inflater inf)
+ protected int setInput(final int pos, final Inflater inf)
throws DataFormatException {
- final byte[] tmp = new byte[512];
final ByteBuffer s = buffer.slice();
s.position(pos);
- while (s.remaining() > 0 && !inf.finished()) {
- if (inf.needsInput()) {
- final int n = Math.min(s.remaining(), tmp.length);
- s.get(tmp, 0, n);
- inf.setInput(tmp, 0, n);
- }
- inf.inflate(verifyGarbageBuffer, 0, verifyGarbageBuffer.length);
- }
- while (!inf.finished() && !inf.needsInput())
- inf.inflate(verifyGarbageBuffer, 0, verifyGarbageBuffer.length);
+ final byte[] tmp = new byte[Math.min(s.remaining(), 512)];
+ s.get(tmp, 0, tmp.length);
+ inf.setInput(tmp, 0, tmp.length);
+ return tmp.length;
}
}
diff --git a/org.eclipse.jgit/src/org/eclipse/jgit/lib/ByteWindow.java b/org.eclipse.jgit/src/org/eclipse/jgit/storage/file/ByteWindow.java
index cbef4218af..f92efb4ac9 100644
--- a/org.eclipse.jgit/src/org/eclipse/jgit/lib/ByteWindow.java
+++ b/org.eclipse.jgit/src/org/eclipse/jgit/storage/file/ByteWindow.java
@@ -42,7 +42,7 @@
* ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
*/
-package org.eclipse.jgit.lib;
+package org.eclipse.jgit.storage.file;
import java.util.zip.DataFormatException;
import java.util.zip.Inflater;
@@ -117,69 +117,10 @@ abstract class ByteWindow {
*/
protected abstract int copy(int pos, byte[] dstbuf, int dstoff, int cnt);
- /**
- * Pump bytes into the supplied inflater as input.
- *
- * @param pos
- * offset within the file to start supplying input from.
- * @param dstbuf
- * destination buffer the inflater should output decompressed
- * data to.
- * @param dstoff
- * current offset within <code>dstbuf</code> to inflate into.
- * @param inf
- * the inflater to feed input to. The caller is responsible for
- * initializing the inflater as multiple windows may need to
- * supply data to the same inflater to completely decompress
- * something.
- * @return updated <code>dstoff</code> based on the number of bytes
- * successfully copied into <code>dstbuf</code> by
- * <code>inf</code>. If the inflater is not yet finished then
- * another window's data must still be supplied as input to finish
- * decompression.
- * @throws DataFormatException
- * the inflater encountered an invalid chunk of data. Data
- * stream corruption is likely.
- */
- final int inflate(long pos, byte[] dstbuf, int dstoff, Inflater inf)
- throws DataFormatException {
- return inflate((int) (pos - start), dstbuf, dstoff, inf);
- }
-
- /**
- * Pump bytes into the supplied inflater as input.
- *
- * @param pos
- * offset within the window to start supplying input from.
- * @param dstbuf
- * destination buffer the inflater should output decompressed
- * data to.
- * @param dstoff
- * current offset within <code>dstbuf</code> to inflate into.
- * @param inf
- * the inflater to feed input to. The caller is responsible for
- * initializing the inflater as multiple windows may need to
- * supply data to the same inflater to completely decompress
- * something.
- * @return updated <code>dstoff</code> based on the number of bytes
- * successfully copied into <code>dstbuf</code> by
- * <code>inf</code>. If the inflater is not yet finished then
- * another window's data must still be supplied as input to finish
- * decompression.
- * @throws DataFormatException
- * the inflater encountered an invalid chunk of data. Data
- * stream corruption is likely.
- */
- protected abstract int inflate(int pos, byte[] dstbuf, int dstoff,
- Inflater inf) throws DataFormatException;
-
- protected static final byte[] verifyGarbageBuffer = new byte[2048];
-
- final void inflateVerify(final long pos, final Inflater inf)
- throws DataFormatException {
- inflateVerify((int) (pos - start), inf);
+ final int setInput(long pos, Inflater inf) throws DataFormatException {
+ return setInput((int) (pos - start), inf);
}
- protected abstract void inflateVerify(int pos, Inflater inf)
+ protected abstract int setInput(int pos, Inflater inf)
throws DataFormatException;
}
diff --git a/org.eclipse.jgit/src/org/eclipse/jgit/lib/CachedObjectDirectory.java b/org.eclipse.jgit/src/org/eclipse/jgit/storage/file/CachedObjectDirectory.java
index 3724f84463..8ea0b854c0 100644
--- a/org.eclipse.jgit/src/org/eclipse/jgit/lib/CachedObjectDirectory.java
+++ b/org.eclipse.jgit/src/org/eclipse/jgit/storage/file/CachedObjectDirectory.java
@@ -42,32 +42,47 @@
* ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
*/
-package org.eclipse.jgit.lib;
+package org.eclipse.jgit.storage.file;
import java.io.File;
import java.io.IOException;
+import org.eclipse.jgit.lib.AnyObjectId;
+import org.eclipse.jgit.lib.Constants;
+import org.eclipse.jgit.lib.ObjectDatabase;
+import org.eclipse.jgit.lib.ObjectId;
+import org.eclipse.jgit.lib.ObjectIdSubclassMap;
+import org.eclipse.jgit.lib.ObjectInserter;
+import org.eclipse.jgit.lib.ObjectLoader;
+import org.eclipse.jgit.storage.pack.ObjectToPack;
+import org.eclipse.jgit.storage.pack.PackWriter;
+
/**
* The cached instance of an {@link ObjectDirectory}.
* <p>
* This class caches the list of loose objects in memory, so the file system is
* not queried with stat calls.
*/
-public class CachedObjectDirectory extends CachedObjectDatabase {
+class CachedObjectDirectory extends FileObjectDatabase {
/**
* The set that contains unpacked objects identifiers, it is created when
* the cached instance is created.
*/
private final ObjectIdSubclassMap<ObjectId> unpackedObjects = new ObjectIdSubclassMap<ObjectId>();
+ private final ObjectDirectory wrapped;
+
+ private AlternateHandle[] alts;
+
/**
* The constructor
*
* @param wrapped
* the wrapped database
*/
- public CachedObjectDirectory(ObjectDirectory wrapped) {
- super(wrapped);
+ CachedObjectDirectory(ObjectDirectory wrapped) {
+ this.wrapped = wrapped;
+
File objects = wrapped.getDirectory();
String[] fanout = objects.list();
if (fanout == null)
@@ -91,22 +106,108 @@ public class CachedObjectDirectory extends CachedObjectDatabase {
}
@Override
- protected ObjectLoader openObject2(WindowCursor curs, String objectName,
+ public void close() {
+ // Don't close anything.
+ }
+
+ @Override
+ public ObjectInserter newInserter() {
+ return wrapped.newInserter();
+ }
+
+ @Override
+ public ObjectDatabase newCachedDatabase() {
+ return this;
+ }
+
+ @Override
+ FileObjectDatabase newCachedFileObjectDatabase() {
+ return this;
+ }
+
+ @Override
+ File getDirectory() {
+ return wrapped.getDirectory();
+ }
+
+ @Override
+ AlternateHandle[] myAlternates() {
+ if (alts == null) {
+ AlternateHandle[] src = wrapped.myAlternates();
+ alts = new AlternateHandle[src.length];
+ for (int i = 0; i < alts.length; i++) {
+ FileObjectDatabase s = src[i].db;
+ alts[i] = new AlternateHandle(s.newCachedFileObjectDatabase());
+ }
+ }
+ return alts;
+ }
+
+ @Override
+ boolean tryAgain1() {
+ return wrapped.tryAgain1();
+ }
+
+ @Override
+ public boolean has(final AnyObjectId objectId) {
+ return hasObjectImpl1(objectId);
+ }
+
+ @Override
+ boolean hasObject1(AnyObjectId objectId) {
+ return unpackedObjects.contains(objectId)
+ || wrapped.hasObject1(objectId);
+ }
+
+ @Override
+ ObjectLoader openObject(final WindowCursor curs,
+ final AnyObjectId objectId) throws IOException {
+ return openObjectImpl1(curs, objectId);
+ }
+
+ @Override
+ ObjectLoader openObject1(WindowCursor curs, AnyObjectId objectId)
+ throws IOException {
+ if (unpackedObjects.contains(objectId))
+ return wrapped.openObject2(curs, objectId.name(), objectId);
+ return wrapped.openObject1(curs, objectId);
+ }
+
+ @Override
+ boolean hasObject2(String objectId) {
+ // This method should never be invoked.
+ throw new UnsupportedOperationException();
+ }
+
+ @Override
+ ObjectLoader openObject2(WindowCursor curs, String objectName,
AnyObjectId objectId) throws IOException {
- if (unpackedObjects.get(objectId) == null)
- return null;
- return super.openObject2(curs, objectName, objectId);
+ // This method should never be invoked.
+ throw new UnsupportedOperationException();
+ }
+
+ @Override
+ long getObjectSize1(WindowCursor curs, AnyObjectId objectId) throws IOException {
+ if (unpackedObjects.contains(objectId))
+ return wrapped.getObjectSize2(curs, objectId.name(), objectId);
+ return wrapped.getObjectSize1(curs, objectId);
+ }
+
+ @Override
+ long getObjectSize2(WindowCursor curs, String objectName, AnyObjectId objectId)
+ throws IOException {
+ // This method should never be invoked.
+ throw new UnsupportedOperationException();
}
@Override
- protected boolean hasObject1(AnyObjectId objectId) {
- if (unpackedObjects.get(objectId) != null)
- return true; // known to be loose
- return super.hasObject1(objectId);
+ void selectObjectRepresentation(PackWriter packer, ObjectToPack otp,
+ WindowCursor curs) throws IOException {
+ wrapped.selectObjectRepresentation(packer, otp, curs);
}
@Override
- protected boolean hasObject2(String name) {
- return false; // loose objects were tested by hasObject1
+ int getStreamFileThreshold() {
+ return wrapped.getStreamFileThreshold();
}
}
diff --git a/org.eclipse.jgit/src/org/eclipse/jgit/lib/FileBasedConfig.java b/org.eclipse.jgit/src/org/eclipse/jgit/storage/file/FileBasedConfig.java
index eb00917917..b39efb0e8b 100644
--- a/org.eclipse.jgit/src/org/eclipse/jgit/lib/FileBasedConfig.java
+++ b/org.eclipse.jgit/src/org/eclipse/jgit/storage/file/FileBasedConfig.java
@@ -47,7 +47,7 @@
* ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
*/
-package org.eclipse.jgit.lib;
+package org.eclipse.jgit.storage.file;
import java.io.File;
import java.io.FileNotFoundException;
@@ -56,6 +56,8 @@ import java.text.MessageFormat;
import org.eclipse.jgit.JGitText;
import org.eclipse.jgit.errors.ConfigInvalidException;
+import org.eclipse.jgit.lib.Config;
+import org.eclipse.jgit.lib.Constants;
import org.eclipse.jgit.util.IO;
import org.eclipse.jgit.util.RawParseUtils;
diff --git a/org.eclipse.jgit/src/org/eclipse/jgit/storage/file/FileObjectDatabase.java b/org.eclipse.jgit/src/org/eclipse/jgit/storage/file/FileObjectDatabase.java
new file mode 100644
index 0000000000..250c7cac0d
--- /dev/null
+++ b/org.eclipse.jgit/src/org/eclipse/jgit/storage/file/FileObjectDatabase.java
@@ -0,0 +1,271 @@
+/*
+ * Copyright (C) 2010, Google Inc.
+ * and other copyright owners as documented in the project's IP log.
+ *
+ * This program and the accompanying materials are made available
+ * under the terms of the Eclipse Distribution License v1.0 which
+ * accompanies this distribution, is reproduced below, and is
+ * available at http://www.eclipse.org/org/documents/edl-v10.php
+ *
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or
+ * without modification, are permitted provided that the following
+ * conditions are met:
+ *
+ * - Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ *
+ * - Redistributions in binary form must reproduce the above
+ * copyright notice, this list of conditions and the following
+ * disclaimer in the documentation and/or other materials provided
+ * with the distribution.
+ *
+ * - Neither the name of the Eclipse Foundation, Inc. nor the
+ * names of its contributors may be used to endorse or promote
+ * products derived from this software without specific prior
+ * written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND
+ * CONTRIBUTORS "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES,
+ * INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
+ * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR
+ * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
+ * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT
+ * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
+ * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER
+ * CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT,
+ * STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
+ * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF
+ * ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ */
+
+package org.eclipse.jgit.storage.file;
+
+import java.io.File;
+import java.io.IOException;
+
+import org.eclipse.jgit.lib.AnyObjectId;
+import org.eclipse.jgit.lib.ObjectDatabase;
+import org.eclipse.jgit.lib.ObjectLoader;
+import org.eclipse.jgit.lib.ObjectReader;
+import org.eclipse.jgit.storage.pack.ObjectToPack;
+import org.eclipse.jgit.storage.pack.PackWriter;
+
+abstract class FileObjectDatabase extends ObjectDatabase {
+ @Override
+ public ObjectReader newReader() {
+ return new WindowCursor(this);
+ }
+
+ /**
+ * Does the requested object exist in this database?
+ * <p>
+ * Alternates (if present) are searched automatically.
+ *
+ * @param objectId
+ * identity of the object to test for existence of.
+ * @return true if the specified object is stored in this database, or any
+ * of the alternate databases.
+ */
+ public boolean has(final AnyObjectId objectId) {
+ return hasObjectImpl1(objectId) || hasObjectImpl2(objectId.name());
+ }
+
+ final boolean hasObjectImpl1(final AnyObjectId objectId) {
+ if (hasObject1(objectId))
+ return true;
+
+ for (final AlternateHandle alt : myAlternates()) {
+ if (alt.db.hasObjectImpl1(objectId))
+ return true;
+ }
+
+ return tryAgain1() && hasObject1(objectId);
+ }
+
+ final boolean hasObjectImpl2(final String objectId) {
+ if (hasObject2(objectId))
+ return true;
+
+ for (final AlternateHandle alt : myAlternates()) {
+ if (alt.db.hasObjectImpl2(objectId))
+ return true;
+ }
+
+ return false;
+ }
+
+ /**
+ * Open an object from this database.
+ * <p>
+ * Alternates (if present) are searched automatically.
+ *
+ * @param curs
+ * temporary working space associated with the calling thread.
+ * @param objectId
+ * identity of the object to open.
+ * @return a {@link ObjectLoader} for accessing the data of the named
+ * object, or null if the object does not exist.
+ * @throws IOException
+ */
+ ObjectLoader openObject(final WindowCursor curs, final AnyObjectId objectId)
+ throws IOException {
+ ObjectLoader ldr;
+
+ ldr = openObjectImpl1(curs, objectId);
+ if (ldr != null)
+ return ldr;
+
+ ldr = openObjectImpl2(curs, objectId.name(), objectId);
+ if (ldr != null)
+ return ldr;
+
+ return null;
+ }
+
+ final ObjectLoader openObjectImpl1(final WindowCursor curs,
+ final AnyObjectId objectId) throws IOException {
+ ObjectLoader ldr;
+
+ ldr = openObject1(curs, objectId);
+ if (ldr != null)
+ return ldr;
+
+ for (final AlternateHandle alt : myAlternates()) {
+ ldr = alt.db.openObjectImpl1(curs, objectId);
+ if (ldr != null)
+ return ldr;
+ }
+
+ if (tryAgain1()) {
+ ldr = openObject1(curs, objectId);
+ if (ldr != null)
+ return ldr;
+ }
+
+ return null;
+ }
+
+ final ObjectLoader openObjectImpl2(final WindowCursor curs,
+ final String objectName, final AnyObjectId objectId)
+ throws IOException {
+ ObjectLoader ldr;
+
+ ldr = openObject2(curs, objectName, objectId);
+ if (ldr != null)
+ return ldr;
+
+ for (final AlternateHandle alt : myAlternates()) {
+ ldr = alt.db.openObjectImpl2(curs, objectName, objectId);
+ if (ldr != null)
+ return ldr;
+ }
+
+ return null;
+ }
+
+ long getObjectSize(WindowCursor curs, AnyObjectId objectId)
+ throws IOException {
+ long sz = getObjectSizeImpl1(curs, objectId);
+ if (0 <= sz)
+ return sz;
+ return getObjectSizeImpl2(curs, objectId.name(), objectId);
+ }
+
+ final long getObjectSizeImpl1(final WindowCursor curs,
+ final AnyObjectId objectId) throws IOException {
+ long sz;
+
+ sz = getObjectSize1(curs, objectId);
+ if (0 <= sz)
+ return sz;
+
+ for (final AlternateHandle alt : myAlternates()) {
+ sz = alt.db.getObjectSizeImpl1(curs, objectId);
+ if (0 <= sz)
+ return sz;
+ }
+
+ if (tryAgain1()) {
+ sz = getObjectSize1(curs, objectId);
+ if (0 <= sz)
+ return sz;
+ }
+
+ return -1;
+ }
+
+ final long getObjectSizeImpl2(final WindowCursor curs,
+ final String objectName, final AnyObjectId objectId)
+ throws IOException {
+ long sz;
+
+ sz = getObjectSize2(curs, objectName, objectId);
+ if (0 <= sz)
+ return sz;
+
+ for (final AlternateHandle alt : myAlternates()) {
+ sz = alt.db.getObjectSizeImpl2(curs, objectName, objectId);
+ if (0 <= sz)
+ return sz;
+ }
+
+ return -1;
+ }
+
+ abstract void selectObjectRepresentation(PackWriter packer,
+ ObjectToPack otp, WindowCursor curs) throws IOException;
+
+ abstract File getDirectory();
+
+ abstract AlternateHandle[] myAlternates();
+
+ abstract boolean tryAgain1();
+
+ abstract boolean hasObject1(AnyObjectId objectId);
+
+ abstract boolean hasObject2(String objectId);
+
+ abstract ObjectLoader openObject1(WindowCursor curs, AnyObjectId objectId)
+ throws IOException;
+
+ abstract ObjectLoader openObject2(WindowCursor curs, String objectName,
+ AnyObjectId objectId) throws IOException;
+
+ abstract long getObjectSize1(WindowCursor curs, AnyObjectId objectId)
+ throws IOException;
+
+ abstract long getObjectSize2(WindowCursor curs, String objectName,
+ AnyObjectId objectId) throws IOException;
+
+ abstract FileObjectDatabase newCachedFileObjectDatabase();
+
+ abstract int getStreamFileThreshold();
+
+ static class AlternateHandle {
+ final FileObjectDatabase db;
+
+ AlternateHandle(FileObjectDatabase db) {
+ this.db = db;
+ }
+
+ void close() {
+ db.close();
+ }
+ }
+
+ static class AlternateRepository extends AlternateHandle {
+ final FileRepository repository;
+
+ AlternateRepository(FileRepository r) {
+ super(r.getObjectDatabase());
+ repository = r;
+ }
+
+ void close() {
+ repository.close();
+ }
+ }
+}
diff --git a/org.eclipse.jgit/src/org/eclipse/jgit/storage/file/FileRepository.java b/org.eclipse.jgit/src/org/eclipse/jgit/storage/file/FileRepository.java
new file mode 100644
index 0000000000..15aafbdaeb
--- /dev/null
+++ b/org.eclipse.jgit/src/org/eclipse/jgit/storage/file/FileRepository.java
@@ -0,0 +1,328 @@
+/*
+ * Copyright (C) 2007, Dave Watson <dwatson@mimvista.com>
+ * Copyright (C) 2008-2010, Google Inc.
+ * Copyright (C) 2006-2010, Robin Rosenberg <robin.rosenberg@dewire.com>
+ * Copyright (C) 2006-2008, Shawn O. Pearce <spearce@spearce.org>
+ * and other copyright owners as documented in the project's IP log.
+ *
+ * This program and the accompanying materials are made available
+ * under the terms of the Eclipse Distribution License v1.0 which
+ * accompanies this distribution, is reproduced below, and is
+ * available at http://www.eclipse.org/org/documents/edl-v10.php
+ *
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or
+ * without modification, are permitted provided that the following
+ * conditions are met:
+ *
+ * - Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ *
+ * - Redistributions in binary form must reproduce the above
+ * copyright notice, this list of conditions and the following
+ * disclaimer in the documentation and/or other materials provided
+ * with the distribution.
+ *
+ * - Neither the name of the Eclipse Foundation, Inc. nor the
+ * names of its contributors may be used to endorse or promote
+ * products derived from this software without specific prior
+ * written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND
+ * CONTRIBUTORS "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES,
+ * INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
+ * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR
+ * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
+ * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT
+ * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
+ * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER
+ * CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT,
+ * STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
+ * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF
+ * ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ */
+
+package org.eclipse.jgit.storage.file;
+
+import java.io.File;
+import java.io.IOException;
+import java.text.MessageFormat;
+import java.util.HashSet;
+import java.util.Set;
+
+import org.eclipse.jgit.JGitText;
+import org.eclipse.jgit.errors.ConfigInvalidException;
+import org.eclipse.jgit.lib.BaseRepositoryBuilder;
+import org.eclipse.jgit.lib.ConfigConstants;
+import org.eclipse.jgit.lib.Constants;
+import org.eclipse.jgit.lib.ObjectId;
+import org.eclipse.jgit.lib.Ref;
+import org.eclipse.jgit.lib.RefDatabase;
+import org.eclipse.jgit.lib.RefUpdate;
+import org.eclipse.jgit.lib.Repository;
+import org.eclipse.jgit.storage.file.FileObjectDatabase.AlternateHandle;
+import org.eclipse.jgit.storage.file.FileObjectDatabase.AlternateRepository;
+import org.eclipse.jgit.util.SystemReader;
+
+/**
+ * Represents a Git repository. A repository holds all objects and refs used for
+ * managing source code (could by any type of file, but source code is what
+ * SCM's are typically used for).
+ *
+ * In Git terms all data is stored in GIT_DIR, typically a directory called
+ * .git. A work tree is maintained unless the repository is a bare repository.
+ * Typically the .git directory is located at the root of the work dir.
+ *
+ * <ul>
+ * <li>GIT_DIR
+ * <ul>
+ * <li>objects/ - objects</li>
+ * <li>refs/ - tags and heads</li>
+ * <li>config - configuration</li>
+ * <li>info/ - more configurations</li>
+ * </ul>
+ * </li>
+ * </ul>
+ * <p>
+ * This class is thread-safe.
+ * <p>
+ * This implementation only handles a subtly undocumented subset of git features.
+ *
+ */
+public class FileRepository extends Repository {
+ private final FileBasedConfig userConfig;
+
+ private final FileBasedConfig repoConfig;
+
+ private final RefDatabase refs;
+
+ private final ObjectDirectory objectDatabase;
+
+ /**
+ * Construct a representation of a Git repository.
+ * <p>
+ * The work tree, object directory, alternate object directories and index
+ * file locations are deduced from the given git directory and the default
+ * rules by running {@link FileRepositoryBuilder}. This constructor is the
+ * same as saying:
+ *
+ * <pre>
+ * new FileRepositoryBuilder().setGitDir(gitDir).build()
+ * </pre>
+ *
+ * @param gitDir
+ * GIT_DIR (the location of the repository metadata).
+ * @throws IOException
+ * the repository appears to already exist but cannot be
+ * accessed.
+ * @see FileRepositoryBuilder
+ */
+ public FileRepository(final File gitDir) throws IOException {
+ this(new FileRepositoryBuilder().setGitDir(gitDir).setup());
+ }
+
+ /**
+ * Create a repository using the local file system.
+ *
+ * @param options
+ * description of the repository's important paths.
+ * @throws IOException
+ * the user configuration file or repository configuration file
+ * cannot be accessed.
+ */
+ public FileRepository(final BaseRepositoryBuilder options) throws IOException {
+ super(options);
+
+ userConfig = SystemReader.getInstance().openUserConfig(getFS());
+ repoConfig = new FileBasedConfig(userConfig, getFS().resolve(getDirectory(), "config"));
+
+ loadUserConfig();
+ loadRepoConfig();
+
+ refs = new RefDirectory(this);
+ objectDatabase = new ObjectDirectory(repoConfig, //
+ options.getObjectDirectory(), //
+ options.getAlternateObjectDirectories(), //
+ getFS());
+ getListenerList().addConfigChangedListener(objectDatabase);
+
+ if (objectDatabase.exists()) {
+ final String repositoryFormatVersion = getConfig().getString(
+ ConfigConstants.CONFIG_CORE_SECTION, null,
+ ConfigConstants.CONFIG_KEY_REPO_FORMAT_VERSION);
+ if (!"0".equals(repositoryFormatVersion)) {
+ throw new IOException(MessageFormat.format(
+ JGitText.get().unknownRepositoryFormat2,
+ repositoryFormatVersion));
+ }
+ }
+ }
+
+ private void loadUserConfig() throws IOException {
+ try {
+ userConfig.load();
+ } catch (ConfigInvalidException e1) {
+ IOException e2 = new IOException(MessageFormat.format(JGitText
+ .get().userConfigFileInvalid, userConfig.getFile()
+ .getAbsolutePath(), e1));
+ e2.initCause(e1);
+ throw e2;
+ }
+ }
+
+ private void loadRepoConfig() throws IOException {
+ try {
+ repoConfig.load();
+ } catch (ConfigInvalidException e1) {
+ IOException e2 = new IOException(JGitText.get().unknownRepositoryFormat);
+ e2.initCause(e1);
+ throw e2;
+ }
+ }
+
+ /**
+ * Create a new Git repository initializing the necessary files and
+ * directories.
+ *
+ * @param bare
+ * if true, a bare repository is created.
+ *
+ * @throws IOException
+ * in case of IO problem
+ */
+ public void create(boolean bare) throws IOException {
+ final FileBasedConfig cfg = getConfig();
+ if (cfg.getFile().exists()) {
+ throw new IllegalStateException(MessageFormat.format(
+ JGitText.get().repositoryAlreadyExists, getDirectory()));
+ }
+ getDirectory().mkdirs();
+ refs.create();
+ objectDatabase.create();
+
+ new File(getDirectory(), "branches").mkdir();
+
+ RefUpdate head = updateRef(Constants.HEAD);
+ head.disableRefLog();
+ head.link(Constants.R_HEADS + Constants.MASTER);
+
+ cfg.setInt(ConfigConstants.CONFIG_CORE_SECTION, null,
+ ConfigConstants.CONFIG_KEY_REPO_FORMAT_VERSION, 0);
+ cfg.setBoolean(ConfigConstants.CONFIG_CORE_SECTION, null,
+ ConfigConstants.CONFIG_KEY_FILEMODE, true);
+ if (bare)
+ cfg.setBoolean(ConfigConstants.CONFIG_CORE_SECTION, null,
+ ConfigConstants.CONFIG_KEY_BARE, true);
+ cfg.setBoolean(ConfigConstants.CONFIG_CORE_SECTION, null,
+ ConfigConstants.CONFIG_KEY_LOGALLREFUPDATES, !bare);
+ cfg.setBoolean(ConfigConstants.CONFIG_CORE_SECTION, null,
+ ConfigConstants.CONFIG_KEY_AUTOCRLF, false);
+ cfg.save();
+ }
+
+ /**
+ * @return the directory containing the objects owned by this repository.
+ */
+ public File getObjectsDirectory() {
+ return objectDatabase.getDirectory();
+ }
+
+ /**
+ * @return the object database which stores this repository's data.
+ */
+ public ObjectDirectory getObjectDatabase() {
+ return objectDatabase;
+ }
+
+ /** @return the reference database which stores the reference namespace. */
+ public RefDatabase getRefDatabase() {
+ return refs;
+ }
+
+ /**
+ * @return the configuration of this repository
+ */
+ public FileBasedConfig getConfig() {
+ if (userConfig.isOutdated()) {
+ try {
+ loadUserConfig();
+ } catch (IOException e) {
+ throw new RuntimeException(e);
+ }
+ }
+ if (repoConfig.isOutdated()) {
+ try {
+ loadRepoConfig();
+ } catch (IOException e) {
+ throw new RuntimeException(e);
+ }
+ }
+ return repoConfig;
+ }
+
+ /**
+ * Objects known to exist but not expressed by {@link #getAllRefs()}.
+ * <p>
+ * When a repository borrows objects from another repository, it can
+ * advertise that it safely has that other repository's references, without
+ * exposing any other details about the other repository. This may help
+ * a client trying to push changes avoid pushing more than it needs to.
+ *
+ * @return unmodifiable collection of other known objects.
+ */
+ public Set<ObjectId> getAdditionalHaves() {
+ HashSet<ObjectId> r = new HashSet<ObjectId>();
+ for (AlternateHandle d : objectDatabase. myAlternates()) {
+ if (d instanceof AlternateRepository) {
+ Repository repo;
+
+ repo = ((AlternateRepository) d).repository;
+ for (Ref ref : repo.getAllRefs().values())
+ r.add(ref.getObjectId());
+ r.addAll(repo.getAdditionalHaves());
+ }
+ }
+ return r;
+ }
+
+ /**
+ * Add a single existing pack to the list of available pack files.
+ *
+ * @param pack
+ * path of the pack file to open.
+ * @param idx
+ * path of the corresponding index file.
+ * @throws IOException
+ * index file could not be opened, read, or is not recognized as
+ * a Git pack file index.
+ */
+ public void openPack(final File pack, final File idx) throws IOException {
+ objectDatabase.openPack(pack, idx);
+ }
+
+ /**
+ * Force a scan for changed refs.
+ *
+ * @throws IOException
+ */
+ public void scanForRepoChanges() throws IOException {
+ getAllRefs(); // This will look for changes to refs
+ if (!isBare())
+ getIndex(); // This will detect changes in the index
+ }
+
+ /**
+ * @param refName
+ * @return a {@link ReflogReader} for the supplied refname, or null if the
+ * named ref does not exist.
+ * @throws IOException the ref could not be accessed.
+ */
+ public ReflogReader getReflogReader(String refName) throws IOException {
+ Ref ref = getRef(refName);
+ if (ref != null)
+ return new ReflogReader(this, ref.getName());
+ return null;
+ }
+}
diff --git a/org.eclipse.jgit/src/org/eclipse/jgit/lib/DeltaOfsPackedObjectLoader.java b/org.eclipse.jgit/src/org/eclipse/jgit/storage/file/FileRepositoryBuilder.java
index d0e98a2a92..31d3e99ad6 100644
--- a/org.eclipse.jgit/src/org/eclipse/jgit/lib/DeltaOfsPackedObjectLoader.java
+++ b/org.eclipse.jgit/src/org/eclipse/jgit/storage/file/FileRepositoryBuilder.java
@@ -1,8 +1,5 @@
/*
- * Copyright (C) 2009, Google Inc.
- * Copyright (C) 2008, Marek Zawirski <marek.zawirski@gmail.com>
- * Copyright (C) 2007, Robin Rosenberg <robin.rosenberg@dewire.com>
- * Copyright (C) 2006-2008, Shawn O. Pearce <spearce@spearce.org>
+ * Copyright (C) 2010, Google Inc.
* and other copyright owners as documented in the project's IP log.
*
* This program and the accompanying materials are made available
@@ -44,39 +41,50 @@
* ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
*/
-package org.eclipse.jgit.lib;
+package org.eclipse.jgit.storage.file;
+import java.io.File;
import java.io.IOException;
-import org.eclipse.jgit.JGitText;
-import org.eclipse.jgit.errors.CorruptObjectException;
-
-/** Reads a deltified object which uses an offset to find its base. */
-class DeltaOfsPackedObjectLoader extends DeltaPackedObjectLoader {
- private final long deltaBase;
-
- DeltaOfsPackedObjectLoader(final PackFile pr, final long objectOffset,
- final int headerSz, final int deltaSz, final long base) {
- super(pr, objectOffset, headerSz, deltaSz);
- deltaBase = base;
- }
-
- protected PackedObjectLoader getBaseLoader(final WindowCursor curs)
- throws IOException {
- return pack.resolveBase(curs, deltaBase);
- }
-
- @Override
- public int getRawType() {
- return Constants.OBJ_OFS_DELTA;
- }
+import org.eclipse.jgit.lib.BaseRepositoryBuilder;
+/**
+ * Constructs a {@link FileRepository}.
+ * <p>
+ * Applications must set one of {@link #setGitDir(File)} or
+ * {@link #setWorkTree(File)}, or use {@link #readEnvironment()} or
+ * {@link #findGitDir()} in order to configure the minimum property set
+ * necessary to open a repository.
+ * <p>
+ * Single repository applications trying to be compatible with other Git
+ * implementations are encouraged to use a model such as:
+ *
+ * <pre>
+ * new FileRepositoryBuilder() //
+ * .setGitDir(gitDirArgument) // --git-dir if supplied, no-op if null
+ * .readEnviroment() // scan environment GIT_* variables
+ * .findGitDir() // scan up the file system tree
+ * .build()
+ * </pre>
+ */
+public class FileRepositoryBuilder extends
+ BaseRepositoryBuilder<FileRepositoryBuilder, FileRepository> {
+ /**
+ * Create a repository matching the configuration in this builder.
+ * <p>
+ * If an option was not set, the build method will try to default the option
+ * based on other options. If insufficient information is available, an
+ * exception is thrown to the caller.
+ *
+ * @return a repository matching this configuration.
+ * @throws IllegalArgumentException
+ * insufficient parameters were set.
+ * @throws IOException
+ * the repository could not be accessed to configure the rest of
+ * the builder's parameters.
+ */
@Override
- public ObjectId getDeltaBase() throws IOException {
- final ObjectId id = pack.findObjectForOffset(deltaBase);
- if (id == null)
- throw new CorruptObjectException(
- JGitText.get().offsetWrittenDeltaBaseForObjectNotFoundInAPack);
- return id;
+ public FileRepository build() throws IOException {
+ return new FileRepository(setup());
}
}
diff --git a/org.eclipse.jgit/src/org/eclipse/jgit/storage/file/LargePackedDeltaObject.java b/org.eclipse.jgit/src/org/eclipse/jgit/storage/file/LargePackedDeltaObject.java
new file mode 100644
index 0000000000..53a0e617fc
--- /dev/null
+++ b/org.eclipse.jgit/src/org/eclipse/jgit/storage/file/LargePackedDeltaObject.java
@@ -0,0 +1,230 @@
+/*
+ * Copyright (C) 2010, Google Inc.
+ * and other copyright owners as documented in the project's IP log.
+ *
+ * This program and the accompanying materials are made available
+ * under the terms of the Eclipse Distribution License v1.0 which
+ * accompanies this distribution, is reproduced below, and is
+ * available at http://www.eclipse.org/org/documents/edl-v10.php
+ *
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or
+ * without modification, are permitted provided that the following
+ * conditions are met:
+ *
+ * - Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ *
+ * - Redistributions in binary form must reproduce the above
+ * copyright notice, this list of conditions and the following
+ * disclaimer in the documentation and/or other materials provided
+ * with the distribution.
+ *
+ * - Neither the name of the Eclipse Foundation, Inc. nor the
+ * names of its contributors may be used to endorse or promote
+ * products derived from this software without specific prior
+ * written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND
+ * CONTRIBUTORS "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES,
+ * INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
+ * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR
+ * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
+ * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT
+ * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
+ * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER
+ * CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT,
+ * STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
+ * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF
+ * ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ */
+
+package org.eclipse.jgit.storage.file;
+
+import java.io.BufferedInputStream;
+import java.io.IOException;
+import java.io.InputStream;
+import java.util.zip.DataFormatException;
+import java.util.zip.InflaterInputStream;
+
+import org.eclipse.jgit.errors.IncorrectObjectTypeException;
+import org.eclipse.jgit.errors.LargeObjectException;
+import org.eclipse.jgit.errors.MissingObjectException;
+import org.eclipse.jgit.lib.Constants;
+import org.eclipse.jgit.lib.ObjectId;
+import org.eclipse.jgit.lib.ObjectLoader;
+import org.eclipse.jgit.lib.ObjectStream;
+import org.eclipse.jgit.storage.pack.BinaryDelta;
+import org.eclipse.jgit.storage.pack.DeltaStream;
+
+class LargePackedDeltaObject extends ObjectLoader {
+ private static final long SIZE_UNKNOWN = -1;
+
+ private int type;
+
+ private long size;
+
+ private final long objectOffset;
+
+ private final long baseOffset;
+
+ private final int headerLength;
+
+ private final PackFile pack;
+
+ private final FileObjectDatabase db;
+
+ LargePackedDeltaObject(long objectOffset,
+ long baseOffset, int headerLength, PackFile pack,
+ FileObjectDatabase db) {
+ this.type = Constants.OBJ_BAD;
+ this.size = SIZE_UNKNOWN;
+ this.objectOffset = objectOffset;
+ this.baseOffset = baseOffset;
+ this.headerLength = headerLength;
+ this.pack = pack;
+ this.db = db;
+ }
+
+ @Override
+ public int getType() {
+ if (type == Constants.OBJ_BAD) {
+ WindowCursor wc = new WindowCursor(db);
+ try {
+ type = pack.getObjectType(wc, objectOffset);
+ } catch (IOException packGone) {
+ // If the pack file cannot be pinned into the cursor, it
+ // probably was repacked recently. Go find the object
+ // again and get the type from that location instead.
+ //
+ try {
+ type = wc.open(getObjectId()).getType();
+ } catch (IOException packGone2) {
+ // "He's dead, Jim." We just can't discover the type
+ // and the interface isn't supposed to be lazy here.
+ // Report an invalid type code instead, callers will
+ // wind up bailing out with an error at some point.
+ }
+ } finally {
+ wc.release();
+ }
+ }
+ return type;
+ }
+
+ @Override
+ public long getSize() {
+ if (size == SIZE_UNKNOWN) {
+ WindowCursor wc = new WindowCursor(db);
+ try {
+ byte[] b = pack.getDeltaHeader(wc, objectOffset + headerLength);
+ size = BinaryDelta.getResultSize(b);
+ } catch (DataFormatException objectCorrupt) {
+ // The zlib stream for the delta is corrupt. We probably
+ // cannot access the object. Keep the size negative and
+ // report that bogus result to the caller.
+ } catch (IOException packGone) {
+ // If the pack file cannot be pinned into the cursor, it
+ // probably was repacked recently. Go find the object
+ // again and get the size from that location instead.
+ //
+ try {
+ size = wc.open(getObjectId()).getSize();
+ } catch (IOException packGone2) {
+ // "He's dead, Jim." We just can't discover the size
+ // and the interface isn't supposed to be lazy here.
+ // Report an invalid type code instead, callers will
+ // wind up bailing out with an error at some point.
+ }
+ } finally {
+ wc.release();
+ }
+ }
+ return size;
+ }
+
+ @Override
+ public boolean isLarge() {
+ return true;
+ }
+
+ @Override
+ public byte[] getCachedBytes() throws LargeObjectException {
+ try {
+ throw new LargeObjectException(getObjectId());
+ } catch (IOException cannotObtainId) {
+ throw new LargeObjectException();
+ }
+ }
+
+ @Override
+ public ObjectStream openStream() throws MissingObjectException, IOException {
+ final WindowCursor wc = new WindowCursor(db);
+ InputStream in = open(wc);
+ in = new BufferedInputStream(in, 8192);
+ return new ObjectStream.Filter(getType(), size, in) {
+ @Override
+ public void close() throws IOException {
+ wc.release();
+ super.close();
+ }
+ };
+ }
+
+ private InputStream open(final WindowCursor wc)
+ throws MissingObjectException, IOException,
+ IncorrectObjectTypeException {
+ InputStream delta;
+ try {
+ delta = new PackInputStream(pack, objectOffset + headerLength, wc);
+ } catch (IOException packGone) {
+ // If the pack file cannot be pinned into the cursor, it
+ // probably was repacked recently. Go find the object
+ // again and open the stream from that location instead.
+ //
+ return wc.open(getObjectId()).openStream();
+ }
+ delta = new InflaterInputStream(delta);
+
+ final ObjectLoader base = pack.load(wc, baseOffset);
+ DeltaStream ds = new DeltaStream(delta) {
+ private long baseSize = SIZE_UNKNOWN;
+
+ @Override
+ protected InputStream openBase() throws IOException {
+ InputStream in;
+ if (base instanceof LargePackedDeltaObject)
+ in = ((LargePackedDeltaObject) base).open(wc);
+ else
+ in = base.openStream();
+ if (baseSize == SIZE_UNKNOWN) {
+ if (in instanceof DeltaStream)
+ baseSize = ((DeltaStream) in).getSize();
+ else if (in instanceof ObjectStream)
+ baseSize = ((ObjectStream) in).getSize();
+ }
+ return in;
+ }
+
+ @Override
+ protected long getBaseSize() throws IOException {
+ if (baseSize == SIZE_UNKNOWN) {
+ // This code path should never be used as DeltaStream
+ // is supposed to open the stream first, which would
+ // initialize the size for us directly from the stream.
+ baseSize = base.getSize();
+ }
+ return baseSize;
+ }
+ };
+ if (size == SIZE_UNKNOWN)
+ size = ds.getSize();
+ return ds;
+ }
+
+ private ObjectId getObjectId() throws IOException {
+ return pack.findObjectForOffset(objectOffset);
+ }
+}
diff --git a/org.eclipse.jgit/src/org/eclipse/jgit/storage/file/LargePackedWholeObject.java b/org.eclipse.jgit/src/org/eclipse/jgit/storage/file/LargePackedWholeObject.java
new file mode 100644
index 0000000000..9f5b804ce4
--- /dev/null
+++ b/org.eclipse.jgit/src/org/eclipse/jgit/storage/file/LargePackedWholeObject.java
@@ -0,0 +1,130 @@
+/*
+ * Copyright (C) 2010, Google Inc.
+ * and other copyright owners as documented in the project's IP log.
+ *
+ * This program and the accompanying materials are made available
+ * under the terms of the Eclipse Distribution License v1.0 which
+ * accompanies this distribution, is reproduced below, and is
+ * available at http://www.eclipse.org/org/documents/edl-v10.php
+ *
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or
+ * without modification, are permitted provided that the following
+ * conditions are met:
+ *
+ * - Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ *
+ * - Redistributions in binary form must reproduce the above
+ * copyright notice, this list of conditions and the following
+ * disclaimer in the documentation and/or other materials provided
+ * with the distribution.
+ *
+ * - Neither the name of the Eclipse Foundation, Inc. nor the
+ * names of its contributors may be used to endorse or promote
+ * products derived from this software without specific prior
+ * written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND
+ * CONTRIBUTORS "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES,
+ * INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
+ * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR
+ * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
+ * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT
+ * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
+ * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER
+ * CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT,
+ * STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
+ * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF
+ * ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ */
+
+package org.eclipse.jgit.storage.file;
+
+import java.io.BufferedInputStream;
+import java.io.IOException;
+import java.io.InputStream;
+import java.util.zip.InflaterInputStream;
+
+import org.eclipse.jgit.errors.LargeObjectException;
+import org.eclipse.jgit.errors.MissingObjectException;
+import org.eclipse.jgit.lib.ObjectId;
+import org.eclipse.jgit.lib.ObjectLoader;
+import org.eclipse.jgit.lib.ObjectStream;
+
+class LargePackedWholeObject extends ObjectLoader {
+ private final int type;
+
+ private final long size;
+
+ private final long objectOffset;
+
+ private final int headerLength;
+
+ private final PackFile pack;
+
+ private final FileObjectDatabase db;
+
+ LargePackedWholeObject(int type, long size, long objectOffset,
+ int headerLength, PackFile pack, FileObjectDatabase db) {
+ this.type = type;
+ this.size = size;
+ this.objectOffset = objectOffset;
+ this.headerLength = headerLength;
+ this.pack = pack;
+ this.db = db;
+ }
+
+ @Override
+ public int getType() {
+ return type;
+ }
+
+ @Override
+ public long getSize() {
+ return size;
+ }
+
+ @Override
+ public boolean isLarge() {
+ return true;
+ }
+
+ @Override
+ public byte[] getCachedBytes() throws LargeObjectException {
+ try {
+ throw new LargeObjectException(getObjectId());
+ } catch (IOException cannotObtainId) {
+ throw new LargeObjectException();
+ }
+ }
+
+ @Override
+ public ObjectStream openStream() throws MissingObjectException, IOException {
+ WindowCursor wc = new WindowCursor(db);
+ InputStream in;
+ try {
+ in = new PackInputStream(pack, objectOffset + headerLength, wc);
+ } catch (IOException packGone) {
+ // If the pack file cannot be pinned into the cursor, it
+ // probably was repacked recently. Go find the object
+ // again and open the stream from that location instead.
+ //
+ return wc.open(getObjectId(), type).openStream();
+ }
+
+ in = new BufferedInputStream( //
+ new InflaterInputStream( //
+ in, //
+ wc.inflater(), //
+ 8192), //
+ 8192);
+ return new ObjectStream.Filter(type, size, in);
+ }
+
+ private ObjectId getObjectId() throws IOException {
+ return pack.findObjectForOffset(objectOffset);
+ }
+}
diff --git a/org.eclipse.jgit/src/org/eclipse/jgit/lib/PackOutputStream.java b/org.eclipse.jgit/src/org/eclipse/jgit/storage/file/LocalObjectRepresentation.java
index a348f1e547..08bb8e60d5 100644
--- a/org.eclipse.jgit/src/org/eclipse/jgit/lib/PackOutputStream.java
+++ b/org.eclipse.jgit/src/org/eclipse/jgit/storage/file/LocalObjectRepresentation.java
@@ -1,6 +1,5 @@
/*
- * Copyright (C) 2008-2009, Google Inc.
- * Copyright (C) 2008, Marek Zawirski <marek.zawirski@gmail.com>
+ * Copyright (C) 2010, Google Inc.
* and other copyright owners as documented in the project's IP log.
*
* This program and the accompanying materials are made available
@@ -42,66 +41,78 @@
* ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
*/
-package org.eclipse.jgit.lib;
+package org.eclipse.jgit.storage.file;
import java.io.IOException;
-import java.io.OutputStream;
-import java.security.MessageDigest;
-import java.util.zip.CRC32;
-/** Custom output stream to support {@link PackWriter}. */
-final class PackOutputStream extends OutputStream {
- private final OutputStream out;
+import org.eclipse.jgit.lib.ObjectId;
+import org.eclipse.jgit.storage.pack.StoredObjectRepresentation;
- private final CRC32 crc = new CRC32();
-
- private final MessageDigest md = Constants.newMessageDigest();
-
- private long count;
-
- PackOutputStream(final OutputStream out) {
- this.out = out;
+class LocalObjectRepresentation extends StoredObjectRepresentation {
+ static LocalObjectRepresentation newWhole(PackFile f, long p, long length) {
+ LocalObjectRepresentation r = new LocalObjectRepresentation() {
+ @Override
+ public int getFormat() {
+ return PACK_WHOLE;
+ }
+ };
+ r.pack = f;
+ r.offset = p;
+ r.length = length;
+ return r;
}
- @Override
- public void write(final int b) throws IOException {
- out.write(b);
- crc.update(b);
- md.update((byte) b);
- count++;
+ static LocalObjectRepresentation newDelta(PackFile f, long p, long n,
+ ObjectId base) {
+ LocalObjectRepresentation r = new Delta();
+ r.pack = f;
+ r.offset = p;
+ r.length = n;
+ r.baseId = base;
+ return r;
}
- @Override
- public void write(final byte[] b, final int off, final int len)
- throws IOException {
- out.write(b, off, len);
- crc.update(b, off, len);
- md.update(b, off, len);
- count += len;
+ static LocalObjectRepresentation newDelta(PackFile f, long p, long n,
+ long base) {
+ LocalObjectRepresentation r = new Delta();
+ r.pack = f;
+ r.offset = p;
+ r.length = n;
+ r.baseOffset = base;
+ return r;
}
- @Override
- public void flush() throws IOException {
- out.flush();
- }
+ PackFile pack;
- /** @return total number of bytes written since stream start. */
- long length() {
- return count;
- }
+ long offset;
+
+ long length;
+
+ private long baseOffset;
- /** @return obtain the current CRC32 register. */
- int getCRC32() {
- return (int) crc.getValue();
+ private ObjectId baseId;
+
+ @Override
+ public int getWeight() {
+ return (int) Math.min(length, Integer.MAX_VALUE);
}
- /** Reinitialize the CRC32 register for a new region. */
- void resetCRC32() {
- crc.reset();
+ @Override
+ public ObjectId getDeltaBase() {
+ if (baseId == null && getFormat() == PACK_DELTA) {
+ try {
+ baseId = pack.findObjectForOffset(baseOffset);
+ } catch (IOException error) {
+ return null;
+ }
+ }
+ return baseId;
}
- /** @return obtain the current SHA-1 digest. */
- byte[] getDigest() {
- return md.digest();
+ private static final class Delta extends LocalObjectRepresentation {
+ @Override
+ public int getFormat() {
+ return PACK_DELTA;
+ }
}
}
diff --git a/org.eclipse.jgit/src/org/eclipse/jgit/storage/file/LocalObjectToPack.java b/org.eclipse.jgit/src/org/eclipse/jgit/storage/file/LocalObjectToPack.java
new file mode 100644
index 0000000000..c7ef2c9133
--- /dev/null
+++ b/org.eclipse.jgit/src/org/eclipse/jgit/storage/file/LocalObjectToPack.java
@@ -0,0 +1,78 @@
+/*
+ * Copyright (C) 2010, Google Inc.
+ * and other copyright owners as documented in the project's IP log.
+ *
+ * This program and the accompanying materials are made available
+ * under the terms of the Eclipse Distribution License v1.0 which
+ * accompanies this distribution, is reproduced below, and is
+ * available at http://www.eclipse.org/org/documents/edl-v10.php
+ *
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or
+ * without modification, are permitted provided that the following
+ * conditions are met:
+ *
+ * - Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ *
+ * - Redistributions in binary form must reproduce the above
+ * copyright notice, this list of conditions and the following
+ * disclaimer in the documentation and/or other materials provided
+ * with the distribution.
+ *
+ * - Neither the name of the Eclipse Foundation, Inc. nor the
+ * names of its contributors may be used to endorse or promote
+ * products derived from this software without specific prior
+ * written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND
+ * CONTRIBUTORS "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES,
+ * INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
+ * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR
+ * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
+ * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT
+ * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
+ * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER
+ * CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT,
+ * STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
+ * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF
+ * ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ */
+
+package org.eclipse.jgit.storage.file;
+
+import org.eclipse.jgit.revwalk.RevObject;
+import org.eclipse.jgit.storage.pack.ObjectToPack;
+import org.eclipse.jgit.storage.pack.StoredObjectRepresentation;
+
+/** {@link ObjectToPack} for {@link ObjectDirectory}. */
+class LocalObjectToPack extends ObjectToPack {
+ /** Pack to reuse compressed data from, otherwise null. */
+ PackFile pack;
+
+ /** Offset of the object's header in {@link #pack}. */
+ long offset;
+
+ /** Length of the data section of the object. */
+ long length;
+
+ LocalObjectToPack(RevObject obj) {
+ super(obj);
+ }
+
+ @Override
+ protected void clearReuseAsIs() {
+ super.clearReuseAsIs();
+ pack = null;
+ }
+
+ @Override
+ public void select(StoredObjectRepresentation ref) {
+ LocalObjectRepresentation ptr = (LocalObjectRepresentation) ref;
+ this.pack = ptr.pack;
+ this.offset = ptr.offset;
+ this.length = ptr.length;
+ }
+}
diff --git a/org.eclipse.jgit/src/org/eclipse/jgit/lib/LockFile.java b/org.eclipse.jgit/src/org/eclipse/jgit/storage/file/LockFile.java
index 13f158dedf..ad89a24847 100644
--- a/org.eclipse.jgit/src/org/eclipse/jgit/lib/LockFile.java
+++ b/org.eclipse.jgit/src/org/eclipse/jgit/storage/file/LockFile.java
@@ -42,7 +42,7 @@
* ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
*/
-package org.eclipse.jgit.lib;
+package org.eclipse.jgit.storage.file;
import java.io.BufferedOutputStream;
import java.io.File;
@@ -57,6 +57,8 @@ import java.nio.channels.OverlappingFileLockException;
import java.text.MessageFormat;
import org.eclipse.jgit.JGitText;
+import org.eclipse.jgit.lib.Constants;
+import org.eclipse.jgit.lib.ObjectId;
/**
* Git style file locking and replacement.
diff --git a/org.eclipse.jgit/src/org/eclipse/jgit/lib/ObjectDirectory.java b/org.eclipse.jgit/src/org/eclipse/jgit/storage/file/ObjectDirectory.java
index 9a5bcdb68c..6fe4fd754e 100644
--- a/org.eclipse.jgit/src/org/eclipse/jgit/lib/ObjectDirectory.java
+++ b/org.eclipse.jgit/src/org/eclipse/jgit/storage/file/ObjectDirectory.java
@@ -41,10 +41,11 @@
* ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
*/
-package org.eclipse.jgit.lib;
+package org.eclipse.jgit.storage.file;
import java.io.BufferedReader;
import java.io.File;
+import java.io.FileInputStream;
import java.io.FileNotFoundException;
import java.io.FileReader;
import java.io.IOException;
@@ -62,7 +63,19 @@ import java.util.concurrent.atomic.AtomicReference;
import org.eclipse.jgit.JGitText;
import org.eclipse.jgit.errors.PackMismatchException;
+import org.eclipse.jgit.events.ConfigChangedEvent;
+import org.eclipse.jgit.events.ConfigChangedListener;
+import org.eclipse.jgit.lib.AnyObjectId;
+import org.eclipse.jgit.lib.Config;
+import org.eclipse.jgit.lib.CoreConfig;
+import org.eclipse.jgit.lib.ObjectDatabase;
+import org.eclipse.jgit.lib.ObjectId;
+import org.eclipse.jgit.lib.ObjectInserter;
+import org.eclipse.jgit.lib.ObjectLoader;
+import org.eclipse.jgit.lib.RepositoryCache;
import org.eclipse.jgit.lib.RepositoryCache.FileKey;
+import org.eclipse.jgit.storage.pack.ObjectToPack;
+import org.eclipse.jgit.storage.pack.PackWriter;
import org.eclipse.jgit.util.FS;
/**
@@ -72,10 +85,23 @@ import org.eclipse.jgit.util.FS;
* where objects are stored loose by hashing them into directories by their
* {@link ObjectId}, or are stored in compressed containers known as
* {@link PackFile}s.
+ * <p>
+ * Optionally an object database can reference one or more alternates; other
+ * ObjectDatabase instances that are searched in addition to the current
+ * database.
+ * <p>
+ * Databases are divided into two halves: a half that is considered to be fast
+ * to search (the {@code PackFile}s), and a half that is considered to be slow
+ * to search (loose objects). When alternates are present the fast half is fully
+ * searched (recursively through all alternates) before the slow half is
+ * considered.
*/
-public class ObjectDirectory extends ObjectDatabase {
+public class ObjectDirectory extends FileObjectDatabase implements
+ ConfigChangedListener {
private static final PackList NO_PACKS = new PackList(-1, -1, new PackFile[0]);
+ private final Config config;
+
private final File objects;
private final File infoDirectory;
@@ -86,29 +112,53 @@ public class ObjectDirectory extends ObjectDatabase {
private final AtomicReference<PackList> packList;
- private final File[] alternateObjectDir;
-
private final FS fs;
+ private final AtomicReference<AlternateHandle[]> alternates;
+
+ private int streamFileThreshold;
+
/**
* Initialize a reference to an on-disk object directory.
*
+ * @param cfg
+ * configuration this directory consults for write settings.
* @param dir
* the location of the <code>objects</code> directory.
- * @param alternateObjectDir
+ * @param alternatePaths
* a list of alternate object directories
* @param fs
- * the file system abstraction which will be necessary to
- * perform certain file system operations.
+ * the file system abstraction which will be necessary to perform
+ * certain file system operations.
+ * @throws IOException
+ * an alternate object cannot be opened.
*/
- public ObjectDirectory(final File dir, File[] alternateObjectDir, FS fs) {
+ public ObjectDirectory(final Config cfg, final File dir,
+ File[] alternatePaths, FS fs) throws IOException {
+ config = cfg;
objects = dir;
- this.alternateObjectDir = alternateObjectDir;
infoDirectory = new File(objects, "info");
packDirectory = new File(objects, "pack");
alternatesFile = new File(infoDirectory, "alternates");
packList = new AtomicReference<PackList>(NO_PACKS);
this.fs = fs;
+
+ alternates = new AtomicReference<AlternateHandle[]>();
+ if (alternatePaths != null) {
+ AlternateHandle[] alt;
+
+ alt = new AlternateHandle[alternatePaths.length];
+ for (int i = 0; i < alternatePaths.length; i++)
+ alt[i] = openAlternate(alternatePaths[i]);
+ alternates.set(alt);
+ }
+
+ onConfigChanged(new ConfigChangedEvent());
+ }
+
+ public void onConfigChanged(ConfigChangedEvent event) {
+ CoreConfig core = config.get(CoreConfig.KEY);
+ streamFileThreshold = core.getStreamFileThreshold();
}
/**
@@ -131,11 +181,24 @@ public class ObjectDirectory extends ObjectDatabase {
}
@Override
- public void closeSelf() {
+ public ObjectInserter newInserter() {
+ return new ObjectDirectoryInserter(this, config);
+ }
+
+ @Override
+ public void close() {
final PackList packs = packList.get();
packList.set(NO_PACKS);
for (final PackFile p : packs.packs)
p.close();
+
+ // Fully close all loaded alternates and clear the alternate list.
+ AlternateHandle[] alt = alternates.get();
+ if (alt != null) {
+ alternates.set(null);
+ for(final AlternateHandle od : alt)
+ od.close();
+ }
}
/**
@@ -199,8 +262,7 @@ public class ObjectDirectory extends ObjectDatabase {
return "ObjectDirectory[" + getDirectory() + "]";
}
- @Override
- protected boolean hasObject1(final AnyObjectId objectId) {
+ boolean hasObject1(final AnyObjectId objectId) {
for (final PackFile p : packList.get().packs) {
try {
if (p.hasObject(objectId)) {
@@ -218,18 +280,15 @@ public class ObjectDirectory extends ObjectDatabase {
return false;
}
- @Override
- protected ObjectLoader openObject1(final WindowCursor curs,
+ ObjectLoader openObject1(final WindowCursor curs,
final AnyObjectId objectId) throws IOException {
PackList pList = packList.get();
SEARCH: for (;;) {
for (final PackFile p : pList.packs) {
try {
- final PackedObjectLoader ldr = p.get(curs, objectId);
- if (ldr != null) {
- ldr.materialize(curs);
+ final ObjectLoader ldr = p.get(curs, objectId);
+ if (ldr != null)
return ldr;
- }
} catch (PackMismatchException e) {
// Pack was modified; refresh the entire pack list.
//
@@ -245,18 +304,15 @@ public class ObjectDirectory extends ObjectDatabase {
}
}
- @Override
- void openObjectInAllPacks1(final Collection<PackedObjectLoader> out,
- final WindowCursor curs, final AnyObjectId objectId)
+ long getObjectSize1(final WindowCursor curs, final AnyObjectId objectId)
throws IOException {
PackList pList = packList.get();
SEARCH: for (;;) {
for (final PackFile p : pList.packs) {
try {
- final PackedObjectLoader ldr = p.get(curs, objectId);
- if (ldr != null) {
- out.add(ldr);
- }
+ long sz = p.getObjectSize(curs, objectId);
+ if (0 <= sz)
+ return sz;
} catch (PackMismatchException e) {
// Pack was modified; refresh the entire pack list.
//
@@ -268,28 +324,75 @@ public class ObjectDirectory extends ObjectDatabase {
removePack(p);
}
}
- break SEARCH;
+ return -1;
}
}
@Override
- protected boolean hasObject2(final String objectName) {
- return fileFor(objectName).exists();
+ long getObjectSize2(WindowCursor curs, String objectName,
+ AnyObjectId objectId) throws IOException {
+ try {
+ File path = fileFor(objectName);
+ FileInputStream in = new FileInputStream(path);
+ try {
+ return UnpackedObject.getSize(in, objectId, curs);
+ } finally {
+ in.close();
+ }
+ } catch (FileNotFoundException noFile) {
+ return -1;
+ }
}
@Override
- protected ObjectLoader openObject2(final WindowCursor curs,
+ void selectObjectRepresentation(PackWriter packer, ObjectToPack otp,
+ WindowCursor curs) throws IOException {
+ PackList pList = packList.get();
+ SEARCH: for (;;) {
+ for (final PackFile p : pList.packs) {
+ try {
+ LocalObjectRepresentation rep = p.representation(curs, otp);
+ if (rep != null)
+ packer.select(otp, rep);
+ } catch (PackMismatchException e) {
+ // Pack was modified; refresh the entire pack list.
+ //
+ pList = scanPacks(pList);
+ continue SEARCH;
+ } catch (IOException e) {
+ // Assume the pack is corrupted.
+ //
+ removePack(p);
+ }
+ }
+ break SEARCH;
+ }
+
+ for (AlternateHandle h : myAlternates())
+ h.db.selectObjectRepresentation(packer, otp, curs);
+ }
+
+ boolean hasObject2(final String objectName) {
+ return fileFor(objectName).exists();
+ }
+
+ ObjectLoader openObject2(final WindowCursor curs,
final String objectName, final AnyObjectId objectId)
throws IOException {
try {
- return new UnpackedObjectLoader(fileFor(objectName), objectId);
+ File path = fileFor(objectName);
+ FileInputStream in = new FileInputStream(path);
+ try {
+ return UnpackedObject.open(in, path, objectId, curs);
+ } finally {
+ in.close();
+ }
} catch (FileNotFoundException noFile) {
return null;
}
}
- @Override
- protected boolean tryAgain1() {
+ boolean tryAgain1() {
final PackList old = packList.get();
if (old.tryAgain(packDirectory.lastModified()))
return old != scanPacks(old);
@@ -459,29 +562,36 @@ public class ObjectDirectory extends ObjectDatabase {
return nameSet;
}
- @Override
- protected ObjectDatabase[] loadAlternates() throws IOException {
- final List<ObjectDatabase> l = new ArrayList<ObjectDatabase>(4);
- if (alternateObjectDir != null) {
- for (File d : alternateObjectDir) {
- l.add(openAlternate(d));
- }
- } else {
- final BufferedReader br = open(alternatesFile);
- try {
- String line;
- while ((line = br.readLine()) != null) {
- l.add(openAlternate(line));
+ AlternateHandle[] myAlternates() {
+ AlternateHandle[] alt = alternates.get();
+ if (alt == null) {
+ synchronized (alternates) {
+ alt = alternates.get();
+ if (alt == null) {
+ try {
+ alt = loadAlternates();
+ } catch (IOException e) {
+ alt = new AlternateHandle[0];
+ }
+ alternates.set(alt);
}
- } finally {
- br.close();
}
}
+ return alt;
+ }
- if (l.isEmpty()) {
- return NO_ALTERNATES;
+ private AlternateHandle[] loadAlternates() throws IOException {
+ final List<AlternateHandle> l = new ArrayList<AlternateHandle>(4);
+ final BufferedReader br = open(alternatesFile);
+ try {
+ String line;
+ while ((line = br.readLine()) != null) {
+ l.add(openAlternate(line));
+ }
+ } finally {
+ br.close();
}
- return l.toArray(new ObjectDatabase[l.size()]);
+ return l.toArray(new AlternateHandle[l.size()]);
}
private static BufferedReader open(final File f)
@@ -489,19 +599,22 @@ public class ObjectDirectory extends ObjectDatabase {
return new BufferedReader(new FileReader(f));
}
- private ObjectDatabase openAlternate(final String location)
+ private AlternateHandle openAlternate(final String location)
throws IOException {
final File objdir = fs.resolve(objects, location);
return openAlternate(objdir);
}
- private ObjectDatabase openAlternate(File objdir) throws IOException {
+ private AlternateHandle openAlternate(File objdir) throws IOException {
final File parent = objdir.getParentFile();
if (FileKey.isGitRepository(parent, fs)) {
- final Repository db = RepositoryCache.open(FileKey.exact(parent, fs));
- return new AlternateRepositoryDatabase(db);
+ FileKey key = FileKey.exact(parent, fs);
+ FileRepository db = (FileRepository) RepositoryCache.open(key);
+ return new AlternateRepository(db);
}
- return new ObjectDirectory(objdir, null, fs);
+
+ ObjectDirectory db = new ObjectDirectory(config, objdir, null, fs);
+ return new AlternateHandle(db);
}
private static final class PackList {
@@ -566,6 +679,15 @@ public class ObjectDirectory extends ObjectDatabase {
@Override
public ObjectDatabase newCachedDatabase() {
+ return newCachedFileObjectDatabase();
+ }
+
+ FileObjectDatabase newCachedFileObjectDatabase() {
return new CachedObjectDirectory(this);
}
+
+ @Override
+ int getStreamFileThreshold() {
+ return streamFileThreshold;
+ }
}
diff --git a/org.eclipse.jgit/src/org/eclipse/jgit/storage/file/ObjectDirectoryInserter.java b/org.eclipse.jgit/src/org/eclipse/jgit/storage/file/ObjectDirectoryInserter.java
new file mode 100644
index 0000000000..5016679894
--- /dev/null
+++ b/org.eclipse.jgit/src/org/eclipse/jgit/storage/file/ObjectDirectoryInserter.java
@@ -0,0 +1,182 @@
+/*
+ * Copyright (C) 2007, Robin Rosenberg <robin.rosenberg@dewire.com>
+ * Copyright (C) 2008, Shawn O. Pearce <spearce@spearce.org>
+ * Copyright (C) 2009, Google Inc.
+ * and other copyright owners as documented in the project's IP log.
+ *
+ * This program and the accompanying materials are made available
+ * under the terms of the Eclipse Distribution License v1.0 which
+ * accompanies this distribution, is reproduced below, and is
+ * available at http://www.eclipse.org/org/documents/edl-v10.php
+ *
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or
+ * without modification, are permitted provided that the following
+ * conditions are met:
+ *
+ * - Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ *
+ * - Redistributions in binary form must reproduce the above
+ * copyright notice, this list of conditions and the following
+ * disclaimer in the documentation and/or other materials provided
+ * with the distribution.
+ *
+ * - Neither the name of the Eclipse Foundation, Inc. nor the
+ * names of its contributors may be used to endorse or promote
+ * products derived from this software without specific prior
+ * written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND
+ * CONTRIBUTORS "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES,
+ * INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
+ * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR
+ * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
+ * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT
+ * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
+ * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER
+ * CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT,
+ * STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
+ * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF
+ * ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ */
+
+package org.eclipse.jgit.storage.file;
+
+import java.io.EOFException;
+import java.io.File;
+import java.io.FileNotFoundException;
+import java.io.FileOutputStream;
+import java.io.IOException;
+import java.io.InputStream;
+import java.io.OutputStream;
+import java.security.DigestOutputStream;
+import java.security.MessageDigest;
+import java.util.zip.Deflater;
+import java.util.zip.DeflaterOutputStream;
+
+import org.eclipse.jgit.errors.ObjectWritingException;
+import org.eclipse.jgit.lib.Config;
+import org.eclipse.jgit.lib.Constants;
+import org.eclipse.jgit.lib.CoreConfig;
+import org.eclipse.jgit.lib.ObjectId;
+import org.eclipse.jgit.lib.ObjectInserter;
+
+/** Creates loose objects in a {@link ObjectDirectory}. */
+class ObjectDirectoryInserter extends ObjectInserter {
+ private final ObjectDirectory db;
+
+ private final Config config;
+
+ private Deflater deflate;
+
+ ObjectDirectoryInserter(final ObjectDirectory dest, final Config cfg) {
+ db = dest;
+ config = cfg;
+ }
+
+ @Override
+ public ObjectId insert(final int type, long len, final InputStream is)
+ throws IOException {
+ final MessageDigest md = digest();
+ final File tmp = toTemp(md, type, len, is);
+ final ObjectId id = ObjectId.fromRaw(md.digest());
+ if (db.has(id)) {
+ // Object is already in the repository, remove temporary file.
+ //
+ tmp.delete();
+ return id;
+ }
+
+ final File dst = db.fileFor(id);
+ if (tmp.renameTo(dst))
+ return id;
+
+ // Maybe the directory doesn't exist yet as the object
+ // directories are always lazily created. Note that we
+ // try the rename first as the directory likely does exist.
+ //
+ dst.getParentFile().mkdir();
+ if (tmp.renameTo(dst))
+ return id;
+
+ if (db.has(id)) {
+ tmp.delete();
+ return id;
+ }
+
+ // The object failed to be renamed into its proper
+ // location and it doesn't exist in the repository
+ // either. We really don't know what went wrong, so
+ // fail.
+ //
+ tmp.delete();
+ throw new ObjectWritingException("Unable to create new object: " + dst);
+ }
+
+ @Override
+ public void flush() throws IOException {
+ // Do nothing. Objects are immediately visible.
+ }
+
+ @Override
+ public void release() {
+ if (deflate != null) {
+ try {
+ deflate.end();
+ } finally {
+ deflate = null;
+ }
+ }
+ }
+
+ private File toTemp(final MessageDigest md, final int type, long len,
+ final InputStream is) throws IOException, FileNotFoundException,
+ Error {
+ boolean delete = true;
+ File tmp = File.createTempFile("noz", null, db.getDirectory());
+ try {
+ DigestOutputStream dOut = new DigestOutputStream(
+ compress(new FileOutputStream(tmp)), md);
+ try {
+ dOut.write(Constants.encodedTypeString(type));
+ dOut.write((byte) ' ');
+ dOut.write(Constants.encodeASCII(len));
+ dOut.write((byte) 0);
+
+ final byte[] buf = buffer();
+ while (len > 0) {
+ int n = is.read(buf, 0, (int) Math.min(len, buf.length));
+ if (n <= 0)
+ throw shortInput(len);
+ dOut.write(buf, 0, n);
+ len -= n;
+ }
+ } finally {
+ dOut.close();
+ }
+
+ tmp.setReadOnly();
+ delete = false;
+ return tmp;
+ } finally {
+ if (delete)
+ tmp.delete();
+ }
+ }
+
+ private DeflaterOutputStream compress(final OutputStream out) {
+ if (deflate == null)
+ deflate = new Deflater(config.get(CoreConfig.KEY).getCompression());
+ else
+ deflate.reset();
+ return new DeflaterOutputStream(out, deflate);
+ }
+
+ private static EOFException shortInput(long missing) {
+ return new EOFException("Input did not match supplied length. "
+ + missing + " bytes are missing.");
+ }
+}
diff --git a/org.eclipse.jgit/src/org/eclipse/jgit/storage/file/PackFile.java b/org.eclipse.jgit/src/org/eclipse/jgit/storage/file/PackFile.java
new file mode 100644
index 0000000000..e74a7c0142
--- /dev/null
+++ b/org.eclipse.jgit/src/org/eclipse/jgit/storage/file/PackFile.java
@@ -0,0 +1,920 @@
+/*
+ * Copyright (C) 2008-2009, Google Inc.
+ * Copyright (C) 2007, Robin Rosenberg <robin.rosenberg@dewire.com>
+ * Copyright (C) 2006-2008, Shawn O. Pearce <spearce@spearce.org>
+ * and other copyright owners as documented in the project's IP log.
+ *
+ * This program and the accompanying materials are made available
+ * under the terms of the Eclipse Distribution License v1.0 which
+ * accompanies this distribution, is reproduced below, and is
+ * available at http://www.eclipse.org/org/documents/edl-v10.php
+ *
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or
+ * without modification, are permitted provided that the following
+ * conditions are met:
+ *
+ * - Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ *
+ * - Redistributions in binary form must reproduce the above
+ * copyright notice, this list of conditions and the following
+ * disclaimer in the documentation and/or other materials provided
+ * with the distribution.
+ *
+ * - Neither the name of the Eclipse Foundation, Inc. nor the
+ * names of its contributors may be used to endorse or promote
+ * products derived from this software without specific prior
+ * written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND
+ * CONTRIBUTORS "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES,
+ * INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
+ * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR
+ * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
+ * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT
+ * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
+ * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER
+ * CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT,
+ * STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
+ * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF
+ * ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ */
+
+package org.eclipse.jgit.storage.file;
+
+import java.io.EOFException;
+import java.io.File;
+import java.io.IOException;
+import java.io.RandomAccessFile;
+import java.nio.MappedByteBuffer;
+import java.nio.channels.FileChannel.MapMode;
+import java.text.MessageFormat;
+import java.util.Arrays;
+import java.util.Collections;
+import java.util.Comparator;
+import java.util.Iterator;
+import java.util.zip.CRC32;
+import java.util.zip.DataFormatException;
+import java.util.zip.Inflater;
+
+import org.eclipse.jgit.JGitText;
+import org.eclipse.jgit.errors.CorruptObjectException;
+import org.eclipse.jgit.errors.MissingObjectException;
+import org.eclipse.jgit.errors.PackInvalidException;
+import org.eclipse.jgit.errors.PackMismatchException;
+import org.eclipse.jgit.errors.StoredObjectRepresentationNotAvailableException;
+import org.eclipse.jgit.lib.AnyObjectId;
+import org.eclipse.jgit.lib.Constants;
+import org.eclipse.jgit.lib.ObjectId;
+import org.eclipse.jgit.lib.ObjectLoader;
+import org.eclipse.jgit.storage.pack.BinaryDelta;
+import org.eclipse.jgit.storage.pack.ObjectToPack;
+import org.eclipse.jgit.storage.pack.PackOutputStream;
+import org.eclipse.jgit.util.LongList;
+import org.eclipse.jgit.util.NB;
+import org.eclipse.jgit.util.RawParseUtils;
+
+/**
+ * A Git version 2 pack file representation. A pack file contains Git objects in
+ * delta packed format yielding high compression of lots of object where some
+ * objects are similar.
+ */
+public class PackFile implements Iterable<PackIndex.MutableEntry> {
+ /** Sorts PackFiles to be most recently created to least recently created. */
+ public static Comparator<PackFile> SORT = new Comparator<PackFile>() {
+ public int compare(final PackFile a, final PackFile b) {
+ return b.packLastModified - a.packLastModified;
+ }
+ };
+
+ private final File idxFile;
+
+ private final File packFile;
+
+ final int hash;
+
+ private RandomAccessFile fd;
+
+ /** Serializes reads performed against {@link #fd}. */
+ private final Object readLock = new Object();
+
+ long length;
+
+ private int activeWindows;
+
+ private int activeCopyRawData;
+
+ private int packLastModified;
+
+ private volatile boolean invalid;
+
+ private byte[] packChecksum;
+
+ private PackIndex loadedIdx;
+
+ private PackReverseIndex reverseIdx;
+
+ /**
+ * Objects we have tried to read, and discovered to be corrupt.
+ * <p>
+ * The list is allocated after the first corruption is found, and filled in
+ * as more entries are discovered. Typically this list is never used, as
+ * pack files do not usually contain corrupt objects.
+ */
+ private volatile LongList corruptObjects;
+
+ /**
+ * Construct a reader for an existing, pre-indexed packfile.
+ *
+ * @param idxFile
+ * path of the <code>.idx</code> file listing the contents.
+ * @param packFile
+ * path of the <code>.pack</code> file holding the data.
+ */
+ public PackFile(final File idxFile, final File packFile) {
+ this.idxFile = idxFile;
+ this.packFile = packFile;
+ this.packLastModified = (int) (packFile.lastModified() >> 10);
+
+ // Multiply by 31 here so we can more directly combine with another
+ // value in WindowCache.hash(), without doing the multiply there.
+ //
+ hash = System.identityHashCode(this) * 31;
+ length = Long.MAX_VALUE;
+ }
+
+ private synchronized PackIndex idx() throws IOException {
+ if (loadedIdx == null) {
+ if (invalid)
+ throw new PackInvalidException(packFile);
+
+ try {
+ final PackIndex idx = PackIndex.open(idxFile);
+
+ if (packChecksum == null)
+ packChecksum = idx.packChecksum;
+ else if (!Arrays.equals(packChecksum, idx.packChecksum))
+ throw new PackMismatchException(JGitText.get().packChecksumMismatch);
+
+ loadedIdx = idx;
+ } catch (IOException e) {
+ invalid = true;
+ throw e;
+ }
+ }
+ return loadedIdx;
+ }
+
+ /** @return the File object which locates this pack on disk. */
+ public File getPackFile() {
+ return packFile;
+ }
+
+ /**
+ * Determine if an object is contained within the pack file.
+ * <p>
+ * For performance reasons only the index file is searched; the main pack
+ * content is ignored entirely.
+ * </p>
+ *
+ * @param id
+ * the object to look for. Must not be null.
+ * @return true if the object is in this pack; false otherwise.
+ * @throws IOException
+ * the index file cannot be loaded into memory.
+ */
+ public boolean hasObject(final AnyObjectId id) throws IOException {
+ final long offset = idx().findOffset(id);
+ return 0 < offset && !isCorrupt(offset);
+ }
+
+ /**
+ * Get an object from this pack.
+ *
+ * @param curs
+ * temporary working space associated with the calling thread.
+ * @param id
+ * the object to obtain from the pack. Must not be null.
+ * @return the object loader for the requested object if it is contained in
+ * this pack; null if the object was not found.
+ * @throws IOException
+ * the pack file or the index could not be read.
+ */
+ ObjectLoader get(final WindowCursor curs, final AnyObjectId id)
+ throws IOException {
+ final long offset = idx().findOffset(id);
+ return 0 < offset && !isCorrupt(offset) ? load(curs, offset) : null;
+ }
+
+ /**
+ * Close the resources utilized by this repository
+ */
+ public void close() {
+ UnpackedObjectCache.purge(this);
+ WindowCache.purge(this);
+ synchronized (this) {
+ loadedIdx = null;
+ reverseIdx = null;
+ }
+ }
+
+ /**
+ * Provide iterator over entries in associated pack index, that should also
+ * exist in this pack file. Objects returned by such iterator are mutable
+ * during iteration.
+ * <p>
+ * Iterator returns objects in SHA-1 lexicographical order.
+ * </p>
+ *
+ * @return iterator over entries of associated pack index
+ *
+ * @see PackIndex#iterator()
+ */
+ public Iterator<PackIndex.MutableEntry> iterator() {
+ try {
+ return idx().iterator();
+ } catch (IOException e) {
+ return Collections.<PackIndex.MutableEntry> emptyList().iterator();
+ }
+ }
+
+ /**
+ * Obtain the total number of objects available in this pack. This method
+ * relies on pack index, giving number of effectively available objects.
+ *
+ * @return number of objects in index of this pack, likewise in this pack
+ * @throws IOException
+ * the index file cannot be loaded into memory.
+ */
+ long getObjectCount() throws IOException {
+ return idx().getObjectCount();
+ }
+
+ /**
+ * Search for object id with the specified start offset in associated pack
+ * (reverse) index.
+ *
+ * @param offset
+ * start offset of object to find
+ * @return object id for this offset, or null if no object was found
+ * @throws IOException
+ * the index file cannot be loaded into memory.
+ */
+ ObjectId findObjectForOffset(final long offset) throws IOException {
+ return getReverseIdx().findObject(offset);
+ }
+
+ private final UnpackedObjectCache.Entry readCache(final long position) {
+ return UnpackedObjectCache.get(this, position);
+ }
+
+ private final void saveCache(final long position, final byte[] data, final int type) {
+ UnpackedObjectCache.store(this, position, data, type);
+ }
+
+ private final byte[] decompress(final long position, final long totalSize,
+ final WindowCursor curs) throws IOException, DataFormatException {
+ final byte[] dstbuf = new byte[(int) totalSize];
+ if (curs.inflate(this, position, dstbuf, 0) != totalSize)
+ throw new EOFException(MessageFormat.format(JGitText.get().shortCompressedStreamAt, position));
+ return dstbuf;
+ }
+
+ final void copyAsIs(PackOutputStream out, LocalObjectToPack src,
+ WindowCursor curs) throws IOException,
+ StoredObjectRepresentationNotAvailableException {
+ beginCopyAsIs(src);
+ try {
+ copyAsIs2(out, src, curs);
+ } finally {
+ endCopyAsIs();
+ }
+ }
+
+ private void copyAsIs2(PackOutputStream out, LocalObjectToPack src,
+ WindowCursor curs) throws IOException,
+ StoredObjectRepresentationNotAvailableException {
+ final CRC32 crc1 = new CRC32();
+ final CRC32 crc2 = new CRC32();
+ final byte[] buf = out.getCopyBuffer();
+
+ // Rip apart the header so we can discover the size.
+ //
+ readFully(src.offset, buf, 0, 20, curs);
+ int c = buf[0] & 0xff;
+ final int typeCode = (c >> 4) & 7;
+ long inflatedLength = c & 15;
+ int shift = 4;
+ int headerCnt = 1;
+ while ((c & 0x80) != 0) {
+ c = buf[headerCnt++] & 0xff;
+ inflatedLength += (c & 0x7f) << shift;
+ shift += 7;
+ }
+
+ if (typeCode == Constants.OBJ_OFS_DELTA) {
+ do {
+ c = buf[headerCnt++] & 0xff;
+ } while ((c & 128) != 0);
+ crc1.update(buf, 0, headerCnt);
+ crc2.update(buf, 0, headerCnt);
+ } else if (typeCode == Constants.OBJ_REF_DELTA) {
+ crc1.update(buf, 0, headerCnt);
+ crc2.update(buf, 0, headerCnt);
+
+ readFully(src.offset + headerCnt, buf, 0, 20, curs);
+ crc1.update(buf, 0, 20);
+ crc2.update(buf, 0, headerCnt);
+ headerCnt += 20;
+ } else {
+ crc1.update(buf, 0, headerCnt);
+ crc2.update(buf, 0, headerCnt);
+ }
+
+ final long dataOffset = src.offset + headerCnt;
+ final long dataLength = src.length;
+ final long expectedCRC;
+ final ByteArrayWindow quickCopy;
+
+ // Verify the object isn't corrupt before sending. If it is,
+ // we report it missing instead.
+ //
+ try {
+ quickCopy = curs.quickCopy(this, dataOffset, dataLength);
+
+ if (idx().hasCRC32Support()) {
+ // Index has the CRC32 code cached, validate the object.
+ //
+ expectedCRC = idx().findCRC32(src);
+ if (quickCopy != null) {
+ quickCopy.crc32(crc1, dataOffset, (int) dataLength);
+ } else {
+ long pos = dataOffset;
+ long cnt = dataLength;
+ while (cnt > 0) {
+ final int n = (int) Math.min(cnt, buf.length);
+ readFully(pos, buf, 0, n, curs);
+ crc1.update(buf, 0, n);
+ pos += n;
+ cnt -= n;
+ }
+ }
+ if (crc1.getValue() != expectedCRC) {
+ setCorrupt(src.offset);
+ throw new CorruptObjectException(MessageFormat.format(
+ JGitText.get().objectAtHasBadZlibStream,
+ src.offset, getPackFile()));
+ }
+ } else {
+ // We don't have a CRC32 code in the index, so compute it
+ // now while inflating the raw data to get zlib to tell us
+ // whether or not the data is safe.
+ //
+ Inflater inf = curs.inflater();
+ byte[] tmp = new byte[1024];
+ if (quickCopy != null) {
+ quickCopy.check(inf, tmp, dataOffset, (int) dataLength);
+ } else {
+ long pos = dataOffset;
+ long cnt = dataLength;
+ while (cnt > 0) {
+ final int n = (int) Math.min(cnt, buf.length);
+ readFully(pos, buf, 0, n, curs);
+ crc1.update(buf, 0, n);
+ inf.setInput(buf, 0, n);
+ while (inf.inflate(tmp, 0, tmp.length) > 0)
+ continue;
+ pos += n;
+ cnt -= n;
+ }
+ }
+ if (!inf.finished() || inf.getBytesRead() != dataLength) {
+ setCorrupt(src.offset);
+ throw new EOFException(MessageFormat.format(
+ JGitText.get().shortCompressedStreamAt,
+ src.offset));
+ }
+ expectedCRC = crc1.getValue();
+ }
+ } catch (DataFormatException dataFormat) {
+ setCorrupt(src.offset);
+
+ CorruptObjectException corruptObject = new CorruptObjectException(
+ MessageFormat.format(
+ JGitText.get().objectAtHasBadZlibStream,
+ src.offset, getPackFile()));
+ corruptObject.initCause(dataFormat);
+
+ StoredObjectRepresentationNotAvailableException gone;
+ gone = new StoredObjectRepresentationNotAvailableException(src);
+ gone.initCause(corruptObject);
+ throw gone;
+
+ } catch (IOException ioError) {
+ StoredObjectRepresentationNotAvailableException gone;
+ gone = new StoredObjectRepresentationNotAvailableException(src);
+ gone.initCause(ioError);
+ throw gone;
+ }
+
+ if (quickCopy != null) {
+ // The entire object fits into a single byte array window slice,
+ // and we have it pinned. Write this out without copying.
+ //
+ out.writeHeader(src, inflatedLength);
+ quickCopy.write(out, dataOffset, (int) dataLength);
+
+ } else if (dataLength <= buf.length) {
+ // Tiny optimization: Lots of objects are very small deltas or
+ // deflated commits that are likely to fit in the copy buffer.
+ //
+ out.writeHeader(src, inflatedLength);
+ out.write(buf, 0, (int) dataLength);
+ } else {
+ // Now we are committed to sending the object. As we spool it out,
+ // check its CRC32 code to make sure there wasn't corruption between
+ // the verification we did above, and us actually outputting it.
+ //
+ out.writeHeader(src, inflatedLength);
+ long pos = dataOffset;
+ long cnt = dataLength;
+ while (cnt > 0) {
+ final int n = (int) Math.min(cnt, buf.length);
+ readFully(pos, buf, 0, n, curs);
+ crc2.update(buf, 0, n);
+ out.write(buf, 0, n);
+ pos += n;
+ cnt -= n;
+ }
+ if (crc2.getValue() != expectedCRC) {
+ throw new CorruptObjectException(MessageFormat.format(JGitText
+ .get().objectAtHasBadZlibStream, src.offset,
+ getPackFile()));
+ }
+ }
+ }
+
+ boolean invalid() {
+ return invalid;
+ }
+
+ private void readFully(final long position, final byte[] dstbuf,
+ int dstoff, final int cnt, final WindowCursor curs)
+ throws IOException {
+ if (curs.copy(this, position, dstbuf, dstoff, cnt) != cnt)
+ throw new EOFException();
+ }
+
+ private synchronized void beginCopyAsIs(ObjectToPack otp)
+ throws StoredObjectRepresentationNotAvailableException {
+ if (++activeCopyRawData == 1 && activeWindows == 0) {
+ try {
+ doOpen();
+ } catch (IOException thisPackNotValid) {
+ StoredObjectRepresentationNotAvailableException gone;
+
+ gone = new StoredObjectRepresentationNotAvailableException(otp);
+ gone.initCause(thisPackNotValid);
+ throw gone;
+ }
+ }
+ }
+
+ private synchronized void endCopyAsIs() {
+ if (--activeCopyRawData == 0 && activeWindows == 0)
+ doClose();
+ }
+
+ synchronized boolean beginWindowCache() throws IOException {
+ if (++activeWindows == 1) {
+ if (activeCopyRawData == 0)
+ doOpen();
+ return true;
+ }
+ return false;
+ }
+
+ synchronized boolean endWindowCache() {
+ final boolean r = --activeWindows == 0;
+ if (r && activeCopyRawData == 0)
+ doClose();
+ return r;
+ }
+
+ private void doOpen() throws IOException {
+ try {
+ if (invalid)
+ throw new PackInvalidException(packFile);
+ synchronized (readLock) {
+ fd = new RandomAccessFile(packFile, "r");
+ length = fd.length();
+ onOpenPack();
+ }
+ } catch (IOException ioe) {
+ openFail();
+ throw ioe;
+ } catch (RuntimeException re) {
+ openFail();
+ throw re;
+ } catch (Error re) {
+ openFail();
+ throw re;
+ }
+ }
+
+ private void openFail() {
+ activeWindows = 0;
+ activeCopyRawData = 0;
+ invalid = true;
+ doClose();
+ }
+
+ private void doClose() {
+ synchronized (readLock) {
+ if (fd != null) {
+ try {
+ fd.close();
+ } catch (IOException err) {
+ // Ignore a close event. We had it open only for reading.
+ // There should not be errors related to network buffers
+ // not flushed, etc.
+ }
+ fd = null;
+ }
+ }
+ }
+
+ ByteArrayWindow read(final long pos, int size) throws IOException {
+ synchronized (readLock) {
+ if (length < pos + size)
+ size = (int) (length - pos);
+ final byte[] buf = new byte[size];
+ fd.seek(pos);
+ fd.readFully(buf, 0, size);
+ return new ByteArrayWindow(this, pos, buf);
+ }
+ }
+
+ ByteWindow mmap(final long pos, int size) throws IOException {
+ synchronized (readLock) {
+ if (length < pos + size)
+ size = (int) (length - pos);
+
+ MappedByteBuffer map;
+ try {
+ map = fd.getChannel().map(MapMode.READ_ONLY, pos, size);
+ } catch (IOException ioe1) {
+ // The most likely reason this failed is the JVM has run out
+ // of virtual memory. We need to discard quickly, and try to
+ // force the GC to finalize and release any existing mappings.
+ //
+ System.gc();
+ System.runFinalization();
+ map = fd.getChannel().map(MapMode.READ_ONLY, pos, size);
+ }
+
+ if (map.hasArray())
+ return new ByteArrayWindow(this, pos, map.array());
+ return new ByteBufferWindow(this, pos, map);
+ }
+ }
+
+ private void onOpenPack() throws IOException {
+ final PackIndex idx = idx();
+ final byte[] buf = new byte[20];
+
+ fd.seek(0);
+ fd.readFully(buf, 0, 12);
+ if (RawParseUtils.match(buf, 0, Constants.PACK_SIGNATURE) != 4)
+ throw new IOException(JGitText.get().notAPACKFile);
+ final long vers = NB.decodeUInt32(buf, 4);
+ final long packCnt = NB.decodeUInt32(buf, 8);
+ if (vers != 2 && vers != 3)
+ throw new IOException(MessageFormat.format(JGitText.get().unsupportedPackVersion, vers));
+
+ if (packCnt != idx.getObjectCount())
+ throw new PackMismatchException(MessageFormat.format(
+ JGitText.get().packObjectCountMismatch, packCnt, idx.getObjectCount(), getPackFile()));
+
+ fd.seek(length - 20);
+ fd.read(buf, 0, 20);
+ if (!Arrays.equals(buf, packChecksum))
+ throw new PackMismatchException(MessageFormat.format(
+ JGitText.get().packObjectCountMismatch
+ , ObjectId.fromRaw(buf).name()
+ , ObjectId.fromRaw(idx.packChecksum).name()
+ , getPackFile()));
+ }
+
+ ObjectLoader load(final WindowCursor curs, final long pos)
+ throws IOException {
+ final byte[] ib = curs.tempId;
+ readFully(pos, ib, 0, 20, curs);
+ int c = ib[0] & 0xff;
+ final int type = (c >> 4) & 7;
+ long sz = c & 15;
+ int shift = 4;
+ int p = 1;
+ while ((c & 0x80) != 0) {
+ c = ib[p++] & 0xff;
+ sz += (c & 0x7f) << shift;
+ shift += 7;
+ }
+
+ try {
+ switch (type) {
+ case Constants.OBJ_COMMIT:
+ case Constants.OBJ_TREE:
+ case Constants.OBJ_BLOB:
+ case Constants.OBJ_TAG: {
+ if (sz < curs.getStreamFileThreshold()) {
+ byte[] data = decompress(pos + p, sz, curs);
+ return new ObjectLoader.SmallObject(type, data);
+ }
+ return new LargePackedWholeObject(type, sz, pos, p, this, curs.db);
+ }
+
+ case Constants.OBJ_OFS_DELTA: {
+ c = ib[p++] & 0xff;
+ long ofs = c & 127;
+ while ((c & 128) != 0) {
+ ofs += 1;
+ c = ib[p++] & 0xff;
+ ofs <<= 7;
+ ofs += (c & 127);
+ }
+ return loadDelta(pos, p, sz, pos - ofs, curs);
+ }
+
+ case Constants.OBJ_REF_DELTA: {
+ readFully(pos + p, ib, 0, 20, curs);
+ long ofs = findDeltaBase(ObjectId.fromRaw(ib));
+ return loadDelta(pos, p + 20, sz, ofs, curs);
+ }
+
+ default:
+ throw new IOException(MessageFormat.format(
+ JGitText.get().unknownObjectType, type));
+ }
+ } catch (DataFormatException dfe) {
+ CorruptObjectException coe = new CorruptObjectException(
+ MessageFormat.format(
+ JGitText.get().objectAtHasBadZlibStream, pos,
+ getPackFile()));
+ coe.initCause(dfe);
+ throw coe;
+ }
+ }
+
+ private long findDeltaBase(ObjectId baseId) throws IOException,
+ MissingObjectException {
+ long ofs = idx().findOffset(baseId);
+ if (ofs < 0)
+ throw new MissingObjectException(baseId,
+ JGitText.get().missingDeltaBase);
+ return ofs;
+ }
+
+ private ObjectLoader loadDelta(long posSelf, int hdrLen, long sz,
+ long posBase, WindowCursor curs) throws IOException,
+ DataFormatException {
+ if (curs.getStreamFileThreshold() <= sz) {
+ // The delta instruction stream itself is pretty big, and
+ // that implies the resulting object is going to be massive.
+ // Use only the large delta format here.
+ //
+ return new LargePackedDeltaObject(posSelf, posBase, hdrLen, //
+ this, curs.db);
+ }
+
+ byte[] data;
+ int type;
+
+ UnpackedObjectCache.Entry e = readCache(posBase);
+ if (e != null) {
+ data = e.data;
+ type = e.type;
+ } else {
+ ObjectLoader p = load(curs, posBase);
+ if (p.isLarge()) {
+ // The base itself is large. We have to produce a large
+ // delta stream as we don't want to build the whole base.
+ //
+ return new LargePackedDeltaObject(posSelf, posBase, hdrLen,
+ this, curs.db);
+ }
+ data = p.getCachedBytes();
+ type = p.getType();
+ saveCache(posBase, data, type);
+ }
+
+ // At this point we have the base, and its small, and the delta
+ // stream also is small, so the result object cannot be more than
+ // 2x our small size. This occurs if the delta instructions were
+ // "copy entire base, literal insert entire delta". Go with the
+ // faster small object style at this point.
+ //
+ data = BinaryDelta.apply(data, decompress(posSelf + hdrLen, sz, curs));
+ return new ObjectLoader.SmallObject(type, data);
+ }
+
+ byte[] getDeltaHeader(WindowCursor wc, long pos)
+ throws IOException, DataFormatException {
+ // The delta stream starts as two variable length integers. If we
+ // assume they are 64 bits each, we need 16 bytes to encode them,
+ // plus 2 extra bytes for the variable length overhead. So 18 is
+ // the longest delta instruction header.
+ //
+ final byte[] hdr = new byte[18];
+ wc.inflate(this, pos, hdr, 0);
+ return hdr;
+ }
+
+ int getObjectType(final WindowCursor curs, long pos) throws IOException {
+ final byte[] ib = curs.tempId;
+ for (;;) {
+ readFully(pos, ib, 0, 20, curs);
+ int c = ib[0] & 0xff;
+ final int type = (c >> 4) & 7;
+ int shift = 4;
+ int p = 1;
+ while ((c & 0x80) != 0) {
+ c = ib[p++] & 0xff;
+ shift += 7;
+ }
+
+ switch (type) {
+ case Constants.OBJ_COMMIT:
+ case Constants.OBJ_TREE:
+ case Constants.OBJ_BLOB:
+ case Constants.OBJ_TAG:
+ return type;
+
+ case Constants.OBJ_OFS_DELTA: {
+ c = ib[p++] & 0xff;
+ long ofs = c & 127;
+ while ((c & 128) != 0) {
+ ofs += 1;
+ c = ib[p++] & 0xff;
+ ofs <<= 7;
+ ofs += (c & 127);
+ }
+ pos = pos - ofs;
+ continue;
+ }
+
+ case Constants.OBJ_REF_DELTA: {
+ readFully(pos + p, ib, 0, 20, curs);
+ pos = findDeltaBase(ObjectId.fromRaw(ib));
+ continue;
+ }
+
+ default:
+ throw new IOException(MessageFormat.format(
+ JGitText.get().unknownObjectType, type));
+ }
+ }
+ }
+
+ long getObjectSize(final WindowCursor curs, final AnyObjectId id)
+ throws IOException {
+ final long offset = idx().findOffset(id);
+ return 0 < offset ? getObjectSize(curs, offset) : -1;
+ }
+
+ long getObjectSize(final WindowCursor curs, final long pos)
+ throws IOException {
+ final byte[] ib = curs.tempId;
+ readFully(pos, ib, 0, 20, curs);
+ int c = ib[0] & 0xff;
+ final int type = (c >> 4) & 7;
+ long sz = c & 15;
+ int shift = 4;
+ int p = 1;
+ while ((c & 0x80) != 0) {
+ c = ib[p++] & 0xff;
+ sz += (c & 0x7f) << shift;
+ shift += 7;
+ }
+
+ long deltaAt;
+ switch (type) {
+ case Constants.OBJ_COMMIT:
+ case Constants.OBJ_TREE:
+ case Constants.OBJ_BLOB:
+ case Constants.OBJ_TAG:
+ return sz;
+
+ case Constants.OBJ_OFS_DELTA:
+ c = ib[p++] & 0xff;
+ while ((c & 128) != 0)
+ c = ib[p++] & 0xff;
+ deltaAt = pos + p;
+ break;
+
+ case Constants.OBJ_REF_DELTA:
+ deltaAt = pos + p + 20;
+ break;
+
+ default:
+ throw new IOException(MessageFormat.format(
+ JGitText.get().unknownObjectType, type));
+ }
+
+ try {
+ return BinaryDelta.getResultSize(getDeltaHeader(curs, deltaAt));
+ } catch (DataFormatException e) {
+ throw new CorruptObjectException(MessageFormat.format(JGitText
+ .get().objectAtHasBadZlibStream, pos, getPackFile()));
+ }
+ }
+
+ LocalObjectRepresentation representation(final WindowCursor curs,
+ final AnyObjectId objectId) throws IOException {
+ final long pos = idx().findOffset(objectId);
+ if (pos < 0)
+ return null;
+
+ final byte[] ib = curs.tempId;
+ readFully(pos, ib, 0, 20, curs);
+ int c = ib[0] & 0xff;
+ int p = 1;
+ final int typeCode = (c >> 4) & 7;
+ while ((c & 0x80) != 0)
+ c = ib[p++] & 0xff;
+
+ long len = (findEndOffset(pos) - pos);
+ switch (typeCode) {
+ case Constants.OBJ_COMMIT:
+ case Constants.OBJ_TREE:
+ case Constants.OBJ_BLOB:
+ case Constants.OBJ_TAG:
+ return LocalObjectRepresentation.newWhole(this, pos, len - p);
+
+ case Constants.OBJ_OFS_DELTA: {
+ c = ib[p++] & 0xff;
+ long ofs = c & 127;
+ while ((c & 128) != 0) {
+ ofs += 1;
+ c = ib[p++] & 0xff;
+ ofs <<= 7;
+ ofs += (c & 127);
+ }
+ ofs = pos - ofs;
+ return LocalObjectRepresentation.newDelta(this, pos, len - p, ofs);
+ }
+
+ case Constants.OBJ_REF_DELTA: {
+ len -= p;
+ len -= Constants.OBJECT_ID_LENGTH;
+ readFully(pos + p, ib, 0, 20, curs);
+ ObjectId id = ObjectId.fromRaw(ib);
+ return LocalObjectRepresentation.newDelta(this, pos, len, id);
+ }
+
+ default:
+ throw new IOException(MessageFormat.format(
+ JGitText.get().unknownObjectType, typeCode));
+ }
+ }
+
+ private long findEndOffset(final long startOffset)
+ throws IOException, CorruptObjectException {
+ final long maxOffset = length - 20;
+ return getReverseIdx().findNextOffset(startOffset, maxOffset);
+ }
+
+ private synchronized PackReverseIndex getReverseIdx() throws IOException {
+ if (reverseIdx == null)
+ reverseIdx = new PackReverseIndex(idx());
+ return reverseIdx;
+ }
+
+ private boolean isCorrupt(long offset) {
+ LongList list = corruptObjects;
+ if (list == null)
+ return false;
+ synchronized (list) {
+ return list.contains(offset);
+ }
+ }
+
+ private void setCorrupt(long offset) {
+ LongList list = corruptObjects;
+ if (list == null) {
+ synchronized (readLock) {
+ list = corruptObjects;
+ if (list == null) {
+ list = new LongList();
+ corruptObjects = list;
+ }
+ }
+ }
+ synchronized (list) {
+ list.add(offset);
+ }
+ }
+}
diff --git a/org.eclipse.jgit/src/org/eclipse/jgit/lib/PackIndex.java b/org.eclipse.jgit/src/org/eclipse/jgit/storage/file/PackIndex.java
index 13985e78e9..62d1c9d8f8 100644
--- a/org.eclipse.jgit/src/org/eclipse/jgit/lib/PackIndex.java
+++ b/org.eclipse.jgit/src/org/eclipse/jgit/storage/file/PackIndex.java
@@ -42,7 +42,7 @@
* ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
*/
-package org.eclipse.jgit.lib;
+package org.eclipse.jgit.storage.file;
import java.io.File;
import java.io.FileInputStream;
@@ -53,6 +53,9 @@ import java.util.Iterator;
import org.eclipse.jgit.JGitText;
import org.eclipse.jgit.errors.MissingObjectException;
+import org.eclipse.jgit.lib.AnyObjectId;
+import org.eclipse.jgit.lib.MutableObjectId;
+import org.eclipse.jgit.lib.ObjectId;
import org.eclipse.jgit.util.IO;
import org.eclipse.jgit.util.NB;
diff --git a/org.eclipse.jgit/src/org/eclipse/jgit/lib/PackIndexV1.java b/org.eclipse.jgit/src/org/eclipse/jgit/storage/file/PackIndexV1.java
index bb7cd8b53c..3b68edc191 100644
--- a/org.eclipse.jgit/src/org/eclipse/jgit/lib/PackIndexV1.java
+++ b/org.eclipse.jgit/src/org/eclipse/jgit/storage/file/PackIndexV1.java
@@ -44,7 +44,7 @@
* ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
*/
-package org.eclipse.jgit.lib;
+package org.eclipse.jgit.storage.file;
import java.io.IOException;
import java.io.InputStream;
@@ -53,6 +53,9 @@ import java.util.Iterator;
import java.util.NoSuchElementException;
import org.eclipse.jgit.errors.CorruptObjectException;
+import org.eclipse.jgit.lib.AnyObjectId;
+import org.eclipse.jgit.lib.Constants;
+import org.eclipse.jgit.lib.ObjectId;
import org.eclipse.jgit.util.IO;
import org.eclipse.jgit.util.NB;
diff --git a/org.eclipse.jgit/src/org/eclipse/jgit/lib/PackIndexV2.java b/org.eclipse.jgit/src/org/eclipse/jgit/storage/file/PackIndexV2.java
index 128b2df8cb..cef7cc429e 100644
--- a/org.eclipse.jgit/src/org/eclipse/jgit/lib/PackIndexV2.java
+++ b/org.eclipse.jgit/src/org/eclipse/jgit/storage/file/PackIndexV2.java
@@ -41,7 +41,7 @@
* ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
*/
-package org.eclipse.jgit.lib;
+package org.eclipse.jgit.storage.file;
import java.io.IOException;
import java.io.InputStream;
@@ -51,6 +51,9 @@ import java.util.NoSuchElementException;
import org.eclipse.jgit.JGitText;
import org.eclipse.jgit.errors.MissingObjectException;
+import org.eclipse.jgit.lib.AnyObjectId;
+import org.eclipse.jgit.lib.Constants;
+import org.eclipse.jgit.lib.ObjectId;
import org.eclipse.jgit.util.IO;
import org.eclipse.jgit.util.NB;
diff --git a/org.eclipse.jgit/src/org/eclipse/jgit/lib/PackIndexWriter.java b/org.eclipse.jgit/src/org/eclipse/jgit/storage/file/PackIndexWriter.java
index 4d2714bc55..6bd73adcb1 100644
--- a/org.eclipse.jgit/src/org/eclipse/jgit/lib/PackIndexWriter.java
+++ b/org.eclipse.jgit/src/org/eclipse/jgit/storage/file/PackIndexWriter.java
@@ -42,7 +42,7 @@
* ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
*/
-package org.eclipse.jgit.lib;
+package org.eclipse.jgit.storage.file;
import java.io.BufferedOutputStream;
import java.io.IOException;
@@ -52,6 +52,8 @@ import java.text.MessageFormat;
import java.util.List;
import org.eclipse.jgit.JGitText;
+import org.eclipse.jgit.lib.Constants;
+import org.eclipse.jgit.lib.ObjectId;
import org.eclipse.jgit.transport.PackedObjectInfo;
import org.eclipse.jgit.util.NB;
diff --git a/org.eclipse.jgit/src/org/eclipse/jgit/lib/PackIndexWriterV1.java b/org.eclipse.jgit/src/org/eclipse/jgit/storage/file/PackIndexWriterV1.java
index eb44b3a8c7..722ab0e06b 100644
--- a/org.eclipse.jgit/src/org/eclipse/jgit/lib/PackIndexWriterV1.java
+++ b/org.eclipse.jgit/src/org/eclipse/jgit/storage/file/PackIndexWriterV1.java
@@ -42,7 +42,7 @@
* ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
*/
-package org.eclipse.jgit.lib;
+package org.eclipse.jgit.storage.file;
import java.io.IOException;
import java.io.OutputStream;
diff --git a/org.eclipse.jgit/src/org/eclipse/jgit/lib/PackIndexWriterV2.java b/org.eclipse.jgit/src/org/eclipse/jgit/storage/file/PackIndexWriterV2.java
index b6ac7b89e3..21ebd1ca9c 100644
--- a/org.eclipse.jgit/src/org/eclipse/jgit/lib/PackIndexWriterV2.java
+++ b/org.eclipse.jgit/src/org/eclipse/jgit/storage/file/PackIndexWriterV2.java
@@ -41,7 +41,7 @@
* ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
*/
-package org.eclipse.jgit.lib;
+package org.eclipse.jgit.storage.file;
import java.io.IOException;
import java.io.OutputStream;
diff --git a/org.eclipse.jgit/src/org/eclipse/jgit/storage/file/PackInputStream.java b/org.eclipse.jgit/src/org/eclipse/jgit/storage/file/PackInputStream.java
new file mode 100644
index 0000000000..5425eedbe9
--- /dev/null
+++ b/org.eclipse.jgit/src/org/eclipse/jgit/storage/file/PackInputStream.java
@@ -0,0 +1,85 @@
+/*
+ * Copyright (C) 2010, Google Inc.
+ * and other copyright owners as documented in the project's IP log.
+ *
+ * This program and the accompanying materials are made available
+ * under the terms of the Eclipse Distribution License v1.0 which
+ * accompanies this distribution, is reproduced below, and is
+ * available at http://www.eclipse.org/org/documents/edl-v10.php
+ *
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or
+ * without modification, are permitted provided that the following
+ * conditions are met:
+ *
+ * - Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ *
+ * - Redistributions in binary form must reproduce the above
+ * copyright notice, this list of conditions and the following
+ * disclaimer in the documentation and/or other materials provided
+ * with the distribution.
+ *
+ * - Neither the name of the Eclipse Foundation, Inc. nor the
+ * names of its contributors may be used to endorse or promote
+ * products derived from this software without specific prior
+ * written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND
+ * CONTRIBUTORS "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES,
+ * INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
+ * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR
+ * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
+ * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT
+ * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
+ * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER
+ * CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT,
+ * STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
+ * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF
+ * ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ */
+
+package org.eclipse.jgit.storage.file;
+
+import java.io.IOException;
+import java.io.InputStream;
+
+class PackInputStream extends InputStream {
+ private final WindowCursor wc;
+
+ private final PackFile pack;
+
+ private long pos;
+
+ PackInputStream(PackFile pack, long pos, WindowCursor wc)
+ throws IOException {
+ this.pack = pack;
+ this.pos = pos;
+ this.wc = wc;
+
+ // Pin the first window, to ensure the pack is open and valid.
+ //
+ wc.pin(pack, pos);
+ }
+
+ @Override
+ public int read(byte[] b, int off, int len) throws IOException {
+ int n = wc.copy(pack, pos, b, off, len);
+ pos += n;
+ return n;
+ }
+
+ @Override
+ public int read() throws IOException {
+ byte[] buf = new byte[1];
+ int n = read(buf, 0, 1);
+ return n == 1 ? buf[0] & 0xff : -1;
+ }
+
+ @Override
+ public void close() {
+ wc.release();
+ }
+} \ No newline at end of file
diff --git a/org.eclipse.jgit/src/org/eclipse/jgit/lib/PackLock.java b/org.eclipse.jgit/src/org/eclipse/jgit/storage/file/PackLock.java
index de8e3fa637..be250114c2 100644
--- a/org.eclipse.jgit/src/org/eclipse/jgit/lib/PackLock.java
+++ b/org.eclipse.jgit/src/org/eclipse/jgit/storage/file/PackLock.java
@@ -41,11 +41,13 @@
* ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
*/
-package org.eclipse.jgit.lib;
+package org.eclipse.jgit.storage.file;
import java.io.File;
import java.io.IOException;
+import org.eclipse.jgit.lib.Constants;
+
/** Keeps track of a {@link PackFile}'s associated <code>.keep</code> file. */
public class PackLock {
private final File keepFile;
diff --git a/org.eclipse.jgit/src/org/eclipse/jgit/lib/PackReverseIndex.java b/org.eclipse.jgit/src/org/eclipse/jgit/storage/file/PackReverseIndex.java
index f4f57aed43..96abaeefd3 100644
--- a/org.eclipse.jgit/src/org/eclipse/jgit/lib/PackReverseIndex.java
+++ b/org.eclipse.jgit/src/org/eclipse/jgit/storage/file/PackReverseIndex.java
@@ -41,14 +41,15 @@
* ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
*/
-package org.eclipse.jgit.lib;
+package org.eclipse.jgit.storage.file;
import java.text.MessageFormat;
import java.util.Arrays;
import org.eclipse.jgit.JGitText;
import org.eclipse.jgit.errors.CorruptObjectException;
-import org.eclipse.jgit.lib.PackIndex.MutableEntry;
+import org.eclipse.jgit.lib.ObjectId;
+import org.eclipse.jgit.storage.file.PackIndex.MutableEntry;
/**
* <p>
diff --git a/org.eclipse.jgit/src/org/eclipse/jgit/lib/RefDirectory.java b/org.eclipse.jgit/src/org/eclipse/jgit/storage/file/RefDirectory.java
index 302b63b486..68b0270df9 100644
--- a/org.eclipse.jgit/src/org/eclipse/jgit/lib/RefDirectory.java
+++ b/org.eclipse.jgit/src/org/eclipse/jgit/storage/file/RefDirectory.java
@@ -44,7 +44,7 @@
* ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
*/
-package org.eclipse.jgit.lib;
+package org.eclipse.jgit.storage.file;
import static org.eclipse.jgit.lib.Constants.CHARSET;
import static org.eclipse.jgit.lib.Constants.HEAD;
@@ -74,7 +74,21 @@ import java.util.concurrent.atomic.AtomicInteger;
import java.util.concurrent.atomic.AtomicReference;
import org.eclipse.jgit.JGitText;
+import org.eclipse.jgit.errors.MissingObjectException;
import org.eclipse.jgit.errors.ObjectWritingException;
+import org.eclipse.jgit.events.RefsChangedEvent;
+import org.eclipse.jgit.lib.Constants;
+import org.eclipse.jgit.lib.CoreConfig;
+import org.eclipse.jgit.lib.ObjectId;
+import org.eclipse.jgit.lib.ObjectIdRef;
+import org.eclipse.jgit.lib.PersonIdent;
+import org.eclipse.jgit.lib.Ref;
+import org.eclipse.jgit.lib.RefComparator;
+import org.eclipse.jgit.lib.RefDatabase;
+import org.eclipse.jgit.lib.RefUpdate;
+import org.eclipse.jgit.lib.RefWriter;
+import org.eclipse.jgit.lib.Repository;
+import org.eclipse.jgit.lib.SymbolicRef;
import org.eclipse.jgit.revwalk.RevObject;
import org.eclipse.jgit.revwalk.RevTag;
import org.eclipse.jgit.revwalk.RevWalk;
@@ -109,7 +123,7 @@ public class RefDirectory extends RefDatabase {
/** If in the header, denotes the file has peeled data. */
public static final String PACKED_REFS_PEELED = " peeled"; //$NON-NLS-1$
- private final Repository parent;
+ private final FileRepository parent;
private final File gitDir;
@@ -150,7 +164,7 @@ public class RefDirectory extends RefDatabase {
*/
private final AtomicInteger lastNotifiedModCnt = new AtomicInteger();
- RefDirectory(final Repository db) {
+ RefDirectory(final FileRepository db) {
final FS fs = db.getFS();
parent = db;
gitDir = db.getDirectory();
@@ -405,20 +419,7 @@ public class RefDirectory extends RefDatabase {
if (leaf.isPeeled() || leaf.getObjectId() == null)
return ref;
- RevWalk rw = new RevWalk(getRepository());
- RevObject obj = rw.parseAny(leaf.getObjectId());
- ObjectIdRef newLeaf;
- if (obj instanceof RevTag) {
- do {
- obj = rw.parseAny(((RevTag) obj).getObject());
- } while (obj instanceof RevTag);
-
- newLeaf = new ObjectIdRef.PeeledTag(leaf.getStorage(), leaf
- .getName(), leaf.getObjectId(), obj.copy());
- } else {
- newLeaf = new ObjectIdRef.PeeledNonTag(leaf.getStorage(), leaf
- .getName(), leaf.getObjectId());
- }
+ ObjectIdRef newLeaf = doPeel(leaf);
// Try to remember this peeling in the cache, so we don't have to do
// it again in the future, but only if the reference is unchanged.
@@ -435,6 +436,23 @@ public class RefDirectory extends RefDatabase {
return recreate(ref, newLeaf);
}
+ private ObjectIdRef doPeel(final Ref leaf) throws MissingObjectException,
+ IOException {
+ RevWalk rw = new RevWalk(getRepository());
+ try {
+ RevObject obj = rw.parseAny(leaf.getObjectId());
+ if (obj instanceof RevTag) {
+ return new ObjectIdRef.PeeledTag(leaf.getStorage(), leaf
+ .getName(), leaf.getObjectId(), rw.peel(obj).copy());
+ } else {
+ return new ObjectIdRef.PeeledNonTag(leaf.getStorage(), leaf
+ .getName(), leaf.getObjectId());
+ }
+ } finally {
+ rw.release();
+ }
+ }
+
private static Ref recreate(final Ref old, final ObjectIdRef leaf) {
if (old.isSymbolic()) {
Ref dst = recreate(old.getTarget(), leaf);
@@ -590,7 +608,7 @@ public class RefDirectory extends RefDatabase {
}
private boolean isLogAllRefUpdates() {
- return parent.getConfig().getCore().isLogAllRefUpdates();
+ return parent.getConfig().get(CoreConfig.KEY).isLogAllRefUpdates();
}
private boolean shouldAutoCreateLog(final String refName) {
@@ -833,7 +851,7 @@ public class RefDirectory extends RefDatabase {
final int last = lastNotifiedModCnt.get();
final int curr = modCnt.get();
if (last != curr && lastNotifiedModCnt.compareAndSet(last, curr))
- parent.fireRefsChanged();
+ parent.fireEvent(new RefsChangedEvent());
}
/**
diff --git a/org.eclipse.jgit/src/org/eclipse/jgit/lib/RefDirectoryRename.java b/org.eclipse.jgit/src/org/eclipse/jgit/storage/file/RefDirectoryRename.java
index fec00d9325..4f3efe343e 100644
--- a/org.eclipse.jgit/src/org/eclipse/jgit/lib/RefDirectoryRename.java
+++ b/org.eclipse.jgit/src/org/eclipse/jgit/storage/file/RefDirectoryRename.java
@@ -42,11 +42,15 @@
* ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
*/
-package org.eclipse.jgit.lib;
+package org.eclipse.jgit.storage.file;
import java.io.File;
import java.io.IOException;
+import org.eclipse.jgit.lib.Constants;
+import org.eclipse.jgit.lib.ObjectId;
+import org.eclipse.jgit.lib.RefRename;
+import org.eclipse.jgit.lib.RefUpdate;
import org.eclipse.jgit.lib.RefUpdate.Result;
import org.eclipse.jgit.revwalk.RevWalk;
@@ -88,10 +92,10 @@ class RefDirectoryRename extends RefRename {
if (source.getRef().isSymbolic())
return Result.IO_FAILURE; // not supported
- final RevWalk rw = new RevWalk(refdb.getRepository());
objId = source.getOldObjectId();
updateHEAD = needToUpdateHEAD();
tmp = refdb.newTemporaryUpdate();
+ final RevWalk rw = new RevWalk(refdb.getRepository());
try {
// First backup the source so its never unreachable.
tmp.setNewObjectId(objId);
@@ -173,6 +177,7 @@ class RefDirectoryRename extends RefRename {
} catch (IOException err) {
refdb.fileFor(tmp.getName()).delete();
}
+ rw.release();
}
}
diff --git a/org.eclipse.jgit/src/org/eclipse/jgit/lib/RefDirectoryUpdate.java b/org.eclipse.jgit/src/org/eclipse/jgit/storage/file/RefDirectoryUpdate.java
index 447be104ab..8d35ec34f6 100644
--- a/org.eclipse.jgit/src/org/eclipse/jgit/lib/RefDirectoryUpdate.java
+++ b/org.eclipse.jgit/src/org/eclipse/jgit/storage/file/RefDirectoryUpdate.java
@@ -42,12 +42,16 @@
* ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
*/
-package org.eclipse.jgit.lib;
+package org.eclipse.jgit.storage.file;
import static org.eclipse.jgit.lib.Constants.encode;
import java.io.IOException;
+import org.eclipse.jgit.lib.Ref;
+import org.eclipse.jgit.lib.RefUpdate;
+import org.eclipse.jgit.lib.Repository;
+
/** Updates any reference stored by {@link RefDirectory}. */
class RefDirectoryUpdate extends RefUpdate {
private final RefDirectory database;
diff --git a/org.eclipse.jgit/src/org/eclipse/jgit/lib/ReflogReader.java b/org.eclipse.jgit/src/org/eclipse/jgit/storage/file/ReflogReader.java
index 4c5503f321..75214308d6 100644
--- a/org.eclipse.jgit/src/org/eclipse/jgit/lib/ReflogReader.java
+++ b/org.eclipse.jgit/src/org/eclipse/jgit/storage/file/ReflogReader.java
@@ -42,7 +42,7 @@
* ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
*/
-package org.eclipse.jgit.lib;
+package org.eclipse.jgit.storage.file;
import java.io.File;
import java.io.FileNotFoundException;
@@ -52,6 +52,10 @@ import java.util.Collections;
import java.util.List;
import org.eclipse.jgit.JGitText;
+import org.eclipse.jgit.lib.Constants;
+import org.eclipse.jgit.lib.ObjectId;
+import org.eclipse.jgit.lib.PersonIdent;
+import org.eclipse.jgit.lib.Repository;
import org.eclipse.jgit.util.IO;
import org.eclipse.jgit.util.RawParseUtils;
diff --git a/org.eclipse.jgit/src/org/eclipse/jgit/storage/file/UnpackedObject.java b/org.eclipse.jgit/src/org/eclipse/jgit/storage/file/UnpackedObject.java
new file mode 100644
index 0000000000..59f9c82670
--- /dev/null
+++ b/org.eclipse.jgit/src/org/eclipse/jgit/storage/file/UnpackedObject.java
@@ -0,0 +1,413 @@
+/*
+ * Copyright (C) 2007, Robin Rosenberg <robin.rosenberg@dewire.com>
+ * Copyright (C) 2006-2008, Shawn O. Pearce <spearce@spearce.org>
+ * Copyright (C) 2010, Google Inc.
+ * and other copyright owners as documented in the project's IP log.
+ *
+ * This program and the accompanying materials are made available
+ * under the terms of the Eclipse Distribution License v1.0 which
+ * accompanies this distribution, is reproduced below, and is
+ * available at http://www.eclipse.org/org/documents/edl-v10.php
+ *
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or
+ * without modification, are permitted provided that the following
+ * conditions are met:
+ *
+ * - Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ *
+ * - Redistributions in binary form must reproduce the above
+ * copyright notice, this list of conditions and the following
+ * disclaimer in the documentation and/or other materials provided
+ * with the distribution.
+ *
+ * - Neither the name of the Eclipse Foundation, Inc. nor the
+ * names of its contributors may be used to endorse or promote
+ * products derived from this software without specific prior
+ * written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND
+ * CONTRIBUTORS "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES,
+ * INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
+ * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR
+ * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
+ * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT
+ * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
+ * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER
+ * CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT,
+ * STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
+ * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF
+ * ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ */
+
+package org.eclipse.jgit.storage.file;
+
+import java.io.BufferedInputStream;
+import java.io.ByteArrayInputStream;
+import java.io.File;
+import java.io.FileInputStream;
+import java.io.FileNotFoundException;
+import java.io.IOException;
+import java.io.InputStream;
+import java.util.zip.DataFormatException;
+import java.util.zip.Inflater;
+import java.util.zip.InflaterInputStream;
+import java.util.zip.ZipException;
+
+import org.eclipse.jgit.JGitText;
+import org.eclipse.jgit.errors.CorruptObjectException;
+import org.eclipse.jgit.errors.LargeObjectException;
+import org.eclipse.jgit.errors.MissingObjectException;
+import org.eclipse.jgit.lib.AnyObjectId;
+import org.eclipse.jgit.lib.Constants;
+import org.eclipse.jgit.lib.InflaterCache;
+import org.eclipse.jgit.lib.ObjectId;
+import org.eclipse.jgit.lib.ObjectLoader;
+import org.eclipse.jgit.lib.ObjectStream;
+import org.eclipse.jgit.util.IO;
+import org.eclipse.jgit.util.MutableInteger;
+import org.eclipse.jgit.util.RawParseUtils;
+
+/**
+ * Loose object loader. This class loads an object not stored in a pack.
+ */
+public class UnpackedObject {
+ private static final int BUFFER_SIZE = 8192;
+
+ /**
+ * Parse an object from the unpacked object format.
+ *
+ * @param raw
+ * complete contents of the compressed object.
+ * @param id
+ * expected ObjectId of the object, used only for error reporting
+ * in exceptions.
+ * @return loader to read the inflated contents.
+ * @throws IOException
+ * the object cannot be parsed.
+ */
+ public static ObjectLoader parse(byte[] raw, AnyObjectId id)
+ throws IOException {
+ WindowCursor wc = new WindowCursor(null);
+ try {
+ return open(new ByteArrayInputStream(raw), null, id, wc);
+ } finally {
+ wc.release();
+ }
+ }
+
+ static ObjectLoader open(InputStream in, File path, AnyObjectId id,
+ WindowCursor wc) throws IOException {
+ try {
+ in = buffer(in);
+ in.mark(20);
+ final byte[] hdr = new byte[64];
+ IO.readFully(in, hdr, 0, 2);
+
+ if (isStandardFormat(hdr)) {
+ in.reset();
+ Inflater inf = wc.inflater();
+ InputStream zIn = inflate(in, inf);
+ int avail = readSome(zIn, hdr, 0, 64);
+ if (avail < 5)
+ throw new CorruptObjectException(id,
+ JGitText.get().corruptObjectNoHeader);
+
+ final MutableInteger p = new MutableInteger();
+ int type = Constants.decodeTypeString(id, hdr, (byte) ' ', p);
+ long size = RawParseUtils.parseLongBase10(hdr, p.value, p);
+ if (size < 0)
+ throw new CorruptObjectException(id,
+ JGitText.get().corruptObjectNegativeSize);
+ if (hdr[p.value++] != 0)
+ throw new CorruptObjectException(id,
+ JGitText.get().corruptObjectGarbageAfterSize);
+ if (path == null && Integer.MAX_VALUE < size)
+ throw new LargeObjectException(id.copy());
+ if (size < wc.getStreamFileThreshold() || path == null) {
+ byte[] data = new byte[(int) size];
+ int n = avail - p.value;
+ if (n > 0)
+ System.arraycopy(hdr, p.value, data, 0, n);
+ IO.readFully(zIn, data, n, data.length - n);
+ checkValidEndOfStream(in, inf, id, hdr);
+ return new ObjectLoader.SmallObject(type, data);
+ }
+ return new LargeObject(type, size, path, id, wc.db);
+
+ } else {
+ readSome(in, hdr, 2, 18);
+ int c = hdr[0] & 0xff;
+ int type = (c >> 4) & 7;
+ long size = c & 15;
+ int shift = 4;
+ int p = 1;
+ while ((c & 0x80) != 0) {
+ c = hdr[p++] & 0xff;
+ size += (c & 0x7f) << shift;
+ shift += 7;
+ }
+
+ switch (type) {
+ case Constants.OBJ_COMMIT:
+ case Constants.OBJ_TREE:
+ case Constants.OBJ_BLOB:
+ case Constants.OBJ_TAG:
+ // Acceptable types for a loose object.
+ break;
+ default:
+ throw new CorruptObjectException(id,
+ JGitText.get().corruptObjectInvalidType);
+ }
+
+ if (path == null && Integer.MAX_VALUE < size)
+ throw new LargeObjectException(id.copy());
+ if (size < wc.getStreamFileThreshold() || path == null) {
+ in.reset();
+ IO.skipFully(in, p);
+ Inflater inf = wc.inflater();
+ InputStream zIn = inflate(in, inf);
+ byte[] data = new byte[(int) size];
+ IO.readFully(zIn, data, 0, data.length);
+ checkValidEndOfStream(in, inf, id, hdr);
+ return new ObjectLoader.SmallObject(type, data);
+ }
+ return new LargeObject(type, size, path, id, wc.db);
+ }
+ } catch (ZipException badStream) {
+ throw new CorruptObjectException(id,
+ JGitText.get().corruptObjectBadStream);
+ }
+ }
+
+ static long getSize(InputStream in, AnyObjectId id, WindowCursor wc)
+ throws IOException {
+ try {
+ in = buffer(in);
+ in.mark(20);
+ final byte[] hdr = new byte[64];
+ IO.readFully(in, hdr, 0, 2);
+
+ if (isStandardFormat(hdr)) {
+ in.reset();
+ Inflater inf = wc.inflater();
+ InputStream zIn = inflate(in, inf);
+ int avail = readSome(zIn, hdr, 0, 64);
+ if (avail < 5)
+ throw new CorruptObjectException(id,
+ JGitText.get().corruptObjectNoHeader);
+
+ final MutableInteger p = new MutableInteger();
+ Constants.decodeTypeString(id, hdr, (byte) ' ', p);
+ long size = RawParseUtils.parseLongBase10(hdr, p.value, p);
+ if (size < 0)
+ throw new CorruptObjectException(id,
+ JGitText.get().corruptObjectNegativeSize);
+ return size;
+
+ } else {
+ readSome(in, hdr, 2, 18);
+ int c = hdr[0] & 0xff;
+ long size = c & 15;
+ int shift = 4;
+ int p = 1;
+ while ((c & 0x80) != 0) {
+ c = hdr[p++] & 0xff;
+ size += (c & 0x7f) << shift;
+ shift += 7;
+ }
+ return size;
+ }
+ } catch (ZipException badStream) {
+ throw new CorruptObjectException(id,
+ JGitText.get().corruptObjectBadStream);
+ }
+ }
+
+ private static void checkValidEndOfStream(InputStream in, Inflater inf,
+ AnyObjectId id, final byte[] buf) throws IOException,
+ CorruptObjectException {
+ for (;;) {
+ int r;
+ try {
+ r = inf.inflate(buf);
+ } catch (DataFormatException e) {
+ throw new CorruptObjectException(id,
+ JGitText.get().corruptObjectBadStream);
+ }
+ if (r != 0)
+ throw new CorruptObjectException(id,
+ JGitText.get().corruptObjectIncorrectLength);
+
+ if (inf.finished()) {
+ if (inf.getRemaining() != 0 || in.read() != -1)
+ throw new CorruptObjectException(id,
+ JGitText.get().corruptObjectBadStream);
+ break;
+ }
+
+ if (!inf.needsInput())
+ throw new CorruptObjectException(id,
+ JGitText.get().corruptObjectBadStream);
+
+ r = in.read(buf);
+ if (r <= 0)
+ throw new CorruptObjectException(id,
+ JGitText.get().corruptObjectBadStream);
+ inf.setInput(buf, 0, r);
+ }
+ }
+
+ private static boolean isStandardFormat(final byte[] hdr) {
+ // Try to determine if this is a standard format loose object or
+ // a pack style loose object. The standard format is completely
+ // compressed with zlib so the first byte must be 0x78 (15-bit
+ // window size, deflated) and the first 16 bit word must be
+ // evenly divisible by 31. Otherwise its a pack style object.
+ //
+ final int fb = hdr[0] & 0xff;
+ return fb == 0x78 && (((fb << 8) | hdr[1] & 0xff) % 31) == 0;
+ }
+
+ private static InputStream inflate(final InputStream in, final long size,
+ final ObjectId id) {
+ final Inflater inf = InflaterCache.get();
+ return new InflaterInputStream(in, inf) {
+ private long remaining = size;
+
+ @Override
+ public int read(byte[] b, int off, int cnt) throws IOException {
+ try {
+ int r = super.read(b, off, cnt);
+ if (r > 0)
+ remaining -= r;
+ return r;
+ } catch (ZipException badStream) {
+ throw new CorruptObjectException(id,
+ JGitText.get().corruptObjectBadStream);
+ }
+ }
+
+ @Override
+ public void close() throws IOException {
+ try {
+ if (remaining <= 0)
+ checkValidEndOfStream(in, inf, id, new byte[64]);
+ super.close();
+ } finally {
+ InflaterCache.release(inf);
+ }
+ }
+ };
+ }
+
+ private static InflaterInputStream inflate(InputStream in, Inflater inf) {
+ return new InflaterInputStream(in, inf, BUFFER_SIZE);
+ }
+
+ private static BufferedInputStream buffer(InputStream in) {
+ return new BufferedInputStream(in, BUFFER_SIZE);
+ }
+
+ private static int readSome(InputStream in, final byte[] hdr, int off,
+ int cnt) throws IOException {
+ int avail = 0;
+ while (0 < cnt) {
+ int n = in.read(hdr, off, cnt);
+ if (n < 0)
+ break;
+ avail += n;
+ cnt -= n;
+ }
+ return avail;
+ }
+
+ private static final class LargeObject extends ObjectLoader {
+ private final int type;
+
+ private final long size;
+
+ private final File path;
+
+ private final ObjectId id;
+
+ private final FileObjectDatabase source;
+
+ private LargeObject(int type, long size, File path, AnyObjectId id,
+ FileObjectDatabase db) {
+ this.type = type;
+ this.size = size;
+ this.path = path;
+ this.id = id.copy();
+ this.source = db;
+ }
+
+ @Override
+ public int getType() {
+ return type;
+ }
+
+ @Override
+ public long getSize() {
+ return size;
+ }
+
+ @Override
+ public boolean isLarge() {
+ return true;
+ }
+
+ @Override
+ public byte[] getCachedBytes() throws LargeObjectException {
+ throw new LargeObjectException(id);
+ }
+
+ @Override
+ public ObjectStream openStream() throws MissingObjectException,
+ IOException {
+ InputStream in;
+ try {
+ in = buffer(new FileInputStream(path));
+ } catch (FileNotFoundException gone) {
+ // If the loose file no longer exists, it may have been
+ // moved into a pack file in the mean time. Try again
+ // to locate the object.
+ //
+ return source.open(id, type).openStream();
+ }
+
+ boolean ok = false;
+ try {
+ final byte[] hdr = new byte[64];
+ in.mark(20);
+ IO.readFully(in, hdr, 0, 2);
+
+ if (isStandardFormat(hdr)) {
+ in.reset();
+ in = buffer(inflate(in, size, id));
+ while (0 < in.read())
+ continue;
+ } else {
+ readSome(in, hdr, 2, 18);
+ int c = hdr[0] & 0xff;
+ int p = 1;
+ while ((c & 0x80) != 0)
+ c = hdr[p++] & 0xff;
+
+ in.reset();
+ IO.skipFully(in, p);
+ in = buffer(inflate(in, size, id));
+ }
+
+ ok = true;
+ return new ObjectStream.Filter(type, size, in);
+ } finally {
+ if (!ok)
+ in.close();
+ }
+ }
+ }
+}
diff --git a/org.eclipse.jgit/src/org/eclipse/jgit/lib/UnpackedObjectCache.java b/org.eclipse.jgit/src/org/eclipse/jgit/storage/file/UnpackedObjectCache.java
index 3cef48242d..92f4824254 100644
--- a/org.eclipse.jgit/src/org/eclipse/jgit/lib/UnpackedObjectCache.java
+++ b/org.eclipse.jgit/src/org/eclipse/jgit/storage/file/UnpackedObjectCache.java
@@ -41,7 +41,7 @@
* ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
*/
-package org.eclipse.jgit.lib;
+package org.eclipse.jgit.storage.file;
import java.lang.ref.SoftReference;
diff --git a/org.eclipse.jgit/src/org/eclipse/jgit/lib/WindowCache.java b/org.eclipse.jgit/src/org/eclipse/jgit/storage/file/WindowCache.java
index a44a30ee2d..39633ee5ef 100644
--- a/org.eclipse.jgit/src/org/eclipse/jgit/lib/WindowCache.java
+++ b/org.eclipse.jgit/src/org/eclipse/jgit/storage/file/WindowCache.java
@@ -42,7 +42,7 @@
* ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
*/
-package org.eclipse.jgit.lib;
+package org.eclipse.jgit.storage.file;
import java.io.IOException;
import java.lang.ref.ReferenceQueue;
diff --git a/org.eclipse.jgit/src/org/eclipse/jgit/lib/WindowCacheConfig.java b/org.eclipse.jgit/src/org/eclipse/jgit/storage/file/WindowCacheConfig.java
index 2d8aef34ca..48d7018e42 100644
--- a/org.eclipse.jgit/src/org/eclipse/jgit/lib/WindowCacheConfig.java
+++ b/org.eclipse.jgit/src/org/eclipse/jgit/storage/file/WindowCacheConfig.java
@@ -41,7 +41,9 @@
* ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
*/
-package org.eclipse.jgit.lib;
+package org.eclipse.jgit.storage.file;
+
+import org.eclipse.jgit.lib.Config;
/** Configuration parameters for {@link WindowCache}. */
public class WindowCacheConfig {
diff --git a/org.eclipse.jgit/src/org/eclipse/jgit/lib/WindowCursor.java b/org.eclipse.jgit/src/org/eclipse/jgit/storage/file/WindowCursor.java
index 968c92e5ce..5376d077ad 100644
--- a/org.eclipse.jgit/src/org/eclipse/jgit/lib/WindowCursor.java
+++ b/org.eclipse.jgit/src/org/eclipse/jgit/storage/file/WindowCursor.java
@@ -42,14 +42,28 @@
* ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
*/
-package org.eclipse.jgit.lib;
+package org.eclipse.jgit.storage.file;
import java.io.IOException;
import java.util.zip.DataFormatException;
import java.util.zip.Inflater;
+import org.eclipse.jgit.errors.IncorrectObjectTypeException;
+import org.eclipse.jgit.errors.MissingObjectException;
+import org.eclipse.jgit.errors.StoredObjectRepresentationNotAvailableException;
+import org.eclipse.jgit.lib.AnyObjectId;
+import org.eclipse.jgit.lib.Constants;
+import org.eclipse.jgit.lib.InflaterCache;
+import org.eclipse.jgit.lib.ObjectLoader;
+import org.eclipse.jgit.lib.ObjectReader;
+import org.eclipse.jgit.revwalk.RevObject;
+import org.eclipse.jgit.storage.pack.ObjectReuseAsIs;
+import org.eclipse.jgit.storage.pack.ObjectToPack;
+import org.eclipse.jgit.storage.pack.PackOutputStream;
+import org.eclipse.jgit.storage.pack.PackWriter;
+
/** Active handle to a ByteWindow. */
-public final class WindowCursor {
+final class WindowCursor extends ObjectReader implements ObjectReuseAsIs {
/** Temporary buffer large enough for at least one raw object id. */
final byte[] tempId = new byte[Constants.OBJECT_ID_LENGTH];
@@ -57,6 +71,62 @@ public final class WindowCursor {
private ByteWindow window;
+ final FileObjectDatabase db;
+
+ WindowCursor(FileObjectDatabase db) {
+ this.db = db;
+ }
+
+ @Override
+ public ObjectReader newReader() {
+ return new WindowCursor(db);
+ }
+
+ public boolean has(AnyObjectId objectId) throws IOException {
+ return db.has(objectId);
+ }
+
+ public ObjectLoader open(AnyObjectId objectId, int typeHint)
+ throws MissingObjectException, IncorrectObjectTypeException,
+ IOException {
+ final ObjectLoader ldr = db.openObject(this, objectId);
+ if (ldr == null) {
+ if (typeHint == OBJ_ANY)
+ throw new MissingObjectException(objectId.copy(), "unknown");
+ throw new MissingObjectException(objectId.copy(), typeHint);
+ }
+ if (typeHint != OBJ_ANY && ldr.getType() != typeHint)
+ throw new IncorrectObjectTypeException(objectId.copy(), typeHint);
+ return ldr;
+ }
+
+ public long getObjectSize(AnyObjectId objectId, int typeHint)
+ throws MissingObjectException, IncorrectObjectTypeException,
+ IOException {
+ long sz = db.getObjectSize(this, objectId);
+ if (sz < 0) {
+ if (typeHint == OBJ_ANY)
+ throw new MissingObjectException(objectId.copy(), "unknown");
+ throw new MissingObjectException(objectId.copy(), typeHint);
+ }
+ return sz;
+ }
+
+ public LocalObjectToPack newObjectToPack(RevObject obj) {
+ return new LocalObjectToPack(obj);
+ }
+
+ public void selectObjectRepresentation(PackWriter packer, ObjectToPack otp)
+ throws IOException, MissingObjectException {
+ db.selectObjectRepresentation(packer, otp, this);
+ }
+
+ public void copyObjectAsIs(PackOutputStream out, ObjectToPack otp)
+ throws IOException, StoredObjectRepresentationNotAvailableException {
+ LocalObjectToPack src = (LocalObjectToPack) otp;
+ src.pack.copyAsIs(out, src, this);
+ }
+
/**
* Copy bytes from the window to a caller supplied buffer.
*
@@ -73,8 +143,8 @@ public final class WindowCursor {
* bytes remaining in the window starting at offset
* <code>pos</code>.
* @return number of bytes actually copied; this may be less than
- * <code>cnt</code> if <code>cnt</code> exceeded the number of
- * bytes available.
+ * <code>cnt</code> if <code>cnt</code> exceeded the number of bytes
+ * available.
* @throws IOException
* this cursor does not match the provider or id and the proper
* window could not be acquired through the provider's cache.
@@ -94,7 +164,7 @@ public final class WindowCursor {
}
/**
- * Pump bytes into the supplied inflater as input.
+ * Inflate a region of the pack starting at {@code position}.
*
* @param pack
* the file the desired window is stored within.
@@ -117,25 +187,36 @@ public final class WindowCursor {
int inflate(final PackFile pack, long position, final byte[] dstbuf,
int dstoff) throws IOException, DataFormatException {
prepareInflater();
- for (;;) {
- pin(pack, position);
- dstoff = window.inflate(position, dstbuf, dstoff, inf);
- if (inf.finished())
- return dstoff;
- position = window.end;
- }
+ pin(pack, position);
+ position += window.setInput(position, inf);
+ do {
+ int n = inf.inflate(dstbuf, dstoff, dstbuf.length - dstoff);
+ if (n == 0) {
+ if (inf.needsInput()) {
+ pin(pack, position);
+ position += window.setInput(position, inf);
+ } else if (inf.finished())
+ return dstoff;
+ else
+ throw new DataFormatException();
+ }
+ dstoff += n;
+ } while (dstoff < dstbuf.length);
+ return dstoff;
}
- void inflateVerify(final PackFile pack, long position)
- throws IOException, DataFormatException {
+ ByteArrayWindow quickCopy(PackFile p, long pos, long cnt)
+ throws IOException {
+ pin(p, pos);
+ if (window instanceof ByteArrayWindow
+ && window.contains(p, pos + (cnt - 1)))
+ return (ByteArrayWindow) window;
+ return null;
+ }
+
+ Inflater inflater() {
prepareInflater();
- for (;;) {
- pin(pack, position);
- window.inflateVerify(position, inf);
- if (inf.finished())
- return;
- position = window.end;
- }
+ return inf;
}
private void prepareInflater() {
@@ -145,7 +226,7 @@ public final class WindowCursor {
inf.reset();
}
- private void pin(final PackFile pack, final long position)
+ void pin(final PackFile pack, final long position)
throws IOException {
final ByteWindow w = window;
if (w == null || !w.contains(pack, position)) {
@@ -159,6 +240,12 @@ public final class WindowCursor {
}
}
+ int getStreamFileThreshold() {
+ if (db == null)
+ return ObjectLoader.STREAM_THRESHOLD;
+ return db.getStreamFileThreshold();
+ }
+
/** Release the current window cursor. */
public void release() {
window = null;
@@ -168,14 +255,4 @@ public final class WindowCursor {
inf = null;
}
}
-
- /**
- * @param curs cursor to release; may be null.
- * @return always null.
- */
- public static WindowCursor release(final WindowCursor curs) {
- if (curs != null)
- curs.release();
- return null;
- }
}
diff --git a/org.eclipse.jgit/src/org/eclipse/jgit/lib/BinaryDelta.java b/org.eclipse.jgit/src/org/eclipse/jgit/storage/pack/BinaryDelta.java
index a59b335330..1d433d78a6 100644
--- a/org.eclipse.jgit/src/org/eclipse/jgit/lib/BinaryDelta.java
+++ b/org.eclipse.jgit/src/org/eclipse/jgit/storage/pack/BinaryDelta.java
@@ -42,9 +42,11 @@
* ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
*/
-package org.eclipse.jgit.lib;
+package org.eclipse.jgit.storage.pack;
import org.eclipse.jgit.JGitText;
+import org.eclipse.jgit.util.QuotedString;
+import org.eclipse.jgit.util.RawParseUtils;
/**
* Recreate a stream from a base stream and a GIT pack delta.
@@ -55,6 +57,51 @@ import org.eclipse.jgit.JGitText;
* </p>
*/
public class BinaryDelta {
+ /**
+ * Length of the base object in the delta stream.
+ *
+ * @param delta
+ * the delta stream, or at least the header of it.
+ * @return the base object's size.
+ */
+ public static long getBaseSize(final byte[] delta) {
+ int p = 0;
+ long baseLen = 0;
+ int c, shift = 0;
+ do {
+ c = delta[p++] & 0xff;
+ baseLen |= (c & 0x7f) << shift;
+ shift += 7;
+ } while ((c & 0x80) != 0);
+ return baseLen;
+ }
+
+ /**
+ * Length of the resulting object in the delta stream.
+ *
+ * @param delta
+ * the delta stream, or at least the header of it.
+ * @return the resulting object's size.
+ */
+ public static long getResultSize(final byte[] delta) {
+ int p = 0;
+
+ // Skip length of the base object.
+ //
+ int c;
+ do {
+ c = delta[p++] & 0xff;
+ } while ((c & 0x80) != 0);
+
+ long resLen = 0;
+ int shift = 0;
+ do {
+ c = delta[p++] & 0xff;
+ resLen |= (c & 0x7f) << shift;
+ shift += 7;
+ } while ((c & 0x80) != 0);
+ return resLen;
+ }
/**
* Apply the changes defined by delta to the data in base, yielding a new
@@ -80,7 +127,8 @@ public class BinaryDelta {
shift += 7;
} while ((c & 0x80) != 0);
if (base.length != baseLen)
- throw new IllegalArgumentException(JGitText.get().baseLengthIncorrect);
+ throw new IllegalArgumentException(
+ JGitText.get().baseLengthIncorrect);
// Length of the resulting object (a variable length int).
//
@@ -134,10 +182,105 @@ public class BinaryDelta {
// cmd == 0 has been reserved for future encoding but
// for now its not acceptable.
//
- throw new IllegalArgumentException(JGitText.get().unsupportedCommand0);
+ throw new IllegalArgumentException(
+ JGitText.get().unsupportedCommand0);
}
}
return result;
}
+
+ /**
+ * Format this delta as a human readable string.
+ *
+ * @param delta
+ * the delta instruction sequence to format.
+ * @return the formatted delta.
+ */
+ public static String format(byte[] delta) {
+ return format(delta, true);
+ }
+
+ /**
+ * Format this delta as a human readable string.
+ *
+ * @param delta
+ * the delta instruction sequence to format.
+ * @param includeHeader
+ * true if the header (base size and result size) should be
+ * included in the formatting.
+ * @return the formatted delta.
+ */
+ public static String format(byte[] delta, boolean includeHeader) {
+ StringBuilder r = new StringBuilder();
+ int deltaPtr = 0;
+
+ long baseLen = 0;
+ int c, shift = 0;
+ do {
+ c = delta[deltaPtr++] & 0xff;
+ baseLen |= (c & 0x7f) << shift;
+ shift += 7;
+ } while ((c & 0x80) != 0);
+
+ long resLen = 0;
+ shift = 0;
+ do {
+ c = delta[deltaPtr++] & 0xff;
+ resLen |= (c & 0x7f) << shift;
+ shift += 7;
+ } while ((c & 0x80) != 0);
+
+ if (includeHeader)
+ r.append("DELTA( BASE=" + baseLen + " RESULT=" + resLen + " )\n");
+
+ while (deltaPtr < delta.length) {
+ final int cmd = delta[deltaPtr++] & 0xff;
+ if ((cmd & 0x80) != 0) {
+ // Determine the segment of the base which should
+ // be copied into the output. The segment is given
+ // as an offset and a length.
+ //
+ int copyOffset = 0;
+ if ((cmd & 0x01) != 0)
+ copyOffset = delta[deltaPtr++] & 0xff;
+ if ((cmd & 0x02) != 0)
+ copyOffset |= (delta[deltaPtr++] & 0xff) << 8;
+ if ((cmd & 0x04) != 0)
+ copyOffset |= (delta[deltaPtr++] & 0xff) << 16;
+ if ((cmd & 0x08) != 0)
+ copyOffset |= (delta[deltaPtr++] & 0xff) << 24;
+
+ int copySize = 0;
+ if ((cmd & 0x10) != 0)
+ copySize = delta[deltaPtr++] & 0xff;
+ if ((cmd & 0x20) != 0)
+ copySize |= (delta[deltaPtr++] & 0xff) << 8;
+ if ((cmd & 0x40) != 0)
+ copySize |= (delta[deltaPtr++] & 0xff) << 16;
+ if (copySize == 0)
+ copySize = 0x10000;
+
+ r.append(" COPY (" + copyOffset + ", " + copySize + ")\n");
+
+ } else if (cmd != 0) {
+ // Anything else the data is literal within the delta
+ // itself.
+ //
+ r.append(" INSERT(");
+ r.append(QuotedString.GIT_PATH.quote(RawParseUtils.decode(
+ delta, deltaPtr, deltaPtr + cmd)));
+ r.append(")\n");
+ deltaPtr += cmd;
+ } else {
+ // cmd == 0 has been reserved for future encoding but
+ // for now its not acceptable.
+ //
+ throw new IllegalArgumentException(
+ JGitText.get().unsupportedCommand0);
+ }
+ }
+
+ return r.toString();
+ }
}
diff --git a/org.eclipse.jgit/src/org/eclipse/jgit/storage/pack/DeltaCache.java b/org.eclipse.jgit/src/org/eclipse/jgit/storage/pack/DeltaCache.java
new file mode 100644
index 0000000000..b6a7436f16
--- /dev/null
+++ b/org.eclipse.jgit/src/org/eclipse/jgit/storage/pack/DeltaCache.java
@@ -0,0 +1,134 @@
+/*
+ * Copyright (C) 2010, Google Inc.
+ * and other copyright owners as documented in the project's IP log.
+ *
+ * This program and the accompanying materials are made available
+ * under the terms of the Eclipse Distribution License v1.0 which
+ * accompanies this distribution, is reproduced below, and is
+ * available at http://www.eclipse.org/org/documents/edl-v10.php
+ *
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or
+ * without modification, are permitted provided that the following
+ * conditions are met:
+ *
+ * - Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ *
+ * - Redistributions in binary form must reproduce the above
+ * copyright notice, this list of conditions and the following
+ * disclaimer in the documentation and/or other materials provided
+ * with the distribution.
+ *
+ * - Neither the name of the Eclipse Foundation, Inc. nor the
+ * names of its contributors may be used to endorse or promote
+ * products derived from this software without specific prior
+ * written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND
+ * CONTRIBUTORS "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES,
+ * INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
+ * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR
+ * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
+ * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT
+ * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
+ * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER
+ * CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT,
+ * STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
+ * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF
+ * ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ */
+
+package org.eclipse.jgit.storage.pack;
+
+import java.lang.ref.ReferenceQueue;
+import java.lang.ref.SoftReference;
+
+class DeltaCache {
+ private final long size;
+
+ private final int entryLimit;
+
+ private final ReferenceQueue<byte[]> queue;
+
+ private long used;
+
+ DeltaCache(PackWriter pw) {
+ size = pw.getDeltaCacheSize();
+ entryLimit = pw.getDeltaCacheLimit();
+ queue = new ReferenceQueue<byte[]>();
+ }
+
+ boolean canCache(int length, ObjectToPack src, ObjectToPack res) {
+ // If the cache would overflow, don't store.
+ //
+ if (0 < size && size < used + length) {
+ checkForGarbageCollectedObjects();
+ if (0 < size && size < used + length)
+ return false;
+ }
+
+ if (length < entryLimit) {
+ used += length;
+ return true;
+ }
+
+ // If the combined source files are multiple megabytes but the delta
+ // is on the order of a kilobyte or two, this was likely costly to
+ // construct. Cache it anyway, even though its over the limit.
+ //
+ if (length >> 10 < (src.getWeight() >> 20) + (res.getWeight() >> 21)) {
+ used += length;
+ return true;
+ }
+
+ return false;
+ }
+
+ void credit(int reservedSize) {
+ used -= reservedSize;
+ }
+
+ Ref cache(byte[] data, int actLen, int reservedSize) {
+ // The caller may have had to allocate more space than is
+ // required. If we are about to waste anything, shrink it.
+ //
+ data = resize(data, actLen);
+
+ // When we reserved space for this item we did it for the
+ // inflated size of the delta, but we were just given the
+ // compressed version. Adjust the cache cost to match.
+ //
+ if (reservedSize != data.length) {
+ used -= reservedSize;
+ used += data.length;
+ }
+ return new Ref(data, queue);
+ }
+
+ byte[] resize(byte[] data, int actLen) {
+ if (data.length != actLen) {
+ byte[] nbuf = new byte[actLen];
+ System.arraycopy(data, 0, nbuf, 0, actLen);
+ data = nbuf;
+ }
+ return data;
+ }
+
+ private void checkForGarbageCollectedObjects() {
+ Ref r;
+ while ((r = (Ref) queue.poll()) != null)
+ used -= r.cost;
+ }
+
+ static class Ref extends SoftReference<byte[]> {
+ final int cost;
+
+ Ref(byte[] array, ReferenceQueue<byte[]> queue) {
+ super(array, queue);
+ cost = array.length;
+ }
+ }
+}
diff --git a/org.eclipse.jgit/src/org/eclipse/jgit/storage/pack/DeltaEncoder.java b/org.eclipse.jgit/src/org/eclipse/jgit/storage/pack/DeltaEncoder.java
new file mode 100644
index 0000000000..204030b4a7
--- /dev/null
+++ b/org.eclipse.jgit/src/org/eclipse/jgit/storage/pack/DeltaEncoder.java
@@ -0,0 +1,294 @@
+/*
+ * Copyright (C) 2010, Google Inc.
+ * and other copyright owners as documented in the project's IP log.
+ *
+ * This program and the accompanying materials are made available
+ * under the terms of the Eclipse Distribution License v1.0 which
+ * accompanies this distribution, is reproduced below, and is
+ * available at http://www.eclipse.org/org/documents/edl-v10.php
+ *
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or
+ * without modification, are permitted provided that the following
+ * conditions are met:
+ *
+ * - Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ *
+ * - Redistributions in binary form must reproduce the above
+ * copyright notice, this list of conditions and the following
+ * disclaimer in the documentation and/or other materials provided
+ * with the distribution.
+ *
+ * - Neither the name of the Eclipse Foundation, Inc. nor the
+ * names of its contributors may be used to endorse or promote
+ * products derived from this software without specific prior
+ * written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND
+ * CONTRIBUTORS "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES,
+ * INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
+ * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR
+ * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
+ * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT
+ * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
+ * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER
+ * CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT,
+ * STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
+ * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF
+ * ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ */
+
+package org.eclipse.jgit.storage.pack;
+
+import java.io.IOException;
+import java.io.OutputStream;
+
+import org.eclipse.jgit.lib.Constants;
+
+/** Encodes an instruction stream for {@link BinaryDelta}. */
+public class DeltaEncoder {
+ /**
+ * Maximum number of bytes to be copied in pack v2 format.
+ * <p>
+ * Historical limitations have this at 64k, even though current delta
+ * decoders recognize larger copy instructions.
+ */
+ private static final int MAX_V2_COPY = 0x10000;
+
+ /*
+ * Maximum number of bytes to be copied in pack v3 format.
+ *
+ * Current delta decoders can recognize a copy instruction with a count that
+ * is this large, but the historical limitation of {@link MAX_V2_COPY} is
+ * still used.
+ */
+ // private static final int MAX_V3_COPY = (0xff << 16) | (0xff << 8) | 0xff;
+
+ /** Maximum number of bytes used by a copy instruction. */
+ private static final int MAX_COPY_CMD_SIZE = 8;
+
+ /** Maximum length that an an insert command can encode at once. */
+ private static final int MAX_INSERT_DATA_SIZE = 127;
+
+ private final OutputStream out;
+
+ private final byte[] buf = new byte[MAX_COPY_CMD_SIZE * 4];
+
+ private final int limit;
+
+ private int size;
+
+ /**
+ * Create an encoder with no upper bound on the instruction stream size.
+ *
+ * @param out
+ * buffer to store the instructions written.
+ * @param baseSize
+ * size of the base object, in bytes.
+ * @param resultSize
+ * size of the resulting object, after applying this instruction
+ * stream to the base object, in bytes.
+ * @throws IOException
+ * the output buffer cannot store the instruction stream's
+ * header with the size fields.
+ */
+ public DeltaEncoder(OutputStream out, long baseSize, long resultSize)
+ throws IOException {
+ this(out, baseSize, resultSize, 0);
+ }
+
+ /**
+ * Create an encoder with an upper limit on the instruction size.
+ *
+ * @param out
+ * buffer to store the instructions written.
+ * @param baseSize
+ * size of the base object, in bytes.
+ * @param resultSize
+ * size of the resulting object, after applying this instruction
+ * stream to the base object, in bytes.
+ * @param limit
+ * maximum number of bytes to write to the out buffer declaring
+ * the stream is over limit and should be discarded. May be 0 to
+ * specify an infinite limit.
+ * @throws IOException
+ * the output buffer cannot store the instruction stream's
+ * header with the size fields.
+ */
+ public DeltaEncoder(OutputStream out, long baseSize, long resultSize,
+ int limit) throws IOException {
+ this.out = out;
+ this.limit = limit;
+ writeVarint(baseSize);
+ writeVarint(resultSize);
+ }
+
+ private void writeVarint(long sz) throws IOException {
+ int p = 0;
+ while (sz >= 0x80) {
+ buf[p++] = (byte) (0x80 | (((int) sz) & 0x7f));
+ sz >>>= 7;
+ }
+ buf[p++] = (byte) (((int) sz) & 0x7f);
+ size += p;
+ if (limit <= 0 || size < limit)
+ out.write(buf, 0, p);
+ }
+
+ /** @return current size of the delta stream, in bytes. */
+ public int getSize() {
+ return size;
+ }
+
+ /**
+ * Insert a literal string of text, in UTF-8 encoding.
+ *
+ * @param text
+ * the string to insert.
+ * @return true if the insert fits within the limit; false if the insert
+ * would cause the instruction stream to exceed the limit.
+ * @throws IOException
+ * the instruction buffer can't store the instructions.
+ */
+ public boolean insert(String text) throws IOException {
+ return insert(Constants.encode(text));
+ }
+
+ /**
+ * Insert a literal binary sequence.
+ *
+ * @param text
+ * the binary to insert.
+ * @return true if the insert fits within the limit; false if the insert
+ * would cause the instruction stream to exceed the limit.
+ * @throws IOException
+ * the instruction buffer can't store the instructions.
+ */
+ public boolean insert(byte[] text) throws IOException {
+ return insert(text, 0, text.length);
+ }
+
+ /**
+ * Insert a literal binary sequence.
+ *
+ * @param text
+ * the binary to insert.
+ * @param off
+ * offset within {@code text} to start copying from.
+ * @param cnt
+ * number of bytes to insert.
+ * @return true if the insert fits within the limit; false if the insert
+ * would cause the instruction stream to exceed the limit.
+ * @throws IOException
+ * the instruction buffer can't store the instructions.
+ */
+ public boolean insert(byte[] text, int off, int cnt)
+ throws IOException {
+ if (cnt <= 0)
+ return true;
+ if (0 < limit) {
+ int hdrs = cnt / MAX_INSERT_DATA_SIZE;
+ if (cnt % MAX_INSERT_DATA_SIZE != 0)
+ hdrs++;
+ if (limit < size + hdrs + cnt)
+ return false;
+ }
+ do {
+ int n = Math.min(MAX_INSERT_DATA_SIZE, cnt);
+ out.write((byte) n);
+ out.write(text, off, n);
+ off += n;
+ cnt -= n;
+ size += 1 + n;
+ } while (0 < cnt);
+ return true;
+ }
+
+ /**
+ * Create a copy instruction to copy from the base object.
+ *
+ * @param offset
+ * position in the base object to copy from. This is absolute,
+ * from the beginning of the base.
+ * @param cnt
+ * number of bytes to copy.
+ * @return true if the copy fits within the limit; false if the copy
+ * would cause the instruction stream to exceed the limit.
+ * @throws IOException
+ * the instruction buffer cannot store the instructions.
+ */
+ public boolean copy(long offset, int cnt) throws IOException {
+ if (cnt == 0)
+ return true;
+
+ int p = 0;
+
+ // We cannot encode more than MAX_V2_COPY bytes in a single
+ // command, so encode that much and start a new command.
+ // This limit is imposed by the pack file format rules.
+ //
+ while (MAX_V2_COPY < cnt) {
+ p = encodeCopy(p, offset, MAX_V2_COPY);
+ offset += MAX_V2_COPY;
+ cnt -= MAX_V2_COPY;
+
+ if (buf.length < p + MAX_COPY_CMD_SIZE) {
+ if (0 < limit && limit < size + p)
+ return false;
+ out.write(buf, 0, p);
+ size += p;
+ p = 0;
+ }
+ }
+
+ p = encodeCopy(p, offset, cnt);
+ if (0 < limit && limit < size + p)
+ return false;
+ out.write(buf, 0, p);
+ size += p;
+ return true;
+ }
+
+ private int encodeCopy(int p, long offset, int cnt) {
+ int cmd = 0x80;
+ final int cmdPtr = p++; // save room for the command
+
+ if ((offset & 0xff) != 0) {
+ cmd |= 0x01;
+ buf[p++] = (byte) (offset & 0xff);
+ }
+ if ((offset & (0xff << 8)) != 0) {
+ cmd |= 0x02;
+ buf[p++] = (byte) ((offset >>> 8) & 0xff);
+ }
+ if ((offset & (0xff << 16)) != 0) {
+ cmd |= 0x04;
+ buf[p++] = (byte) ((offset >>> 16) & 0xff);
+ }
+ if ((offset & (0xff << 24)) != 0) {
+ cmd |= 0x08;
+ buf[p++] = (byte) ((offset >>> 24) & 0xff);
+ }
+
+ if (cnt != MAX_V2_COPY) {
+ if ((cnt & 0xff) != 0) {
+ cmd |= 0x10;
+ buf[p++] = (byte) (cnt & 0xff);
+ }
+ if ((cnt & (0xff << 8)) != 0) {
+ cmd |= 0x20;
+ buf[p++] = (byte) ((cnt >>> 8) & 0xff);
+ }
+ if ((cnt & (0xff << 16)) != 0) {
+ cmd |= 0x40;
+ buf[p++] = (byte) ((cnt >>> 16) & 0xff);
+ }
+ }
+
+ buf[cmdPtr] = (byte) cmd;
+ return p;
+ }
+}
diff --git a/org.eclipse.jgit/src/org/eclipse/jgit/storage/pack/DeltaIndex.java b/org.eclipse.jgit/src/org/eclipse/jgit/storage/pack/DeltaIndex.java
new file mode 100644
index 0000000000..e548cc9365
--- /dev/null
+++ b/org.eclipse.jgit/src/org/eclipse/jgit/storage/pack/DeltaIndex.java
@@ -0,0 +1,586 @@
+/*
+ * Copyright (C) 2010, Google Inc.
+ * and other copyright owners as documented in the project's IP log.
+ *
+ * This program and the accompanying materials are made available
+ * under the terms of the Eclipse Distribution License v1.0 which
+ * accompanies this distribution, is reproduced below, and is
+ * available at http://www.eclipse.org/org/documents/edl-v10.php
+ *
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or
+ * without modification, are permitted provided that the following
+ * conditions are met:
+ *
+ * - Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ *
+ * - Redistributions in binary form must reproduce the above
+ * copyright notice, this list of conditions and the following
+ * disclaimer in the documentation and/or other materials provided
+ * with the distribution.
+ *
+ * - Neither the name of the Eclipse Foundation, Inc. nor the
+ * names of its contributors may be used to endorse or promote
+ * products derived from this software without specific prior
+ * written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND
+ * CONTRIBUTORS "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES,
+ * INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
+ * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR
+ * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
+ * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT
+ * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
+ * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER
+ * CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT,
+ * STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
+ * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF
+ * ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ */
+
+package org.eclipse.jgit.storage.pack;
+
+import java.io.IOException;
+import java.io.OutputStream;
+
+/**
+ * Index of blocks in a source file.
+ * <p>
+ * The index can be passed a result buffer, and output an instruction sequence
+ * that transforms the source buffer used by the index into the result buffer.
+ * The instruction sequence can be executed by {@link BinaryDelta} or
+ * {@link DeltaStream} to recreate the result buffer.
+ * <p>
+ * An index stores the entire contents of the source buffer, but also a table of
+ * block identities mapped to locations where the block appears in the source
+ * buffer. The mapping table uses 12 bytes for every 16 bytes of source buffer,
+ * and is therefore ~75% of the source buffer size. The overall index is ~1.75x
+ * the size of the source buffer. This relationship holds for any JVM, as only a
+ * constant number of objects are allocated per index. Callers can use the
+ * method {@link #getIndexSize()} to obtain a reasonably accurate estimate of
+ * the complete heap space used by this index.
+ * <p>
+ * A {@code DeltaIndex} is thread-safe. Concurrent threads can use the same
+ * index to encode delta instructions for different result buffers.
+ */
+public class DeltaIndex {
+ /** Number of bytes in a block. */
+ static final int BLKSZ = 16; // must be 16, see unrolled loop in hashBlock
+
+ /**
+ * Estimate the size of an index for a given source.
+ * <p>
+ * This is roughly a worst-case estimate. The actual index may be smaller.
+ *
+ * @param sourceLength
+ * length of the source, in bytes.
+ * @return estimated size. Approximately {@code 1.75 * sourceLength}.
+ */
+ public static long estimateIndexSize(int sourceLength) {
+ return sourceLength + (sourceLength * 3 / 4);
+ }
+
+ /**
+ * Maximum number of positions to consider for a given content hash.
+ * <p>
+ * All positions with the same content hash are stored into a single chain.
+ * The chain size is capped to ensure delta encoding stays linear time at
+ * O(len_src + len_dst) rather than quadratic at O(len_src * len_dst).
+ */
+ private static final int MAX_CHAIN_LENGTH = 64;
+
+ /** Original source file that we indexed. */
+ private final byte[] src;
+
+ /**
+ * Pointers into the {@link #entries} table, indexed by block hash.
+ * <p>
+ * A block hash is masked with {@link #tableMask} to become the array index
+ * of this table. The value stored here is the first index within
+ * {@link #entries} that starts the consecutive list of blocks with that
+ * same masked hash. If there are no matching blocks, 0 is stored instead.
+ * <p>
+ * Note that this table is always a power of 2 in size, to support fast
+ * normalization of a block hash into an array index.
+ */
+ private final int[] table;
+
+ /**
+ * Pairs of block hash value and {@link #src} offsets.
+ * <p>
+ * The very first entry in this table at index 0 is always empty, this is to
+ * allow fast evaluation when {@link #table} has no values under any given
+ * slot. Remaining entries are pairs of integers, with the upper 32 bits
+ * holding the block hash and the lower 32 bits holding the source offset.
+ */
+ private final long[] entries;
+
+ /** Mask to make block hashes into an array index for {@link #table}. */
+ private final int tableMask;
+
+ /**
+ * Construct an index from the source file.
+ *
+ * @param sourceBuffer
+ * the source file's raw contents. The buffer will be held by the
+ * index instance to facilitate matching, and therefore must not
+ * be modified by the caller.
+ */
+ public DeltaIndex(byte[] sourceBuffer) {
+ src = sourceBuffer;
+
+ DeltaIndexScanner scan = new DeltaIndexScanner(src, src.length);
+
+ // Reuse the same table the scanner made. We will replace the
+ // values at each position, but we want the same-length array.
+ //
+ table = scan.table;
+ tableMask = scan.tableMask;
+
+ // Because entry index 0 means there are no entries for the
+ // slot in the table, we have to allocate one extra position.
+ //
+ entries = new long[1 + countEntries(scan)];
+ copyEntries(scan);
+ }
+
+ private int countEntries(DeltaIndexScanner scan) {
+ // Figure out exactly how many entries we need. As we do the
+ // enumeration truncate any delta chains longer than what we
+ // are willing to scan during encode. This keeps the encode
+ // logic linear in the size of the input rather than quadratic.
+ //
+ int cnt = 0;
+ for (int i = 0; i < table.length; i++) {
+ int h = table[i];
+ if (h == 0)
+ continue;
+
+ int len = 0;
+ do {
+ if (++len == MAX_CHAIN_LENGTH) {
+ scan.next[h] = 0;
+ break;
+ }
+ h = scan.next[h];
+ } while (h != 0);
+ cnt += len;
+ }
+ return cnt;
+ }
+
+ private void copyEntries(DeltaIndexScanner scan) {
+ // Rebuild the entries list from the scanner, positioning all
+ // blocks in the same hash chain next to each other. We can
+ // then later discard the next list, along with the scanner.
+ //
+ int next = 1;
+ for (int i = 0; i < table.length; i++) {
+ int h = table[i];
+ if (h == 0)
+ continue;
+
+ table[i] = next;
+ do {
+ entries[next++] = scan.entries[h];
+ h = scan.next[h];
+ } while (h != 0);
+ }
+ }
+
+ /** @return size of the source buffer this index has scanned. */
+ public long getSourceSize() {
+ return src.length;
+ }
+
+ /**
+ * Get an estimate of the memory required by this index.
+ *
+ * @return an approximation of the number of bytes used by this index in
+ * memory. The size includes the cached source buffer size from
+ * {@link #getSourceSize()}, as well as a rough approximation of JVM
+ * object overheads.
+ */
+ public long getIndexSize() {
+ long sz = 8 /* object header */;
+ sz += 4 /* fields */* 4 /* guessed size per field */;
+ sz += sizeOf(src);
+ sz += sizeOf(table);
+ sz += sizeOf(entries);
+ return sz;
+ }
+
+ private static long sizeOf(byte[] b) {
+ return sizeOfArray(1, b.length);
+ }
+
+ private static long sizeOf(int[] b) {
+ return sizeOfArray(4, b.length);
+ }
+
+ private static long sizeOf(long[] b) {
+ return sizeOfArray(8, b.length);
+ }
+
+ private static int sizeOfArray(int entSize, int len) {
+ return 12 /* estimated array header size */+ (len * entSize);
+ }
+
+ /**
+ * Generate a delta sequence to recreate the result buffer.
+ * <p>
+ * There is no limit on the size of the delta sequence created. This is the
+ * same as {@code encode(out, res, 0)}.
+ *
+ * @param out
+ * stream to receive the delta instructions that can transform
+ * this index's source buffer into {@code res}. This stream
+ * should be buffered, as instructions are written directly to it
+ * in small bursts.
+ * @param res
+ * the desired result buffer. The generated instructions will
+ * recreate this buffer when applied to the source buffer stored
+ * within this index.
+ * @throws IOException
+ * the output stream refused to write the instructions.
+ */
+ public void encode(OutputStream out, byte[] res) throws IOException {
+ encode(out, res, 0 /* no limit */);
+ }
+
+ /**
+ * Generate a delta sequence to recreate the result buffer.
+ *
+ * @param out
+ * stream to receive the delta instructions that can transform
+ * this index's source buffer into {@code res}. This stream
+ * should be buffered, as instructions are written directly to it
+ * in small bursts. If the caller might need to discard the
+ * instructions (such as when deltaSizeLimit would be exceeded)
+ * the caller is responsible for discarding or rewinding the
+ * stream when this method returns false.
+ * @param res
+ * the desired result buffer. The generated instructions will
+ * recreate this buffer when applied to the source buffer stored
+ * within this index.
+ * @param deltaSizeLimit
+ * maximum number of bytes that the delta instructions can
+ * occupy. If the generated instructions would be longer than
+ * this amount, this method returns false. If 0, there is no
+ * limit on the length of delta created.
+ * @return true if the delta is smaller than deltaSizeLimit; false if the
+ * encoder aborted because the encoded delta instructions would be
+ * longer than deltaSizeLimit bytes.
+ * @throws IOException
+ * the output stream refused to write the instructions.
+ */
+ public boolean encode(OutputStream out, byte[] res, int deltaSizeLimit)
+ throws IOException {
+ final int end = res.length;
+ final DeltaEncoder enc = newEncoder(out, end, deltaSizeLimit);
+
+ // If either input is smaller than one full block, we simply punt
+ // and construct a delta as a literal. This implies that any file
+ // smaller than our block size is never delta encoded as the delta
+ // will always be larger than the file itself would be.
+ //
+ if (end < BLKSZ || table.length == 0)
+ return enc.insert(res);
+
+ // Bootstrap the scan by constructing a hash for the first block
+ // in the input.
+ //
+ int blkPtr = 0;
+ int blkEnd = BLKSZ;
+ int hash = hashBlock(res, 0);
+
+ int resPtr = 0;
+ while (blkEnd < end) {
+ final int tableIdx = hash & tableMask;
+ int entryIdx = table[tableIdx];
+ if (entryIdx == 0) {
+ // No matching blocks, slide forward one byte.
+ //
+ hash = step(hash, res[blkPtr++], res[blkEnd++]);
+ continue;
+ }
+
+ // For every possible location of the current block, try to
+ // extend the match out to the longest common substring.
+ //
+ int bestLen = -1;
+ int bestPtr = -1;
+ int bestNeg = 0;
+ do {
+ long ent = entries[entryIdx++];
+ if (keyOf(ent) == hash) {
+ int neg = 0;
+ if (resPtr < blkPtr) {
+ // If we need to do an insertion, check to see if
+ // moving the starting point of the copy backwards
+ // will allow us to shorten the insert. Our hash
+ // may not have allowed us to identify this area.
+ // Since it is quite fast to perform a negative
+ // scan, try to stretch backwards too.
+ //
+ neg = blkPtr - resPtr;
+ neg = negmatch(res, blkPtr, src, valOf(ent), neg);
+ }
+
+ int len = neg + fwdmatch(res, blkPtr, src, valOf(ent));
+ if (bestLen < len) {
+ bestLen = len;
+ bestPtr = valOf(ent);
+ bestNeg = neg;
+ }
+ } else if ((keyOf(ent) & tableMask) != tableIdx)
+ break;
+ } while (bestLen < 4096 && entryIdx < entries.length);
+
+ if (bestLen < BLKSZ) {
+ // All of the locations were false positives, or the copy
+ // is shorter than a block. In the latter case this won't
+ // give us a very great copy instruction, so delay and try
+ // at the next byte.
+ //
+ hash = step(hash, res[blkPtr++], res[blkEnd++]);
+ continue;
+ }
+
+ blkPtr -= bestNeg;
+
+ if (resPtr < blkPtr) {
+ // There are bytes between the last instruction we made
+ // and the current block pointer. None of these matched
+ // during the earlier iteration so insert them directly
+ // into the instruction stream.
+ //
+ int cnt = blkPtr - resPtr;
+ if (!enc.insert(res, resPtr, cnt))
+ return false;
+ }
+
+ if (!enc.copy(bestPtr - bestNeg, bestLen))
+ return false;
+
+ blkPtr += bestLen;
+ resPtr = blkPtr;
+ blkEnd = blkPtr + BLKSZ;
+
+ // If we don't have a full block available to us, abort now.
+ //
+ if (end <= blkEnd)
+ break;
+
+ // Start a new hash of the block after the copy region.
+ //
+ hash = hashBlock(res, blkPtr);
+ }
+
+ if (resPtr < end) {
+ // There were bytes at the end which didn't match, or maybe
+ // didn't make a full block. Insert whatever is left over.
+ //
+ int cnt = end - resPtr;
+ return enc.insert(res, resPtr, cnt);
+ }
+ return true;
+ }
+
+ private DeltaEncoder newEncoder(OutputStream out, long resSize, int limit)
+ throws IOException {
+ return new DeltaEncoder(out, getSourceSize(), resSize, limit);
+ }
+
+ private static int fwdmatch(byte[] res, int resPtr, byte[] src, int srcPtr) {
+ int start = resPtr;
+ for (; resPtr < res.length && srcPtr < src.length; resPtr++, srcPtr++) {
+ if (res[resPtr] != src[srcPtr])
+ break;
+ }
+ return resPtr - start;
+ }
+
+ private static int negmatch(byte[] res, int resPtr, byte[] src, int srcPtr,
+ int limit) {
+ if (srcPtr == 0)
+ return 0;
+
+ resPtr--;
+ srcPtr--;
+ int start = resPtr;
+ do {
+ if (res[resPtr] != src[srcPtr])
+ break;
+ resPtr--;
+ srcPtr--;
+ } while (0 <= srcPtr && 0 < --limit);
+ return start - resPtr;
+ }
+
+ public String toString() {
+ String[] units = { "bytes", "KiB", "MiB", "GiB" };
+ long sz = getIndexSize();
+ int u = 0;
+ while (1024 <= sz && u < units.length - 1) {
+ int rem = (int) (sz % 1024);
+ sz /= 1024;
+ if (rem != 0)
+ sz++;
+ u++;
+ }
+ return "DeltaIndex[" + sz + " " + units[u] + "]";
+ }
+
+ static int hashBlock(byte[] raw, int ptr) {
+ int hash;
+
+ // The first 4 steps collapse out into a 4 byte big-endian decode,
+ // with a larger right shift as we combined shift lefts together.
+ //
+ hash = ((raw[ptr] & 0xff) << 24) //
+ | ((raw[ptr + 1] & 0xff) << 16) //
+ | ((raw[ptr + 2] & 0xff) << 8) //
+ | (raw[ptr + 3] & 0xff);
+ hash ^= T[hash >>> 31];
+
+ hash = ((hash << 8) | (raw[ptr + 4] & 0xff)) ^ T[hash >>> 23];
+ hash = ((hash << 8) | (raw[ptr + 5] & 0xff)) ^ T[hash >>> 23];
+ hash = ((hash << 8) | (raw[ptr + 6] & 0xff)) ^ T[hash >>> 23];
+ hash = ((hash << 8) | (raw[ptr + 7] & 0xff)) ^ T[hash >>> 23];
+
+ hash = ((hash << 8) | (raw[ptr + 8] & 0xff)) ^ T[hash >>> 23];
+ hash = ((hash << 8) | (raw[ptr + 9] & 0xff)) ^ T[hash >>> 23];
+ hash = ((hash << 8) | (raw[ptr + 10] & 0xff)) ^ T[hash >>> 23];
+ hash = ((hash << 8) | (raw[ptr + 11] & 0xff)) ^ T[hash >>> 23];
+
+ hash = ((hash << 8) | (raw[ptr + 12] & 0xff)) ^ T[hash >>> 23];
+ hash = ((hash << 8) | (raw[ptr + 13] & 0xff)) ^ T[hash >>> 23];
+ hash = ((hash << 8) | (raw[ptr + 14] & 0xff)) ^ T[hash >>> 23];
+ hash = ((hash << 8) | (raw[ptr + 15] & 0xff)) ^ T[hash >>> 23];
+
+ return hash;
+ }
+
+ private static int step(int hash, byte toRemove, byte toAdd) {
+ hash ^= U[toRemove & 0xff];
+ return ((hash << 8) | (toAdd & 0xff)) ^ T[hash >>> 23];
+ }
+
+ private static int keyOf(long ent) {
+ return (int) (ent >>> 32);
+ }
+
+ private static int valOf(long ent) {
+ return (int) ent;
+ }
+
+ private static final int[] T = { 0x00000000, 0xd4c6b32d, 0x7d4bd577,
+ 0xa98d665a, 0x2e5119c3, 0xfa97aaee, 0x531accb4, 0x87dc7f99,
+ 0x5ca23386, 0x886480ab, 0x21e9e6f1, 0xf52f55dc, 0x72f32a45,
+ 0xa6359968, 0x0fb8ff32, 0xdb7e4c1f, 0x6d82d421, 0xb944670c,
+ 0x10c90156, 0xc40fb27b, 0x43d3cde2, 0x97157ecf, 0x3e981895,
+ 0xea5eabb8, 0x3120e7a7, 0xe5e6548a, 0x4c6b32d0, 0x98ad81fd,
+ 0x1f71fe64, 0xcbb74d49, 0x623a2b13, 0xb6fc983e, 0x0fc31b6f,
+ 0xdb05a842, 0x7288ce18, 0xa64e7d35, 0x219202ac, 0xf554b181,
+ 0x5cd9d7db, 0x881f64f6, 0x536128e9, 0x87a79bc4, 0x2e2afd9e,
+ 0xfaec4eb3, 0x7d30312a, 0xa9f68207, 0x007be45d, 0xd4bd5770,
+ 0x6241cf4e, 0xb6877c63, 0x1f0a1a39, 0xcbcca914, 0x4c10d68d,
+ 0x98d665a0, 0x315b03fa, 0xe59db0d7, 0x3ee3fcc8, 0xea254fe5,
+ 0x43a829bf, 0x976e9a92, 0x10b2e50b, 0xc4745626, 0x6df9307c,
+ 0xb93f8351, 0x1f8636de, 0xcb4085f3, 0x62cde3a9, 0xb60b5084,
+ 0x31d72f1d, 0xe5119c30, 0x4c9cfa6a, 0x985a4947, 0x43240558,
+ 0x97e2b675, 0x3e6fd02f, 0xeaa96302, 0x6d751c9b, 0xb9b3afb6,
+ 0x103ec9ec, 0xc4f87ac1, 0x7204e2ff, 0xa6c251d2, 0x0f4f3788,
+ 0xdb8984a5, 0x5c55fb3c, 0x88934811, 0x211e2e4b, 0xf5d89d66,
+ 0x2ea6d179, 0xfa606254, 0x53ed040e, 0x872bb723, 0x00f7c8ba,
+ 0xd4317b97, 0x7dbc1dcd, 0xa97aaee0, 0x10452db1, 0xc4839e9c,
+ 0x6d0ef8c6, 0xb9c84beb, 0x3e143472, 0xead2875f, 0x435fe105,
+ 0x97995228, 0x4ce71e37, 0x9821ad1a, 0x31accb40, 0xe56a786d,
+ 0x62b607f4, 0xb670b4d9, 0x1ffdd283, 0xcb3b61ae, 0x7dc7f990,
+ 0xa9014abd, 0x008c2ce7, 0xd44a9fca, 0x5396e053, 0x8750537e,
+ 0x2edd3524, 0xfa1b8609, 0x2165ca16, 0xf5a3793b, 0x5c2e1f61,
+ 0x88e8ac4c, 0x0f34d3d5, 0xdbf260f8, 0x727f06a2, 0xa6b9b58f,
+ 0x3f0c6dbc, 0xebcade91, 0x4247b8cb, 0x96810be6, 0x115d747f,
+ 0xc59bc752, 0x6c16a108, 0xb8d01225, 0x63ae5e3a, 0xb768ed17,
+ 0x1ee58b4d, 0xca233860, 0x4dff47f9, 0x9939f4d4, 0x30b4928e,
+ 0xe47221a3, 0x528eb99d, 0x86480ab0, 0x2fc56cea, 0xfb03dfc7,
+ 0x7cdfa05e, 0xa8191373, 0x01947529, 0xd552c604, 0x0e2c8a1b,
+ 0xdaea3936, 0x73675f6c, 0xa7a1ec41, 0x207d93d8, 0xf4bb20f5,
+ 0x5d3646af, 0x89f0f582, 0x30cf76d3, 0xe409c5fe, 0x4d84a3a4,
+ 0x99421089, 0x1e9e6f10, 0xca58dc3d, 0x63d5ba67, 0xb713094a,
+ 0x6c6d4555, 0xb8abf678, 0x11269022, 0xc5e0230f, 0x423c5c96,
+ 0x96faefbb, 0x3f7789e1, 0xebb13acc, 0x5d4da2f2, 0x898b11df,
+ 0x20067785, 0xf4c0c4a8, 0x731cbb31, 0xa7da081c, 0x0e576e46,
+ 0xda91dd6b, 0x01ef9174, 0xd5292259, 0x7ca44403, 0xa862f72e,
+ 0x2fbe88b7, 0xfb783b9a, 0x52f55dc0, 0x8633eeed, 0x208a5b62,
+ 0xf44ce84f, 0x5dc18e15, 0x89073d38, 0x0edb42a1, 0xda1df18c,
+ 0x739097d6, 0xa75624fb, 0x7c2868e4, 0xa8eedbc9, 0x0163bd93,
+ 0xd5a50ebe, 0x52797127, 0x86bfc20a, 0x2f32a450, 0xfbf4177d,
+ 0x4d088f43, 0x99ce3c6e, 0x30435a34, 0xe485e919, 0x63599680,
+ 0xb79f25ad, 0x1e1243f7, 0xcad4f0da, 0x11aabcc5, 0xc56c0fe8,
+ 0x6ce169b2, 0xb827da9f, 0x3ffba506, 0xeb3d162b, 0x42b07071,
+ 0x9676c35c, 0x2f49400d, 0xfb8ff320, 0x5202957a, 0x86c42657,
+ 0x011859ce, 0xd5deeae3, 0x7c538cb9, 0xa8953f94, 0x73eb738b,
+ 0xa72dc0a6, 0x0ea0a6fc, 0xda6615d1, 0x5dba6a48, 0x897cd965,
+ 0x20f1bf3f, 0xf4370c12, 0x42cb942c, 0x960d2701, 0x3f80415b,
+ 0xeb46f276, 0x6c9a8def, 0xb85c3ec2, 0x11d15898, 0xc517ebb5,
+ 0x1e69a7aa, 0xcaaf1487, 0x632272dd, 0xb7e4c1f0, 0x3038be69,
+ 0xe4fe0d44, 0x4d736b1e, 0x99b5d833 };
+
+ private static final int[] U = { 0x00000000, 0x12c6e90f, 0x258dd21e,
+ 0x374b3b11, 0x4b1ba43c, 0x59dd4d33, 0x6e967622, 0x7c509f2d,
+ 0x42f1fb55, 0x5037125a, 0x677c294b, 0x75bac044, 0x09ea5f69,
+ 0x1b2cb666, 0x2c678d77, 0x3ea16478, 0x51254587, 0x43e3ac88,
+ 0x74a89799, 0x666e7e96, 0x1a3ee1bb, 0x08f808b4, 0x3fb333a5,
+ 0x2d75daaa, 0x13d4bed2, 0x011257dd, 0x36596ccc, 0x249f85c3,
+ 0x58cf1aee, 0x4a09f3e1, 0x7d42c8f0, 0x6f8421ff, 0x768c3823,
+ 0x644ad12c, 0x5301ea3d, 0x41c70332, 0x3d979c1f, 0x2f517510,
+ 0x181a4e01, 0x0adca70e, 0x347dc376, 0x26bb2a79, 0x11f01168,
+ 0x0336f867, 0x7f66674a, 0x6da08e45, 0x5aebb554, 0x482d5c5b,
+ 0x27a97da4, 0x356f94ab, 0x0224afba, 0x10e246b5, 0x6cb2d998,
+ 0x7e743097, 0x493f0b86, 0x5bf9e289, 0x655886f1, 0x779e6ffe,
+ 0x40d554ef, 0x5213bde0, 0x2e4322cd, 0x3c85cbc2, 0x0bcef0d3,
+ 0x190819dc, 0x39dec36b, 0x2b182a64, 0x1c531175, 0x0e95f87a,
+ 0x72c56757, 0x60038e58, 0x5748b549, 0x458e5c46, 0x7b2f383e,
+ 0x69e9d131, 0x5ea2ea20, 0x4c64032f, 0x30349c02, 0x22f2750d,
+ 0x15b94e1c, 0x077fa713, 0x68fb86ec, 0x7a3d6fe3, 0x4d7654f2,
+ 0x5fb0bdfd, 0x23e022d0, 0x3126cbdf, 0x066df0ce, 0x14ab19c1,
+ 0x2a0a7db9, 0x38cc94b6, 0x0f87afa7, 0x1d4146a8, 0x6111d985,
+ 0x73d7308a, 0x449c0b9b, 0x565ae294, 0x4f52fb48, 0x5d941247,
+ 0x6adf2956, 0x7819c059, 0x04495f74, 0x168fb67b, 0x21c48d6a,
+ 0x33026465, 0x0da3001d, 0x1f65e912, 0x282ed203, 0x3ae83b0c,
+ 0x46b8a421, 0x547e4d2e, 0x6335763f, 0x71f39f30, 0x1e77becf,
+ 0x0cb157c0, 0x3bfa6cd1, 0x293c85de, 0x556c1af3, 0x47aaf3fc,
+ 0x70e1c8ed, 0x622721e2, 0x5c86459a, 0x4e40ac95, 0x790b9784,
+ 0x6bcd7e8b, 0x179de1a6, 0x055b08a9, 0x321033b8, 0x20d6dab7,
+ 0x73bd86d6, 0x617b6fd9, 0x563054c8, 0x44f6bdc7, 0x38a622ea,
+ 0x2a60cbe5, 0x1d2bf0f4, 0x0fed19fb, 0x314c7d83, 0x238a948c,
+ 0x14c1af9d, 0x06074692, 0x7a57d9bf, 0x689130b0, 0x5fda0ba1,
+ 0x4d1ce2ae, 0x2298c351, 0x305e2a5e, 0x0715114f, 0x15d3f840,
+ 0x6983676d, 0x7b458e62, 0x4c0eb573, 0x5ec85c7c, 0x60693804,
+ 0x72afd10b, 0x45e4ea1a, 0x57220315, 0x2b729c38, 0x39b47537,
+ 0x0eff4e26, 0x1c39a729, 0x0531bef5, 0x17f757fa, 0x20bc6ceb,
+ 0x327a85e4, 0x4e2a1ac9, 0x5cecf3c6, 0x6ba7c8d7, 0x796121d8,
+ 0x47c045a0, 0x5506acaf, 0x624d97be, 0x708b7eb1, 0x0cdbe19c,
+ 0x1e1d0893, 0x29563382, 0x3b90da8d, 0x5414fb72, 0x46d2127d,
+ 0x7199296c, 0x635fc063, 0x1f0f5f4e, 0x0dc9b641, 0x3a828d50,
+ 0x2844645f, 0x16e50027, 0x0423e928, 0x3368d239, 0x21ae3b36,
+ 0x5dfea41b, 0x4f384d14, 0x78737605, 0x6ab59f0a, 0x4a6345bd,
+ 0x58a5acb2, 0x6fee97a3, 0x7d287eac, 0x0178e181, 0x13be088e,
+ 0x24f5339f, 0x3633da90, 0x0892bee8, 0x1a5457e7, 0x2d1f6cf6,
+ 0x3fd985f9, 0x43891ad4, 0x514ff3db, 0x6604c8ca, 0x74c221c5,
+ 0x1b46003a, 0x0980e935, 0x3ecbd224, 0x2c0d3b2b, 0x505da406,
+ 0x429b4d09, 0x75d07618, 0x67169f17, 0x59b7fb6f, 0x4b711260,
+ 0x7c3a2971, 0x6efcc07e, 0x12ac5f53, 0x006ab65c, 0x37218d4d,
+ 0x25e76442, 0x3cef7d9e, 0x2e299491, 0x1962af80, 0x0ba4468f,
+ 0x77f4d9a2, 0x653230ad, 0x52790bbc, 0x40bfe2b3, 0x7e1e86cb,
+ 0x6cd86fc4, 0x5b9354d5, 0x4955bdda, 0x350522f7, 0x27c3cbf8,
+ 0x1088f0e9, 0x024e19e6, 0x6dca3819, 0x7f0cd116, 0x4847ea07,
+ 0x5a810308, 0x26d19c25, 0x3417752a, 0x035c4e3b, 0x119aa734,
+ 0x2f3bc34c, 0x3dfd2a43, 0x0ab61152, 0x1870f85d, 0x64206770,
+ 0x76e68e7f, 0x41adb56e, 0x536b5c61 };
+}
diff --git a/org.eclipse.jgit/src/org/eclipse/jgit/storage/pack/DeltaIndexScanner.java b/org.eclipse.jgit/src/org/eclipse/jgit/storage/pack/DeltaIndexScanner.java
new file mode 100644
index 0000000000..d30690d401
--- /dev/null
+++ b/org.eclipse.jgit/src/org/eclipse/jgit/storage/pack/DeltaIndexScanner.java
@@ -0,0 +1,130 @@
+/*
+ * Copyright (C) 2010, Google Inc.
+ * and other copyright owners as documented in the project's IP log.
+ *
+ * This program and the accompanying materials are made available
+ * under the terms of the Eclipse Distribution License v1.0 which
+ * accompanies this distribution, is reproduced below, and is
+ * available at http://www.eclipse.org/org/documents/edl-v10.php
+ *
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or
+ * without modification, are permitted provided that the following
+ * conditions are met:
+ *
+ * - Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ *
+ * - Redistributions in binary form must reproduce the above
+ * copyright notice, this list of conditions and the following
+ * disclaimer in the documentation and/or other materials provided
+ * with the distribution.
+ *
+ * - Neither the name of the Eclipse Foundation, Inc. nor the
+ * names of its contributors may be used to endorse or promote
+ * products derived from this software without specific prior
+ * written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND
+ * CONTRIBUTORS "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES,
+ * INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
+ * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR
+ * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
+ * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT
+ * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
+ * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER
+ * CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT,
+ * STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
+ * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF
+ * ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ */
+
+package org.eclipse.jgit.storage.pack;
+
+/**
+ * Supports {@link DeltaIndex} by performing a partial scan of the content.
+ */
+class DeltaIndexScanner {
+ final int[] table;
+
+ // To save memory the buckets for hash chains are stored in correlated
+ // arrays. This permits us to get 3 values per entry, without paying
+ // the penalty for an object header on each entry.
+
+ final long[] entries;
+
+ final int[] next;
+
+ final int tableMask;
+
+ private int entryCnt;
+
+ DeltaIndexScanner(byte[] raw, int len) {
+ // Clip the length so it falls on a block boundary. We won't
+ // bother to scan the final partial block.
+ //
+ len -= (len % DeltaIndex.BLKSZ);
+
+ final int worstCaseBlockCnt = len / DeltaIndex.BLKSZ;
+ if (worstCaseBlockCnt < 1) {
+ table = new int[] {};
+ tableMask = 0;
+
+ entries = new long[] {};
+ next = new int[] {};
+
+ } else {
+ table = new int[tableSize(worstCaseBlockCnt)];
+ tableMask = table.length - 1;
+
+ // As we insert blocks we preincrement so that 0 is never a
+ // valid entry. Therefore we have to allocate one extra space.
+ //
+ entries = new long[1 + worstCaseBlockCnt];
+ next = new int[entries.length];
+
+ scan(raw, len);
+ }
+ }
+
+ private void scan(byte[] raw, final int end) {
+ // We scan the input backwards, and always insert onto the
+ // front of the chain. This ensures that chains will have lower
+ // offsets at the front of the chain, allowing us to prefer the
+ // earlier match rather than the later match.
+ //
+ int lastHash = 0;
+ int ptr = end - DeltaIndex.BLKSZ;
+ do {
+ final int key = DeltaIndex.hashBlock(raw, ptr);
+ final int tIdx = key & tableMask;
+
+ final int head = table[tIdx];
+ if (head != 0 && lastHash == key) {
+ // Two consecutive blocks have the same content hash,
+ // prefer the earlier block because we want to use the
+ // longest sequence we can during encoding.
+ //
+ entries[head] = (((long) key) << 32) | ptr;
+ } else {
+ final int eIdx = ++entryCnt;
+ entries[eIdx] = (((long) key) << 32) | ptr;
+ next[eIdx] = head;
+ table[tIdx] = eIdx;
+ }
+
+ lastHash = key;
+ ptr -= DeltaIndex.BLKSZ;
+ } while (0 <= ptr);
+ }
+
+ private static int tableSize(final int worstCaseBlockCnt) {
+ int shift = 32 - Integer.numberOfLeadingZeros(worstCaseBlockCnt);
+ int sz = 1 << (shift - 1);
+ if (sz < worstCaseBlockCnt)
+ sz <<= 1;
+ return sz;
+ }
+} \ No newline at end of file
diff --git a/org.eclipse.jgit/src/org/eclipse/jgit/storage/pack/DeltaStream.java b/org.eclipse.jgit/src/org/eclipse/jgit/storage/pack/DeltaStream.java
new file mode 100644
index 0000000000..6f479eb905
--- /dev/null
+++ b/org.eclipse.jgit/src/org/eclipse/jgit/storage/pack/DeltaStream.java
@@ -0,0 +1,341 @@
+/*
+ * Copyright (C) 2007, Robin Rosenberg <robin.rosenberg@dewire.com>
+ * Copyright (C) 2006-2007, Shawn O. Pearce <spearce@spearce.org>
+ * Copyright (C) 2010, Google Inc.
+ * and other copyright owners as documented in the project's IP log.
+ *
+ * This program and the accompanying materials are made available
+ * under the terms of the Eclipse Distribution License v1.0 which
+ * accompanies this distribution, is reproduced below, and is
+ * available at http://www.eclipse.org/org/documents/edl-v10.php
+ *
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or
+ * without modification, are permitted provided that the following
+ * conditions are met:
+ *
+ * - Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ *
+ * - Redistributions in binary form must reproduce the above
+ * copyright notice, this list of conditions and the following
+ * disclaimer in the documentation and/or other materials provided
+ * with the distribution.
+ *
+ * - Neither the name of the Eclipse Foundation, Inc. nor the
+ * names of its contributors may be used to endorse or promote
+ * products derived from this software without specific prior
+ * written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND
+ * CONTRIBUTORS "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES,
+ * INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
+ * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR
+ * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
+ * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT
+ * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
+ * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER
+ * CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT,
+ * STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
+ * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF
+ * ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ */
+
+package org.eclipse.jgit.storage.pack;
+
+import java.io.EOFException;
+import java.io.IOException;
+import java.io.InputStream;
+
+import org.eclipse.jgit.JGitText;
+import org.eclipse.jgit.errors.CorruptObjectException;
+import org.eclipse.jgit.util.IO;
+
+/**
+ * Inflates a delta in an incremental way.
+ * <p>
+ * Implementations must provide a means to access a stream for the base object.
+ * This stream may be accessed multiple times, in order to randomly position it
+ * to match the copy instructions. A {@code DeltaStream} performs an efficient
+ * skip by only moving through the delta stream, making restarts of stacked
+ * deltas reasonably efficient.
+ */
+public abstract class DeltaStream extends InputStream {
+ private static final int CMD_COPY = 0;
+
+ private static final int CMD_INSERT = 1;
+
+ private static final int CMD_EOF = 2;
+
+ private final InputStream deltaStream;
+
+ private long baseSize;
+
+ private long resultSize;
+
+ private final byte[] cmdbuf = new byte[512];
+
+ private int cmdptr;
+
+ private int cmdcnt;
+
+ /** Stream to read from the base object. */
+ private InputStream baseStream;
+
+ /** Current position within {@link #baseStream}. */
+ private long baseOffset;
+
+ private int curcmd;
+
+ /** If {@code curcmd == CMD_COPY}, position the base has to be at. */
+ private long copyOffset;
+
+ /** Total number of bytes in this current command. */
+ private int copySize;
+
+ /**
+ * Construct a delta application stream, reading instructions.
+ *
+ * @param deltaStream
+ * the stream to read delta instructions from.
+ * @throws IOException
+ * the delta instruction stream cannot be read, or is
+ * inconsistent with the the base object information.
+ */
+ public DeltaStream(final InputStream deltaStream) throws IOException {
+ this.deltaStream = deltaStream;
+ if (!fill(cmdbuf.length))
+ throw new EOFException();
+
+ // Length of the base object.
+ //
+ int c, shift = 0;
+ do {
+ c = cmdbuf[cmdptr++] & 0xff;
+ baseSize |= (c & 0x7f) << shift;
+ shift += 7;
+ } while ((c & 0x80) != 0);
+
+ // Length of the resulting object.
+ //
+ shift = 0;
+ do {
+ c = cmdbuf[cmdptr++] & 0xff;
+ resultSize |= (c & 0x7f) << shift;
+ shift += 7;
+ } while ((c & 0x80) != 0);
+
+ curcmd = next();
+ }
+
+ /**
+ * Open the base stream.
+ * <p>
+ * The {@code DeltaStream} may close and reopen the base stream multiple
+ * times if copy instructions use offsets out of order. This can occur if a
+ * large block in the file was moved from near the top, to near the bottom.
+ * In such cases the reopened stream is skipped to the target offset, so
+ * {@code skip(long)} should be as efficient as possible.
+ *
+ * @return stream to read from the base object. This stream should not be
+ * buffered (or should be only minimally buffered), and does not
+ * need to support mark/reset.
+ * @throws IOException
+ * the base object cannot be opened for reading.
+ */
+ protected abstract InputStream openBase() throws IOException;
+
+ /**
+ * @return length of the base object, in bytes.
+ * @throws IOException
+ * the length of the base cannot be determined.
+ */
+ protected abstract long getBaseSize() throws IOException;
+
+ /** @return total size of this stream, in bytes. */
+ public long getSize() {
+ return resultSize;
+ }
+
+ @Override
+ public int read() throws IOException {
+ byte[] buf = new byte[1];
+ int n = read(buf, 0, 1);
+ return n == 1 ? buf[0] & 0xff : -1;
+ }
+
+ @Override
+ public void close() throws IOException {
+ deltaStream.close();
+ if (baseStream != null)
+ baseStream.close();
+ }
+
+ @Override
+ public long skip(long len) throws IOException {
+ long act = 0;
+ while (0 < len) {
+ long n = Math.min(len, copySize);
+ switch (curcmd) {
+ case CMD_COPY:
+ copyOffset += n;
+ break;
+
+ case CMD_INSERT:
+ cmdptr += n;
+ break;
+
+ case CMD_EOF:
+ return act;
+ default:
+ throw new CorruptObjectException(
+ JGitText.get().unsupportedCommand0);
+ }
+
+ act += n;
+ len -= n;
+ copySize -= n;
+ if (copySize == 0)
+ curcmd = next();
+ }
+ return act;
+ }
+
+ @Override
+ public int read(byte[] buf, int off, int len) throws IOException {
+ int act = 0;
+ while (0 < len) {
+ int n = Math.min(len, copySize);
+ switch (curcmd) {
+ case CMD_COPY:
+ seekBase();
+ n = baseStream.read(buf, off, n);
+ if (n < 0)
+ throw new CorruptObjectException(
+ JGitText.get().baseLengthIncorrect);
+ baseOffset += n;
+ break;
+
+ case CMD_INSERT:
+ System.arraycopy(cmdbuf, cmdptr, buf, off, n);
+ cmdptr += n;
+ break;
+
+ case CMD_EOF:
+ return 0 < act ? act : -1;
+ default:
+ throw new CorruptObjectException(
+ JGitText.get().unsupportedCommand0);
+ }
+
+ act += n;
+ off += n;
+ len -= n;
+ copySize -= n;
+ if (copySize == 0)
+ curcmd = next();
+ }
+ return act;
+ }
+
+ private boolean fill(final int need) throws IOException {
+ int n = have();
+ if (need < n)
+ return true;
+ if (n == 0) {
+ cmdptr = 0;
+ cmdcnt = 0;
+ } else if (cmdbuf.length - cmdptr < need) {
+ // There isn't room for the entire worst-case copy command,
+ // so shift the array down to make sure we can use the entire
+ // command without having it span across the end of the array.
+ //
+ System.arraycopy(cmdbuf, cmdptr, cmdbuf, 0, n);
+ cmdptr = 0;
+ cmdcnt = n;
+ }
+
+ do {
+ n = deltaStream.read(cmdbuf, cmdcnt, cmdbuf.length - cmdcnt);
+ if (n < 0)
+ return 0 < have();
+ cmdcnt += n;
+ } while (cmdcnt < cmdbuf.length);
+ return true;
+ }
+
+ private int next() throws IOException {
+ if (!fill(8))
+ return CMD_EOF;
+
+ final int cmd = cmdbuf[cmdptr++] & 0xff;
+ if ((cmd & 0x80) != 0) {
+ // Determine the segment of the base which should
+ // be copied into the output. The segment is given
+ // as an offset and a length.
+ //
+ copyOffset = 0;
+ if ((cmd & 0x01) != 0)
+ copyOffset = cmdbuf[cmdptr++] & 0xff;
+ if ((cmd & 0x02) != 0)
+ copyOffset |= (cmdbuf[cmdptr++] & 0xff) << 8;
+ if ((cmd & 0x04) != 0)
+ copyOffset |= (cmdbuf[cmdptr++] & 0xff) << 16;
+ if ((cmd & 0x08) != 0)
+ copyOffset |= (cmdbuf[cmdptr++] & 0xff) << 24;
+
+ copySize = 0;
+ if ((cmd & 0x10) != 0)
+ copySize = cmdbuf[cmdptr++] & 0xff;
+ if ((cmd & 0x20) != 0)
+ copySize |= (cmdbuf[cmdptr++] & 0xff) << 8;
+ if ((cmd & 0x40) != 0)
+ copySize |= (cmdbuf[cmdptr++] & 0xff) << 16;
+ if (copySize == 0)
+ copySize = 0x10000;
+ return CMD_COPY;
+
+ } else if (cmd != 0) {
+ // Anything else the data is literal within the delta
+ // itself. Page the entire thing into the cmdbuf, if
+ // its not already there.
+ //
+ fill(cmd);
+ copySize = cmd;
+ return CMD_INSERT;
+
+ } else {
+ // cmd == 0 has been reserved for future encoding but
+ // for now its not acceptable.
+ //
+ throw new CorruptObjectException(JGitText.get().unsupportedCommand0);
+ }
+ }
+
+ private int have() {
+ return cmdcnt - cmdptr;
+ }
+
+ private void seekBase() throws IOException {
+ if (baseStream == null) {
+ baseStream = openBase();
+ if (getBaseSize() != baseSize)
+ throw new CorruptObjectException(
+ JGitText.get().baseLengthIncorrect);
+ IO.skipFully(baseStream, copyOffset);
+ baseOffset = copyOffset;
+
+ } else if (baseOffset < copyOffset) {
+ IO.skipFully(baseStream, copyOffset - baseOffset);
+ baseOffset = copyOffset;
+
+ } else if (baseOffset > copyOffset) {
+ baseStream.close();
+ baseStream = openBase();
+ IO.skipFully(baseStream, copyOffset);
+ baseOffset = copyOffset;
+ }
+ }
+}
diff --git a/org.eclipse.jgit/src/org/eclipse/jgit/storage/pack/DeltaWindow.java b/org.eclipse.jgit/src/org/eclipse/jgit/storage/pack/DeltaWindow.java
new file mode 100644
index 0000000000..6521e6d3ec
--- /dev/null
+++ b/org.eclipse.jgit/src/org/eclipse/jgit/storage/pack/DeltaWindow.java
@@ -0,0 +1,511 @@
+/*
+ * Copyright (C) 2010, Google Inc.
+ * and other copyright owners as documented in the project's IP log.
+ *
+ * This program and the accompanying materials are made available
+ * under the terms of the Eclipse Distribution License v1.0 which
+ * accompanies this distribution, is reproduced below, and is
+ * available at http://www.eclipse.org/org/documents/edl-v10.php
+ *
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or
+ * without modification, are permitted provided that the following
+ * conditions are met:
+ *
+ * - Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ *
+ * - Redistributions in binary form must reproduce the above
+ * copyright notice, this list of conditions and the following
+ * disclaimer in the documentation and/or other materials provided
+ * with the distribution.
+ *
+ * - Neither the name of the Eclipse Foundation, Inc. nor the
+ * names of its contributors may be used to endorse or promote
+ * products derived from this software without specific prior
+ * written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND
+ * CONTRIBUTORS "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES,
+ * INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
+ * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR
+ * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
+ * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT
+ * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
+ * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER
+ * CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT,
+ * STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
+ * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF
+ * ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ */
+
+package org.eclipse.jgit.storage.pack;
+
+import java.io.EOFException;
+import java.io.IOException;
+import java.io.OutputStream;
+import java.util.zip.Deflater;
+
+import org.eclipse.jgit.errors.IncorrectObjectTypeException;
+import org.eclipse.jgit.errors.LargeObjectException;
+import org.eclipse.jgit.errors.MissingObjectException;
+import org.eclipse.jgit.lib.ObjectReader;
+import org.eclipse.jgit.lib.ProgressMonitor;
+import org.eclipse.jgit.util.TemporaryBuffer;
+
+class DeltaWindow {
+ private static final int NEXT_RES = 0;
+
+ private static final int NEXT_SRC = 1;
+
+ private final PackWriter writer;
+
+ private final DeltaCache deltaCache;
+
+ private final ObjectReader reader;
+
+ private final DeltaWindowEntry[] window;
+
+ /** Maximum number of bytes to admit to the window at once. */
+ private final long maxMemory;
+
+ /** Maximum depth we should create for any delta chain. */
+ private final int maxDepth;
+
+ /** Amount of memory we have loaded right now. */
+ private long loaded;
+
+ // The object we are currently considering needs a lot of state:
+
+ /** Position of {@link #res} within {@link #window} array. */
+ private int resSlot;
+
+ /**
+ * Maximum delta chain depth the current object can have.
+ * <p>
+ * This can be smaller than {@link #maxDepth}.
+ */
+ private int resMaxDepth;
+
+ /** Window entry of the object we are currently considering. */
+ private DeltaWindowEntry res;
+
+ /** If we have a delta for {@link #res}, this is the shortest found yet. */
+ private TemporaryBuffer.Heap bestDelta;
+
+ /** If we have {@link #bestDelta}, the window position it was created by. */
+ private int bestSlot;
+
+ /** Used to compress cached deltas. */
+ private Deflater deflater;
+
+ DeltaWindow(PackWriter pw, DeltaCache dc, ObjectReader or) {
+ writer = pw;
+ deltaCache = dc;
+ reader = or;
+
+ // C Git increases the window size supplied by the user by 1.
+ // We don't know why it does this, but if the user asks for
+ // window=10, it actually processes with window=11. Because
+ // the window size has the largest direct impact on the final
+ // pack file size, we match this odd behavior here to give us
+ // a better chance of producing a similar sized pack as C Git.
+ //
+ // We would prefer to directly honor the user's request since
+ // PackWriter has a minimum of 2 for the window size, but then
+ // users might complain that JGit is creating a bigger pack file.
+ //
+ window = new DeltaWindowEntry[pw.getDeltaSearchWindowSize() + 1];
+ for (int i = 0; i < window.length; i++)
+ window[i] = new DeltaWindowEntry();
+
+ maxMemory = pw.getDeltaSearchMemoryLimit();
+ maxDepth = pw.getMaxDeltaDepth();
+ }
+
+ void search(ProgressMonitor monitor, ObjectToPack[] toSearch, int off,
+ int cnt) throws IOException {
+ try {
+ for (int end = off + cnt; off < end; off++) {
+ monitor.update(1);
+
+ res = window[resSlot];
+ if (0 < maxMemory) {
+ clear(res);
+ int tail = next(resSlot);
+ final long need = estimateSize(toSearch[off]);
+ while (maxMemory < loaded + need && tail != resSlot) {
+ clear(window[tail]);
+ tail = next(tail);
+ }
+ }
+ res.set(toSearch[off]);
+
+ if (res.object.isDoNotDelta()) {
+ // PackWriter marked edge objects with the
+ // do-not-delta flag. They are the only ones
+ // that appear in toSearch with it set, but
+ // we don't actually want to make a delta for
+ // them, just need to push them into the window
+ // so they can be read by other objects.
+ //
+ keepInWindow();
+ } else {
+ // Search for a delta for the current window slot.
+ //
+ search();
+ }
+ }
+ } finally {
+ if (deflater != null)
+ deflater.end();
+ }
+ }
+
+ private static long estimateSize(ObjectToPack ent) {
+ return DeltaIndex.estimateIndexSize(ent.getWeight());
+ }
+
+ private void clear(DeltaWindowEntry ent) {
+ if (ent.index != null)
+ loaded -= ent.index.getIndexSize();
+ else if (res.buffer != null)
+ loaded -= ent.buffer.length;
+ ent.set(null);
+ }
+
+ private void search() throws IOException {
+ // TODO(spearce) If the object is used as a base for other
+ // objects in this pack we should limit the depth we create
+ // for ourselves to be the remainder of our longest dependent
+ // chain and the configured maximum depth. This can happen
+ // when the dependents are being reused out a pack, but we
+ // cannot be because we are near the edge of a thin pack.
+ //
+ resMaxDepth = maxDepth;
+
+ // Loop through the window backwards, considering every entry.
+ // This lets us look at the bigger objects that came before.
+ //
+ for (int srcSlot = prior(resSlot); srcSlot != resSlot; srcSlot = prior(srcSlot)) {
+ DeltaWindowEntry src = window[srcSlot];
+ if (src.empty())
+ break;
+ if (delta(src, srcSlot) == NEXT_RES) {
+ bestDelta = null;
+ return;
+ }
+ }
+
+ // We couldn't find a suitable delta for this object, but it may
+ // still be able to act as a base for another one.
+ //
+ if (bestDelta == null) {
+ keepInWindow();
+ return;
+ }
+
+ // Select this best matching delta as the base for the object.
+ //
+ ObjectToPack srcObj = window[bestSlot].object;
+ ObjectToPack resObj = res.object;
+ if (srcObj.isDoNotDelta()) {
+ // The source (the delta base) is an edge object outside of the
+ // pack. Its part of the common base set that the peer already
+ // has on hand, so we don't want to send it. We have to store
+ // an ObjectId and *NOT* an ObjectToPack for the base to ensure
+ // the base isn't included in the outgoing pack file.
+ //
+ resObj.setDeltaBase(srcObj.copy());
+ } else {
+ // The base is part of the pack we are sending, so it should be
+ // a direct pointer to the base.
+ //
+ resObj.setDeltaBase(srcObj);
+ }
+ resObj.setDeltaDepth(srcObj.getDeltaDepth() + 1);
+ resObj.clearReuseAsIs();
+ cacheDelta(srcObj, resObj);
+
+ // Discard the cached best result, otherwise it leaks.
+ //
+ bestDelta = null;
+
+ // If this should be the end of a chain, don't keep
+ // it in the window. Just move on to the next object.
+ //
+ if (resObj.getDeltaDepth() == maxDepth)
+ return;
+
+ shuffleBaseUpInPriority();
+ keepInWindow();
+ }
+
+ private int delta(final DeltaWindowEntry src, final int srcSlot)
+ throws IOException {
+ // Objects must use only the same type as their delta base.
+ // If we are looking at something where that isn't true we
+ // have exhausted everything of the correct type and should
+ // move on to the next thing to examine.
+ //
+ if (src.type() != res.type()) {
+ keepInWindow();
+ return NEXT_RES;
+ }
+
+ // Only consider a source with a short enough delta chain.
+ if (src.depth() > resMaxDepth)
+ return NEXT_SRC;
+
+ // Estimate a reasonable upper limit on delta size.
+ int msz = deltaSizeLimit(res, resMaxDepth, src);
+ if (msz <= 8)
+ return NEXT_SRC;
+
+ // If we have to insert a lot to make this work, find another.
+ if (res.size() - src.size() > msz)
+ return NEXT_SRC;
+
+ // If the sizes are radically different, this is a bad pairing.
+ if (res.size() < src.size() / 16)
+ return NEXT_SRC;
+
+ DeltaIndex srcIndex;
+ try {
+ srcIndex = index(src);
+ } catch (LargeObjectException tooBig) {
+ // If the source is too big to work on, skip it.
+ dropFromWindow(srcSlot);
+ return NEXT_SRC;
+ } catch (IOException notAvailable) {
+ if (src.object.isDoNotDelta()) {
+ // This is an edge that is suddenly not available.
+ dropFromWindow(srcSlot);
+ return NEXT_SRC;
+ } else {
+ throw notAvailable;
+ }
+ }
+
+ byte[] resBuf;
+ try {
+ resBuf = buffer(res);
+ } catch (LargeObjectException tooBig) {
+ // If its too big, move on to another item.
+ return NEXT_RES;
+ }
+
+ // If we already have a delta for the current object, abort
+ // encoding early if this new pairing produces a larger delta.
+ if (bestDelta != null && bestDelta.length() < msz)
+ msz = (int) bestDelta.length();
+
+ TemporaryBuffer.Heap delta = new TemporaryBuffer.Heap(msz);
+ try {
+ if (!srcIndex.encode(delta, resBuf, msz))
+ return NEXT_SRC;
+ } catch (IOException deltaTooBig) {
+ // This only happens when the heap overflows our limit.
+ return NEXT_SRC;
+ }
+
+ if (isBetterDelta(src, delta)) {
+ bestDelta = delta;
+ bestSlot = srcSlot;
+ }
+
+ return NEXT_SRC;
+ }
+
+ private void cacheDelta(ObjectToPack srcObj, ObjectToPack resObj) {
+ if (Integer.MAX_VALUE < bestDelta.length())
+ return;
+
+ int rawsz = (int) bestDelta.length();
+ if (deltaCache.canCache(rawsz, srcObj, resObj)) {
+ try {
+ byte[] zbuf = new byte[deflateBound(rawsz)];
+
+ ZipStream zs = new ZipStream(deflater(), zbuf);
+ bestDelta.writeTo(zs, null);
+ bestDelta = null;
+ int len = zs.finish();
+
+ resObj.setCachedDelta(deltaCache.cache(zbuf, len, rawsz));
+ resObj.setCachedSize(rawsz);
+ } catch (IOException err) {
+ deltaCache.credit(rawsz);
+ } catch (OutOfMemoryError err) {
+ deltaCache.credit(rawsz);
+ }
+ }
+ }
+
+ private static int deflateBound(int insz) {
+ return insz + ((insz + 7) >> 3) + ((insz + 63) >> 6) + 11;
+ }
+
+ private void shuffleBaseUpInPriority() {
+ // Shuffle the entire window so that the best match we just used
+ // is at our current index, and our current object is at the index
+ // before it. Slide any entries in between to make space.
+ //
+ window[resSlot] = window[bestSlot];
+
+ DeltaWindowEntry next = res;
+ int slot = prior(resSlot);
+ for (; slot != bestSlot; slot = prior(slot)) {
+ DeltaWindowEntry e = window[slot];
+ window[slot] = next;
+ next = e;
+ }
+ window[slot] = next;
+ }
+
+ private void keepInWindow() {
+ resSlot = next(resSlot);
+ }
+
+ private int next(int slot) {
+ if (++slot == window.length)
+ return 0;
+ return slot;
+ }
+
+ private int prior(int slot) {
+ if (slot == 0)
+ return window.length - 1;
+ return slot - 1;
+ }
+
+ private void dropFromWindow(@SuppressWarnings("unused") int srcSlot) {
+ // We should drop the current source entry from the window,
+ // it is somehow invalid for us to work with.
+ }
+
+ private boolean isBetterDelta(DeltaWindowEntry src,
+ TemporaryBuffer.Heap resDelta) {
+ if (bestDelta == null)
+ return true;
+
+ // If both delta sequences are the same length, use the one
+ // that has a shorter delta chain since it would be faster
+ // to access during reads.
+ //
+ if (resDelta.length() == bestDelta.length())
+ return src.depth() < window[bestSlot].depth();
+
+ return resDelta.length() < bestDelta.length();
+ }
+
+ private static int deltaSizeLimit(DeltaWindowEntry res, int maxDepth,
+ DeltaWindowEntry src) {
+ // Ideally the delta is at least 50% of the original size,
+ // but we also want to account for delta header overhead in
+ // the pack file (to point to the delta base) so subtract off
+ // some of those header bytes from the limit.
+ //
+ final int limit = res.size() / 2 - 20;
+
+ // Distribute the delta limit over the entire chain length.
+ // This is weighted such that deeper items in the chain must
+ // be even smaller than if they were earlier in the chain, as
+ // they cost significantly more to unpack due to the increased
+ // number of recursive unpack calls.
+ //
+ final int remainingDepth = maxDepth - src.depth();
+ return (limit * remainingDepth) / maxDepth;
+ }
+
+ private DeltaIndex index(DeltaWindowEntry ent)
+ throws MissingObjectException, IncorrectObjectTypeException,
+ IOException, LargeObjectException {
+ DeltaIndex idx = ent.index;
+ if (idx == null) {
+ try {
+ idx = new DeltaIndex(buffer(ent));
+ } catch (OutOfMemoryError noMemory) {
+ LargeObjectException e = new LargeObjectException(ent.object);
+ e.initCause(noMemory);
+ throw e;
+ }
+ if (0 < maxMemory)
+ loaded += idx.getIndexSize() - idx.getSourceSize();
+ ent.index = idx;
+ }
+ return idx;
+ }
+
+ private byte[] buffer(DeltaWindowEntry ent) throws MissingObjectException,
+ IncorrectObjectTypeException, IOException, LargeObjectException {
+ byte[] buf = ent.buffer;
+ if (buf == null) {
+ buf = writer.buffer(reader, ent.object);
+ if (0 < maxMemory)
+ loaded += buf.length;
+ ent.buffer = buf;
+ }
+ return buf;
+ }
+
+ private Deflater deflater() {
+ if (deflater == null)
+ deflater = new Deflater(writer.getCompressionLevel());
+ else
+ deflater.reset();
+ return deflater;
+ }
+
+ static final class ZipStream extends OutputStream {
+ private final Deflater deflater;
+
+ private final byte[] zbuf;
+
+ private int outPtr;
+
+ ZipStream(Deflater deflater, byte[] zbuf) {
+ this.deflater = deflater;
+ this.zbuf = zbuf;
+ }
+
+ int finish() throws IOException {
+ deflater.finish();
+ for (;;) {
+ if (outPtr == zbuf.length)
+ throw new EOFException();
+
+ int n = deflater.deflate(zbuf, outPtr, zbuf.length - outPtr);
+ if (n == 0) {
+ if (deflater.finished())
+ return outPtr;
+ throw new IOException();
+ }
+ outPtr += n;
+ }
+ }
+
+ @Override
+ public void write(byte[] b, int off, int len) throws IOException {
+ deflater.setInput(b, off, len);
+ for (;;) {
+ if (outPtr == zbuf.length)
+ throw new EOFException();
+
+ int n = deflater.deflate(zbuf, outPtr, zbuf.length - outPtr);
+ if (n == 0) {
+ if (deflater.needsInput())
+ break;
+ throw new IOException();
+ }
+ outPtr += n;
+ }
+ }
+
+ @Override
+ public void write(int b) throws IOException {
+ throw new UnsupportedOperationException();
+ }
+ }
+}
diff --git a/org.eclipse.jgit/src/org/eclipse/jgit/storage/pack/DeltaWindowEntry.java b/org.eclipse.jgit/src/org/eclipse/jgit/storage/pack/DeltaWindowEntry.java
new file mode 100644
index 0000000000..0f1e6329f9
--- /dev/null
+++ b/org.eclipse.jgit/src/org/eclipse/jgit/storage/pack/DeltaWindowEntry.java
@@ -0,0 +1,80 @@
+/*
+ * Copyright (C) 2010, Google Inc.
+ * and other copyright owners as documented in the project's IP log.
+ *
+ * This program and the accompanying materials are made available
+ * under the terms of the Eclipse Distribution License v1.0 which
+ * accompanies this distribution, is reproduced below, and is
+ * available at http://www.eclipse.org/org/documents/edl-v10.php
+ *
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or
+ * without modification, are permitted provided that the following
+ * conditions are met:
+ *
+ * - Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ *
+ * - Redistributions in binary form must reproduce the above
+ * copyright notice, this list of conditions and the following
+ * disclaimer in the documentation and/or other materials provided
+ * with the distribution.
+ *
+ * - Neither the name of the Eclipse Foundation, Inc. nor the
+ * names of its contributors may be used to endorse or promote
+ * products derived from this software without specific prior
+ * written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND
+ * CONTRIBUTORS "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES,
+ * INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
+ * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR
+ * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
+ * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT
+ * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
+ * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER
+ * CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT,
+ * STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
+ * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF
+ * ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ */
+
+package org.eclipse.jgit.storage.pack;
+
+class DeltaWindowEntry {
+ ObjectToPack object;
+
+ /** Complete contents of this object. Lazily loaded. */
+ byte[] buffer;
+
+ /** Index of this object's content, to encode other deltas. Lazily loaded. */
+ DeltaIndex index;
+
+ void set(ObjectToPack object) {
+ this.object = object;
+ this.index = null;
+ this.buffer = null;
+ }
+
+ /** @return current delta chain depth of this object. */
+ int depth() {
+ return object.getDeltaDepth();
+ }
+
+ /** @return type of the object in this window entry. */
+ int type() {
+ return object.getType();
+ }
+
+ /** @return estimated unpacked size of the object, in bytes . */
+ int size() {
+ return object.getWeight();
+ }
+
+ /** @return true if there is no object stored in this entry. */
+ boolean empty() {
+ return object == null;
+ }
+}
diff --git a/org.eclipse.jgit/src/org/eclipse/jgit/storage/pack/ObjectReuseAsIs.java b/org.eclipse.jgit/src/org/eclipse/jgit/storage/pack/ObjectReuseAsIs.java
new file mode 100644
index 0000000000..a815e93542
--- /dev/null
+++ b/org.eclipse.jgit/src/org/eclipse/jgit/storage/pack/ObjectReuseAsIs.java
@@ -0,0 +1,136 @@
+/*
+ * Copyright (C) 2010, Google Inc.
+ * and other copyright owners as documented in the project's IP log.
+ *
+ * This program and the accompanying materials are made available
+ * under the terms of the Eclipse Distribution License v1.0 which
+ * accompanies this distribution, is reproduced below, and is
+ * available at http://www.eclipse.org/org/documents/edl-v10.php
+ *
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or
+ * without modification, are permitted provided that the following
+ * conditions are met:
+ *
+ * - Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ *
+ * - Redistributions in binary form must reproduce the above
+ * copyright notice, this list of conditions and the following
+ * disclaimer in the documentation and/or other materials provided
+ * with the distribution.
+ *
+ * - Neither the name of the Eclipse Foundation, Inc. nor the
+ * names of its contributors may be used to endorse or promote
+ * products derived from this software without specific prior
+ * written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND
+ * CONTRIBUTORS "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES,
+ * INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
+ * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR
+ * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
+ * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT
+ * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
+ * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER
+ * CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT,
+ * STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
+ * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF
+ * ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ */
+
+package org.eclipse.jgit.storage.pack;
+
+import java.io.IOException;
+
+import org.eclipse.jgit.errors.MissingObjectException;
+import org.eclipse.jgit.errors.StoredObjectRepresentationNotAvailableException;
+import org.eclipse.jgit.lib.ObjectReader;
+import org.eclipse.jgit.revwalk.RevObject;
+
+/**
+ * Extension of {@link ObjectReader} that supports reusing objects in packs.
+ * <p>
+ * {@code ObjectReader} implementations may also optionally implement this
+ * interface to support {@link PackWriter} with a means of copying an object
+ * that is already in pack encoding format directly into the output stream,
+ * without incurring decompression and recompression overheads.
+ */
+public interface ObjectReuseAsIs {
+ /**
+ * Allocate a new {@code PackWriter} state structure for an object.
+ * <p>
+ * {@link PackWriter} allocates these objects to keep track of the
+ * per-object state, and how to load the objects efficiently into the
+ * generated stream. Implementers may subclass this type with additional
+ * object state, such as to remember what file and offset contains the
+ * object's pack encoded data.
+ *
+ * @param obj
+ * identity of the object that will be packed. The object's
+ * parsed status is undefined here. Implementers must not rely on
+ * the object being parsed.
+ * @return a new instance for this object.
+ */
+ public ObjectToPack newObjectToPack(RevObject obj);
+
+ /**
+ * Select the best object representation for a packer.
+ * <p>
+ * Implementations should iterate through all available representations of
+ * an object, and pass them in turn to the PackWriter though
+ * {@link PackWriter#select(ObjectToPack, StoredObjectRepresentation)} so
+ * the writer can select the most suitable representation to reuse into the
+ * output stream.
+ *
+ * @param packer
+ * the packer that will write the object in the near future.
+ * @param otp
+ * the object to pack.
+ * @throws MissingObjectException
+ * there is no representation available for the object, as it is
+ * no longer in the repository. Packing will abort.
+ * @throws IOException
+ * the repository cannot be accessed. Packing will abort.
+ */
+ public void selectObjectRepresentation(PackWriter packer, ObjectToPack otp)
+ throws IOException, MissingObjectException;
+
+ /**
+ * Output a previously selected representation.
+ * <p>
+ * {@code PackWriter} invokes this method only if a representation
+ * previously given to it by {@code selectObjectRepresentation} was chosen
+ * for reuse into the output stream. The {@code otp} argument is an instance
+ * created by this reader's own {@code newObjectToPack}, and the
+ * representation data saved within it also originated from this reader.
+ * <p>
+ * Implementors must write the object header before copying the raw data to
+ * the output stream. The typical implementation is like:
+ *
+ * <pre>
+ * MyToPack mtp = (MyToPack) otp;
+ * byte[] raw = validate(mtp); // throw SORNAE here, if at all
+ * out.writeHeader(mtp, mtp.inflatedSize);
+ * out.write(raw);
+ * </pre>
+ *
+ * @param out
+ * stream the object should be written to.
+ * @param otp
+ * the object's saved representation information.
+ * @throws StoredObjectRepresentationNotAvailableException
+ * the previously selected representation is no longer
+ * available. If thrown before {@code out.writeHeader} the pack
+ * writer will try to find another representation, and write
+ * that one instead. If throw after {@code out.writeHeader},
+ * packing will abort.
+ * @throws IOException
+ * the stream's write method threw an exception. Packing will
+ * abort.
+ */
+ public void copyObjectAsIs(PackOutputStream out, ObjectToPack otp)
+ throws IOException, StoredObjectRepresentationNotAvailableException;
+}
diff --git a/org.eclipse.jgit/src/org/eclipse/jgit/storage/pack/ObjectToPack.java b/org.eclipse.jgit/src/org/eclipse/jgit/storage/pack/ObjectToPack.java
new file mode 100644
index 0000000000..70188a3803
--- /dev/null
+++ b/org.eclipse.jgit/src/org/eclipse/jgit/storage/pack/ObjectToPack.java
@@ -0,0 +1,320 @@
+/*
+ * Copyright (C) 2008-2010, Google Inc.
+ * Copyright (C) 2008, Marek Zawirski <marek.zawirski@gmail.com>
+ * and other copyright owners as documented in the project's IP log.
+ *
+ * This program and the accompanying materials are made available
+ * under the terms of the Eclipse Distribution License v1.0 which
+ * accompanies this distribution, is reproduced below, and is
+ * available at http://www.eclipse.org/org/documents/edl-v10.php
+ *
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or
+ * without modification, are permitted provided that the following
+ * conditions are met:
+ *
+ * - Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ *
+ * - Redistributions in binary form must reproduce the above
+ * copyright notice, this list of conditions and the following
+ * disclaimer in the documentation and/or other materials provided
+ * with the distribution.
+ *
+ * - Neither the name of the Eclipse Foundation, Inc. nor the
+ * names of its contributors may be used to endorse or promote
+ * products derived from this software without specific prior
+ * written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND
+ * CONTRIBUTORS "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES,
+ * INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
+ * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR
+ * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
+ * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT
+ * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
+ * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER
+ * CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT,
+ * STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
+ * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF
+ * ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ */
+
+package org.eclipse.jgit.storage.pack;
+
+import org.eclipse.jgit.lib.AnyObjectId;
+import org.eclipse.jgit.lib.Constants;
+import org.eclipse.jgit.lib.ObjectId;
+import org.eclipse.jgit.revwalk.RevObject;
+import org.eclipse.jgit.transport.PackedObjectInfo;
+
+/**
+ * Per-object state used by {@link PackWriter}.
+ * <p>
+ * {@code PackWriter} uses this class to track the things it needs to include in
+ * the newly generated pack file, and how to efficiently obtain the raw data for
+ * each object as they are written to the output stream.
+ */
+public class ObjectToPack extends PackedObjectInfo {
+ private static final int WANT_WRITE = 1 << 0;
+
+ private static final int REUSE_AS_IS = 1 << 1;
+
+ private static final int DO_NOT_DELTA = 1 << 2;
+
+ private static final int TYPE_SHIFT = 5;
+
+ private static final int DELTA_SHIFT = 8;
+
+ private static final int NON_DELTA_MASK = 0xff;
+
+ /** Other object being packed that this will delta against. */
+ private ObjectId deltaBase;
+
+ /**
+ * Bit field, from bit 0 to bit 31:
+ * <ul>
+ * <li>1 bit: wantWrite</li>
+ * <li>1 bit: canReuseAsIs</li>
+ * <li>1 bit: doNotDelta</li>
+ * <li>2 bits: unused</li>
+ * <li>3 bits: type</li>
+ * <li>--</li>
+ * <li>24 bits: deltaDepth</li>
+ * </ul>
+ */
+ private int flags;
+
+ /** Hash of the object's tree path. */
+ private int pathHash;
+
+ /** If present, deflated delta instruction stream for this object. */
+ private DeltaCache.Ref cachedDelta;
+
+ /**
+ * Construct for the specified object id.
+ *
+ * @param src
+ * object id of object for packing
+ * @param type
+ * real type code of the object, not its in-pack type.
+ */
+ public ObjectToPack(AnyObjectId src, final int type) {
+ super(src);
+ flags = type << TYPE_SHIFT;
+ }
+
+ /**
+ * Construct for the specified object.
+ *
+ * @param obj
+ * identity of the object that will be packed. The object's
+ * parsed status is undefined here. Implementers must not rely on
+ * the object being parsed.
+ */
+ public ObjectToPack(RevObject obj) {
+ this(obj, obj.getType());
+ }
+
+ /**
+ * @return delta base object id if object is going to be packed in delta
+ * representation; null otherwise - if going to be packed as a
+ * whole object.
+ */
+ ObjectId getDeltaBaseId() {
+ return deltaBase;
+ }
+
+ /**
+ * @return delta base object to pack if object is going to be packed in
+ * delta representation and delta is specified as object to
+ * pack; null otherwise - if going to be packed as a whole
+ * object or delta base is specified only as id.
+ */
+ ObjectToPack getDeltaBase() {
+ if (deltaBase instanceof ObjectToPack)
+ return (ObjectToPack) deltaBase;
+ return null;
+ }
+
+ /**
+ * Set delta base for the object. Delta base set by this method is used
+ * by {@link PackWriter} to write object - determines its representation
+ * in a created pack.
+ *
+ * @param deltaBase
+ * delta base object or null if object should be packed as a
+ * whole object.
+ *
+ */
+ void setDeltaBase(ObjectId deltaBase) {
+ this.deltaBase = deltaBase;
+ }
+
+ void setCachedDelta(DeltaCache.Ref data){
+ cachedDelta = data;
+ }
+
+ DeltaCache.Ref popCachedDelta() {
+ DeltaCache.Ref r = cachedDelta;
+ if (r != null)
+ cachedDelta = null;
+ return r;
+ }
+
+ void clearDeltaBase() {
+ this.deltaBase = null;
+
+ if (cachedDelta != null) {
+ cachedDelta.clear();
+ cachedDelta.enqueue();
+ cachedDelta = null;
+ }
+ }
+
+ /**
+ * @return true if object is going to be written as delta; false
+ * otherwise.
+ */
+ boolean isDeltaRepresentation() {
+ return deltaBase != null;
+ }
+
+ /**
+ * Check if object is already written in a pack. This information is
+ * used to achieve delta-base precedence in a pack file.
+ *
+ * @return true if object is already written; false otherwise.
+ */
+ boolean isWritten() {
+ return getOffset() != 0;
+ }
+
+ int getType() {
+ return (flags >> TYPE_SHIFT) & 0x7;
+ }
+
+ int getDeltaDepth() {
+ return flags >>> DELTA_SHIFT;
+ }
+
+ void setDeltaDepth(int d) {
+ flags = (d << DELTA_SHIFT) | (flags & NON_DELTA_MASK);
+ }
+
+ boolean wantWrite() {
+ return (flags & WANT_WRITE) != 0;
+ }
+
+ void markWantWrite() {
+ flags |= WANT_WRITE;
+ }
+
+ boolean isReuseAsIs() {
+ return (flags & REUSE_AS_IS) != 0;
+ }
+
+ void setReuseAsIs() {
+ flags |= REUSE_AS_IS;
+ }
+
+ /**
+ * Forget the reuse information previously stored.
+ * <p>
+ * Implementations may subclass this method, but they must also invoke the
+ * super version with {@code super.clearReuseAsIs()} to ensure the flag is
+ * properly cleared for the writer.
+ */
+ protected void clearReuseAsIs() {
+ flags &= ~REUSE_AS_IS;
+ }
+
+ boolean isDoNotDelta() {
+ return (flags & DO_NOT_DELTA) != 0;
+ }
+
+ void setDoNotDelta(boolean noDelta) {
+ if (noDelta)
+ flags |= DO_NOT_DELTA;
+ else
+ flags &= ~DO_NOT_DELTA;
+ }
+
+ int getFormat() {
+ if (isReuseAsIs()) {
+ if (isDeltaRepresentation())
+ return StoredObjectRepresentation.PACK_DELTA;
+ return StoredObjectRepresentation.PACK_WHOLE;
+ }
+ return StoredObjectRepresentation.FORMAT_OTHER;
+ }
+
+ // Overload weight into CRC since we don't need them at the same time.
+ int getWeight() {
+ return getCRC();
+ }
+
+ void setWeight(int weight) {
+ setCRC(weight);
+ }
+
+ int getPathHash() {
+ return pathHash;
+ }
+
+ void setPathHash(int hc) {
+ pathHash = hc;
+ }
+
+ int getCachedSize() {
+ return pathHash;
+ }
+
+ void setCachedSize(int sz) {
+ pathHash = sz;
+ }
+
+ /**
+ * Remember a specific representation for reuse at a later time.
+ * <p>
+ * Implementers should remember the representation chosen, so it can be
+ * reused at a later time. {@link PackWriter} may invoke this method
+ * multiple times for the same object, each time saving the current best
+ * representation found.
+ *
+ * @param ref
+ * the object representation.
+ */
+ public void select(StoredObjectRepresentation ref) {
+ // Empty by default.
+ }
+
+ @Override
+ public String toString() {
+ StringBuilder buf = new StringBuilder();
+ buf.append("ObjectToPack[");
+ buf.append(Constants.typeString(getType()));
+ buf.append(" ");
+ buf.append(name());
+ if (wantWrite())
+ buf.append(" wantWrite");
+ if (isReuseAsIs())
+ buf.append(" reuseAsIs");
+ if (isDoNotDelta())
+ buf.append(" doNotDelta");
+ if (getDeltaDepth() > 0)
+ buf.append(" depth=" + getDeltaDepth());
+ if (isDeltaRepresentation()) {
+ if (getDeltaBase() != null)
+ buf.append(" base=inpack:" + getDeltaBase().name());
+ else
+ buf.append(" base=edge:" + getDeltaBaseId().name());
+ }
+ if (isWritten())
+ buf.append(" offset=" + getOffset());
+ buf.append("]");
+ return buf.toString();
+ }
+}
diff --git a/org.eclipse.jgit/src/org/eclipse/jgit/storage/pack/PackConfig.java b/org.eclipse.jgit/src/org/eclipse/jgit/storage/pack/PackConfig.java
new file mode 100644
index 0000000000..814ab8f291
--- /dev/null
+++ b/org.eclipse.jgit/src/org/eclipse/jgit/storage/pack/PackConfig.java
@@ -0,0 +1,93 @@
+/*
+ * Copyright (C) 2010, Google Inc.
+ * and other copyright owners as documented in the project's IP log.
+ *
+ * This program and the accompanying materials are made available
+ * under the terms of the Eclipse Distribution License v1.0 which
+ * accompanies this distribution, is reproduced below, and is
+ * available at http://www.eclipse.org/org/documents/edl-v10.php
+ *
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or
+ * without modification, are permitted provided that the following
+ * conditions are met:
+ *
+ * - Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ *
+ * - Redistributions in binary form must reproduce the above
+ * copyright notice, this list of conditions and the following
+ * disclaimer in the documentation and/or other materials provided
+ * with the distribution.
+ *
+ * - Neither the name of the Eclipse Foundation, Inc. nor the
+ * names of its contributors may be used to endorse or promote
+ * products derived from this software without specific prior
+ * written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND
+ * CONTRIBUTORS "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES,
+ * INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
+ * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR
+ * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
+ * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT
+ * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
+ * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER
+ * CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT,
+ * STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
+ * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF
+ * ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ */
+
+package org.eclipse.jgit.storage.pack;
+
+import static java.util.zip.Deflater.DEFAULT_COMPRESSION;
+import org.eclipse.jgit.lib.Config;
+import org.eclipse.jgit.lib.Config.SectionParser;
+
+class PackConfig {
+ /** Key for {@link Config#get(SectionParser)}. */
+ static final Config.SectionParser<PackConfig> KEY = new SectionParser<PackConfig>() {
+ public PackConfig parse(final Config cfg) {
+ return new PackConfig(cfg);
+ }
+ };
+
+ final int deltaWindow;
+
+ final long deltaWindowMemory;
+
+ final int deltaDepth;
+
+ final long deltaCacheSize;
+
+ final int deltaCacheLimit;
+
+ final int compression;
+
+ final int indexVersion;
+
+ final long bigFileThreshold;
+
+ final int threads;
+
+ private PackConfig(Config rc) {
+ deltaWindow = rc.getInt("pack", "window", PackWriter.DEFAULT_DELTA_SEARCH_WINDOW_SIZE);
+ deltaWindowMemory = rc.getLong("pack", null, "windowmemory", 0);
+ deltaCacheSize = rc.getLong("pack", null, "deltacachesize", PackWriter.DEFAULT_DELTA_CACHE_SIZE);
+ deltaCacheLimit = rc.getInt("pack", "deltacachelimit", PackWriter.DEFAULT_DELTA_CACHE_LIMIT);
+ deltaDepth = rc.getInt("pack", "depth", PackWriter.DEFAULT_MAX_DELTA_DEPTH);
+ compression = compression(rc);
+ indexVersion = rc.getInt("pack", "indexversion", 2);
+ bigFileThreshold = rc.getLong("core", null, "bigfilethreshold", PackWriter.DEFAULT_BIG_FILE_THRESHOLD);
+ threads = rc.getInt("pack", "threads", 0);
+ }
+
+ private static int compression(Config rc) {
+ if (rc.getString("pack", null, "compression") != null)
+ return rc.getInt("pack", "compression", DEFAULT_COMPRESSION);
+ return rc.getInt("core", "compression", DEFAULT_COMPRESSION);
+ }
+}
diff --git a/org.eclipse.jgit/src/org/eclipse/jgit/storage/pack/PackOutputStream.java b/org.eclipse.jgit/src/org/eclipse/jgit/storage/pack/PackOutputStream.java
new file mode 100644
index 0000000000..92e1a197cd
--- /dev/null
+++ b/org.eclipse.jgit/src/org/eclipse/jgit/storage/pack/PackOutputStream.java
@@ -0,0 +1,213 @@
+/*
+ * Copyright (C) 2008-2009, Google Inc.
+ * Copyright (C) 2008, Marek Zawirski <marek.zawirski@gmail.com>
+ * and other copyright owners as documented in the project's IP log.
+ *
+ * This program and the accompanying materials are made available
+ * under the terms of the Eclipse Distribution License v1.0 which
+ * accompanies this distribution, is reproduced below, and is
+ * available at http://www.eclipse.org/org/documents/edl-v10.php
+ *
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or
+ * without modification, are permitted provided that the following
+ * conditions are met:
+ *
+ * - Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ *
+ * - Redistributions in binary form must reproduce the above
+ * copyright notice, this list of conditions and the following
+ * disclaimer in the documentation and/or other materials provided
+ * with the distribution.
+ *
+ * - Neither the name of the Eclipse Foundation, Inc. nor the
+ * names of its contributors may be used to endorse or promote
+ * products derived from this software without specific prior
+ * written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND
+ * CONTRIBUTORS "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES,
+ * INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
+ * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR
+ * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
+ * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT
+ * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
+ * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER
+ * CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT,
+ * STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
+ * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF
+ * ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ */
+
+package org.eclipse.jgit.storage.pack;
+
+import java.io.IOException;
+import java.io.OutputStream;
+import java.security.MessageDigest;
+import java.util.zip.CRC32;
+
+import org.eclipse.jgit.lib.Constants;
+import org.eclipse.jgit.lib.ProgressMonitor;
+import org.eclipse.jgit.util.NB;
+
+/** Custom output stream to support {@link PackWriter}. */
+public final class PackOutputStream extends OutputStream {
+ private final ProgressMonitor writeMonitor;
+
+ private final OutputStream out;
+
+ private final boolean ofsDelta;
+
+ private final CRC32 crc = new CRC32();
+
+ private final MessageDigest md = Constants.newMessageDigest();
+
+ private long count;
+
+ private byte[] headerBuffer = new byte[32];
+
+ private byte[] copyBuffer;
+
+ /**
+ * Initialize a pack output stream.
+ * <p>
+ * This constructor is exposed to support debugging the JGit library only.
+ * Application or storage level code should not create a PackOutputStream,
+ * instead use {@link PackWriter}, and let the writer create the stream.
+ *
+ * @param writeMonitor
+ * monitor to update on object output progress.
+ * @param out
+ * target stream to receive all object contents.
+ * @param pw
+ * packer that is going to perform the output.
+ */
+ public PackOutputStream(final ProgressMonitor writeMonitor,
+ final OutputStream out, final PackWriter pw) {
+ this.writeMonitor = writeMonitor;
+ this.out = out;
+ this.ofsDelta = pw.isDeltaBaseAsOffset();
+ }
+
+ @Override
+ public void write(final int b) throws IOException {
+ count++;
+ out.write(b);
+ crc.update(b);
+ md.update((byte) b);
+ }
+
+ @Override
+ public void write(final byte[] b, final int off, final int len)
+ throws IOException {
+ count += len;
+ out.write(b, off, len);
+ crc.update(b, off, len);
+ md.update(b, off, len);
+ }
+
+ @Override
+ public void flush() throws IOException {
+ out.flush();
+ }
+
+ void writeFileHeader(int version, int objectCount) throws IOException {
+ System.arraycopy(Constants.PACK_SIGNATURE, 0, headerBuffer, 0, 4);
+ NB.encodeInt32(headerBuffer, 4, version);
+ NB.encodeInt32(headerBuffer, 8, objectCount);
+ write(headerBuffer, 0, 12);
+ }
+
+ /**
+ * Commits the object header onto the stream.
+ * <p>
+ * Once the header has been written, the object representation must be fully
+ * output, or packing must abort abnormally.
+ *
+ * @param otp
+ * the object to pack. Header information is obtained.
+ * @param rawLength
+ * number of bytes of the inflated content. For an object that is
+ * in whole object format, this is the same as the object size.
+ * For an object that is in a delta format, this is the size of
+ * the inflated delta instruction stream.
+ * @throws IOException
+ * the underlying stream refused to accept the header.
+ */
+ public void writeHeader(ObjectToPack otp, long rawLength)
+ throws IOException {
+ if (otp.isDeltaRepresentation()) {
+ if (ofsDelta) {
+ ObjectToPack baseInPack = otp.getDeltaBase();
+ if (baseInPack != null && baseInPack.isWritten()) {
+ final long start = count;
+ int n = encodeTypeSize(Constants.OBJ_OFS_DELTA, rawLength);
+ write(headerBuffer, 0, n);
+
+ long offsetDiff = start - baseInPack.getOffset();
+ n = headerBuffer.length - 1;
+ headerBuffer[n] = (byte) (offsetDiff & 0x7F);
+ while ((offsetDiff >>= 7) > 0)
+ headerBuffer[--n] = (byte) (0x80 | (--offsetDiff & 0x7F));
+ write(headerBuffer, n, headerBuffer.length - n);
+ return;
+ }
+ }
+
+ int n = encodeTypeSize(Constants.OBJ_REF_DELTA, rawLength);
+ otp.getDeltaBaseId().copyRawTo(headerBuffer, n);
+ write(headerBuffer, 0, n + Constants.OBJECT_ID_LENGTH);
+ } else {
+ int n = encodeTypeSize(otp.getType(), rawLength);
+ write(headerBuffer, 0, n);
+ }
+ }
+
+ private int encodeTypeSize(int type, long rawLength) {
+ long nextLength = rawLength >>> 4;
+ headerBuffer[0] = (byte) ((nextLength > 0 ? 0x80 : 0x00)
+ | (type << 4) | (rawLength & 0x0F));
+ rawLength = nextLength;
+ int n = 1;
+ while (rawLength > 0) {
+ nextLength >>>= 7;
+ headerBuffer[n++] = (byte) ((nextLength > 0 ? 0x80 : 0x00) | (rawLength & 0x7F));
+ rawLength = nextLength;
+ }
+ return n;
+ }
+
+ /** @return a temporary buffer writers can use to copy data with. */
+ public byte[] getCopyBuffer() {
+ if (copyBuffer == null)
+ copyBuffer = new byte[16 * 1024];
+ return copyBuffer;
+ }
+
+ void endObject() {
+ writeMonitor.update(1);
+ }
+
+ /** @return total number of bytes written since stream start. */
+ long length() {
+ return count;
+ }
+
+ /** @return obtain the current CRC32 register. */
+ int getCRC32() {
+ return (int) crc.getValue();
+ }
+
+ /** Reinitialize the CRC32 register for a new region. */
+ void resetCRC32() {
+ crc.reset();
+ }
+
+ /** @return obtain the current SHA-1 digest. */
+ byte[] getDigest() {
+ return md.digest();
+ }
+}
diff --git a/org.eclipse.jgit/src/org/eclipse/jgit/storage/pack/PackWriter.java b/org.eclipse.jgit/src/org/eclipse/jgit/storage/pack/PackWriter.java
new file mode 100644
index 0000000000..38f00b5739
--- /dev/null
+++ b/org.eclipse.jgit/src/org/eclipse/jgit/storage/pack/PackWriter.java
@@ -0,0 +1,1442 @@
+/*
+ * Copyright (C) 2008-2010, Google Inc.
+ * Copyright (C) 2008, Marek Zawirski <marek.zawirski@gmail.com>
+ * and other copyright owners as documented in the project's IP log.
+ *
+ * This program and the accompanying materials are made available
+ * under the terms of the Eclipse Distribution License v1.0 which
+ * accompanies this distribution, is reproduced below, and is
+ * available at http://www.eclipse.org/org/documents/edl-v10.php
+ *
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or
+ * without modification, are permitted provided that the following
+ * conditions are met:
+ *
+ * - Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ *
+ * - Redistributions in binary form must reproduce the above
+ * copyright notice, this list of conditions and the following
+ * disclaimer in the documentation and/or other materials provided
+ * with the distribution.
+ *
+ * - Neither the name of the Eclipse Foundation, Inc. nor the
+ * names of its contributors may be used to endorse or promote
+ * products derived from this software without specific prior
+ * written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND
+ * CONTRIBUTORS "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES,
+ * INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
+ * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR
+ * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
+ * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT
+ * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
+ * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER
+ * CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT,
+ * STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
+ * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF
+ * ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ */
+
+package org.eclipse.jgit.storage.pack;
+
+import static org.eclipse.jgit.storage.pack.StoredObjectRepresentation.PACK_DELTA;
+import static org.eclipse.jgit.storage.pack.StoredObjectRepresentation.PACK_WHOLE;
+
+import java.io.IOException;
+import java.io.InputStream;
+import java.io.OutputStream;
+import java.security.MessageDigest;
+import java.util.ArrayList;
+import java.util.Arrays;
+import java.util.Collection;
+import java.util.Collections;
+import java.util.Comparator;
+import java.util.Iterator;
+import java.util.List;
+import java.util.concurrent.ExecutorService;
+import java.util.concurrent.Executors;
+import java.util.concurrent.TimeUnit;
+import java.util.zip.Deflater;
+import java.util.zip.DeflaterOutputStream;
+
+import org.eclipse.jgit.JGitText;
+import org.eclipse.jgit.errors.CorruptObjectException;
+import org.eclipse.jgit.errors.IncorrectObjectTypeException;
+import org.eclipse.jgit.errors.LargeObjectException;
+import org.eclipse.jgit.errors.MissingObjectException;
+import org.eclipse.jgit.errors.StoredObjectRepresentationNotAvailableException;
+import org.eclipse.jgit.lib.AnyObjectId;
+import org.eclipse.jgit.lib.Config;
+import org.eclipse.jgit.lib.Constants;
+import org.eclipse.jgit.lib.NullProgressMonitor;
+import org.eclipse.jgit.lib.ObjectId;
+import org.eclipse.jgit.lib.ObjectIdSubclassMap;
+import org.eclipse.jgit.lib.ObjectLoader;
+import org.eclipse.jgit.lib.ObjectReader;
+import org.eclipse.jgit.lib.ProgressMonitor;
+import org.eclipse.jgit.lib.Repository;
+import org.eclipse.jgit.lib.ThreadSafeProgressMonitor;
+import org.eclipse.jgit.revwalk.ObjectWalk;
+import org.eclipse.jgit.revwalk.RevFlag;
+import org.eclipse.jgit.revwalk.RevObject;
+import org.eclipse.jgit.revwalk.RevSort;
+import org.eclipse.jgit.storage.file.PackIndexWriter;
+import org.eclipse.jgit.util.IO;
+import org.eclipse.jgit.util.TemporaryBuffer;
+
+/**
+ * <p>
+ * PackWriter class is responsible for generating pack files from specified set
+ * of objects from repository. This implementation produce pack files in format
+ * version 2.
+ * </p>
+ * <p>
+ * Source of objects may be specified in two ways:
+ * <ul>
+ * <li>(usually) by providing sets of interesting and uninteresting objects in
+ * repository - all interesting objects and their ancestors except uninteresting
+ * objects and their ancestors will be included in pack, or</li>
+ * <li>by providing iterator of {@link RevObject} specifying exact list and
+ * order of objects in pack</li>
+ * </ul>
+ * Typical usage consists of creating instance intended for some pack,
+ * configuring options, preparing the list of objects by calling
+ * {@link #preparePack(Iterator)} or
+ * {@link #preparePack(ProgressMonitor, Collection, Collection)}, and finally
+ * producing the stream with {@link #writePack(ProgressMonitor, ProgressMonitor, OutputStream)}.
+ * </p>
+ * <p>
+ * Class provide set of configurable options and {@link ProgressMonitor}
+ * support, as operations may take a long time for big repositories. Deltas
+ * searching algorithm is <b>NOT IMPLEMENTED</b> yet - this implementation
+ * relies only on deltas and objects reuse.
+ * </p>
+ * <p>
+ * This class is not thread safe, it is intended to be used in one thread, with
+ * one instance per created pack. Subsequent calls to writePack result in
+ * undefined behavior.
+ * </p>
+ */
+public class PackWriter {
+ /**
+ * Title of {@link ProgressMonitor} task used during counting objects to
+ * pack.
+ *
+ * @see #preparePack(ProgressMonitor, Collection, Collection)
+ */
+ public static final String COUNTING_OBJECTS_PROGRESS = JGitText.get().countingObjects;
+
+ /**
+ * Title of {@link ProgressMonitor} task used during compression.
+ *
+ * @see #writePack(ProgressMonitor, ProgressMonitor, OutputStream)
+ */
+ public static final String COMPRESSING_OBJECTS_PROGRESS = JGitText.get().compressingObjects;
+
+ /**
+ * Title of {@link ProgressMonitor} task used during writing out pack
+ * (objects)
+ *
+ * @see #writePack(ProgressMonitor, ProgressMonitor, OutputStream)
+ */
+ public static final String WRITING_OBJECTS_PROGRESS = JGitText.get().writingObjects;
+
+ /**
+ * Default value of deltas reuse option.
+ *
+ * @see #setReuseDeltas(boolean)
+ */
+ public static final boolean DEFAULT_REUSE_DELTAS = true;
+
+ /**
+ * Default value of objects reuse option.
+ *
+ * @see #setReuseObjects(boolean)
+ */
+ public static final boolean DEFAULT_REUSE_OBJECTS = true;
+
+ /**
+ * Default value of delta base as offset option.
+ *
+ * @see #setDeltaBaseAsOffset(boolean)
+ */
+ public static final boolean DEFAULT_DELTA_BASE_AS_OFFSET = false;
+
+ /**
+ * Default value of maximum delta chain depth.
+ *
+ * @see #setMaxDeltaDepth(int)
+ */
+ public static final int DEFAULT_MAX_DELTA_DEPTH = 50;
+
+ /**
+ * Default window size during packing.
+ *
+ * @see #setDeltaSearchWindowSize(int)
+ */
+ public static final int DEFAULT_DELTA_SEARCH_WINDOW_SIZE = 10;
+
+ static final long DEFAULT_BIG_FILE_THRESHOLD = 50 * 1024 * 1024;
+
+ static final long DEFAULT_DELTA_CACHE_SIZE = 50 * 1024 * 1024;
+
+ static final int DEFAULT_DELTA_CACHE_LIMIT = 100;
+
+ private static final int PACK_VERSION_GENERATED = 2;
+
+ @SuppressWarnings("unchecked")
+ private final List<ObjectToPack> objectsLists[] = new List[Constants.OBJ_TAG + 1];
+ {
+ objectsLists[0] = Collections.<ObjectToPack> emptyList();
+ objectsLists[Constants.OBJ_COMMIT] = new ArrayList<ObjectToPack>();
+ objectsLists[Constants.OBJ_TREE] = new ArrayList<ObjectToPack>();
+ objectsLists[Constants.OBJ_BLOB] = new ArrayList<ObjectToPack>();
+ objectsLists[Constants.OBJ_TAG] = new ArrayList<ObjectToPack>();
+ }
+
+ private final ObjectIdSubclassMap<ObjectToPack> objectsMap = new ObjectIdSubclassMap<ObjectToPack>();
+
+ // edge objects for thin packs
+ private final ObjectIdSubclassMap<ObjectToPack> edgeObjects = new ObjectIdSubclassMap<ObjectToPack>();
+
+ private int compressionLevel;
+
+ private Deflater myDeflater;
+
+ private final ObjectReader reader;
+
+ /** {@link #reader} recast to the reuse interface, if it supports it. */
+ private final ObjectReuseAsIs reuseSupport;
+
+ private List<ObjectToPack> sortedByName;
+
+ private byte packcsum[];
+
+ private boolean reuseDeltas = DEFAULT_REUSE_DELTAS;
+
+ private boolean reuseObjects = DEFAULT_REUSE_OBJECTS;
+
+ private boolean deltaBaseAsOffset = DEFAULT_DELTA_BASE_AS_OFFSET;
+
+ private boolean deltaCompress = true;
+
+ private int maxDeltaDepth = DEFAULT_MAX_DELTA_DEPTH;
+
+ private int deltaSearchWindowSize = DEFAULT_DELTA_SEARCH_WINDOW_SIZE;
+
+ private long deltaSearchMemoryLimit;
+
+ private long deltaCacheSize = DEFAULT_DELTA_CACHE_SIZE;
+
+ private int deltaCacheLimit = DEFAULT_DELTA_CACHE_LIMIT;
+
+ private int indexVersion;
+
+ private long bigFileThreshold = DEFAULT_BIG_FILE_THRESHOLD;
+
+ private int threads = 1;
+
+ private boolean thin;
+
+ private boolean ignoreMissingUninteresting = true;
+
+ /**
+ * Create writer for specified repository.
+ * <p>
+ * Objects for packing are specified in {@link #preparePack(Iterator)} or
+ * {@link #preparePack(ProgressMonitor, Collection, Collection)}.
+ *
+ * @param repo
+ * repository where objects are stored.
+ */
+ public PackWriter(final Repository repo) {
+ this(repo, repo.newObjectReader());
+ }
+
+ /**
+ * Create a writer to load objects from the specified reader.
+ * <p>
+ * Objects for packing are specified in {@link #preparePack(Iterator)} or
+ * {@link #preparePack(ProgressMonitor, Collection, Collection)}.
+ *
+ * @param reader
+ * reader to read from the repository with.
+ */
+ public PackWriter(final ObjectReader reader) {
+ this(null, reader);
+ }
+
+ /**
+ * Create writer for specified repository.
+ * <p>
+ * Objects for packing are specified in {@link #preparePack(Iterator)} or
+ * {@link #preparePack(ProgressMonitor, Collection, Collection)}.
+ *
+ * @param repo
+ * repository where objects are stored.
+ * @param reader
+ * reader to read from the repository with.
+ */
+ public PackWriter(final Repository repo, final ObjectReader reader) {
+ this.reader = reader;
+ if (reader instanceof ObjectReuseAsIs)
+ reuseSupport = ((ObjectReuseAsIs) reader);
+ else
+ reuseSupport = null;
+
+ final PackConfig pc = configOf(repo).get(PackConfig.KEY);
+ deltaSearchWindowSize = pc.deltaWindow;
+ deltaSearchMemoryLimit = pc.deltaWindowMemory;
+ deltaCacheSize = pc.deltaCacheSize;
+ deltaCacheLimit = pc.deltaCacheLimit;
+ maxDeltaDepth = pc.deltaDepth;
+ compressionLevel = pc.compression;
+ indexVersion = pc.indexVersion;
+ bigFileThreshold = pc.bigFileThreshold;
+ threads = pc.threads;
+ }
+
+ private static Config configOf(final Repository repo) {
+ if (repo == null)
+ return new Config();
+ return repo.getConfig();
+ }
+
+ /**
+ * Check whether object is configured to reuse deltas existing in
+ * repository.
+ * <p>
+ * Default setting: {@value #DEFAULT_REUSE_DELTAS}
+ * </p>
+ *
+ * @return true if object is configured to reuse deltas; false otherwise.
+ */
+ public boolean isReuseDeltas() {
+ return reuseDeltas;
+ }
+
+ /**
+ * Set reuse deltas configuration option for this writer. When enabled,
+ * writer will search for delta representation of object in repository and
+ * use it if possible. Normally, only deltas with base to another object
+ * existing in set of objects to pack will be used. Exception is however
+ * thin-pack (see
+ * {@link #preparePack(ProgressMonitor, Collection, Collection)} and
+ * {@link #preparePack(Iterator)}) where base object must exist on other
+ * side machine.
+ * <p>
+ * When raw delta data is directly copied from a pack file, checksum is
+ * computed to verify data.
+ * </p>
+ * <p>
+ * Default setting: {@value #DEFAULT_REUSE_DELTAS}
+ * </p>
+ *
+ * @param reuseDeltas
+ * boolean indicating whether or not try to reuse deltas.
+ */
+ public void setReuseDeltas(boolean reuseDeltas) {
+ this.reuseDeltas = reuseDeltas;
+ }
+
+ /**
+ * Checks whether object is configured to reuse existing objects
+ * representation in repository.
+ * <p>
+ * Default setting: {@value #DEFAULT_REUSE_OBJECTS}
+ * </p>
+ *
+ * @return true if writer is configured to reuse objects representation from
+ * pack; false otherwise.
+ */
+ public boolean isReuseObjects() {
+ return reuseObjects;
+ }
+
+ /**
+ * Set reuse objects configuration option for this writer. If enabled,
+ * writer searches for representation in a pack file. If possible,
+ * compressed data is directly copied from such a pack file. Data checksum
+ * is verified.
+ * <p>
+ * Default setting: {@value #DEFAULT_REUSE_OBJECTS}
+ * </p>
+ *
+ * @param reuseObjects
+ * boolean indicating whether or not writer should reuse existing
+ * objects representation.
+ */
+ public void setReuseObjects(boolean reuseObjects) {
+ this.reuseObjects = reuseObjects;
+ }
+
+ /**
+ * Check whether writer can store delta base as an offset (new style
+ * reducing pack size) or should store it as an object id (legacy style,
+ * compatible with old readers).
+ * <p>
+ * Default setting: {@value #DEFAULT_DELTA_BASE_AS_OFFSET}
+ * </p>
+ *
+ * @return true if delta base is stored as an offset; false if it is stored
+ * as an object id.
+ */
+ public boolean isDeltaBaseAsOffset() {
+ return deltaBaseAsOffset;
+ }
+
+ /**
+ * Set writer delta base format. Delta base can be written as an offset in a
+ * pack file (new approach reducing file size) or as an object id (legacy
+ * approach, compatible with old readers).
+ * <p>
+ * Default setting: {@value #DEFAULT_DELTA_BASE_AS_OFFSET}
+ * </p>
+ *
+ * @param deltaBaseAsOffset
+ * boolean indicating whether delta base can be stored as an
+ * offset.
+ */
+ public void setDeltaBaseAsOffset(boolean deltaBaseAsOffset) {
+ this.deltaBaseAsOffset = deltaBaseAsOffset;
+ }
+
+ /**
+ * Check whether the writer will create new deltas on the fly.
+ * <p>
+ * Default setting: true
+ * </p>
+ *
+ * @return true if the writer will create a new delta when either
+ * {@link #isReuseDeltas()} is false, or no suitable delta is
+ * available for reuse.
+ */
+ public boolean isDeltaCompress() {
+ return deltaCompress;
+ }
+
+ /**
+ * Set whether or not the writer will create new deltas on the fly.
+ *
+ * @param deltaCompress
+ * true to create deltas when {@link #isReuseDeltas()} is false,
+ * or when a suitable delta isn't available for reuse. Set to
+ * false to write whole objects instead.
+ */
+ public void setDeltaCompress(boolean deltaCompress) {
+ this.deltaCompress = deltaCompress;
+ }
+
+ /**
+ * Get maximum depth of delta chain set up for this writer. Generated chains
+ * are not longer than this value.
+ * <p>
+ * Default setting: {@value #DEFAULT_MAX_DELTA_DEPTH}
+ * </p>
+ *
+ * @return maximum delta chain depth.
+ */
+ public int getMaxDeltaDepth() {
+ return maxDeltaDepth;
+ }
+
+ /**
+ * Set up maximum depth of delta chain for this writer. Generated chains are
+ * not longer than this value. Too low value causes low compression level,
+ * while too big makes unpacking (reading) longer.
+ * <p>
+ * Default setting: {@value #DEFAULT_MAX_DELTA_DEPTH}
+ * </p>
+ *
+ * @param maxDeltaDepth
+ * maximum delta chain depth.
+ */
+ public void setMaxDeltaDepth(int maxDeltaDepth) {
+ this.maxDeltaDepth = maxDeltaDepth;
+ }
+
+ /**
+ * Get the number of objects to try when looking for a delta base.
+ * <p>
+ * This limit is per thread, if 4 threads are used the actual memory
+ * used will be 4 times this value.
+ *
+ * @return the object count to be searched.
+ */
+ public int getDeltaSearchWindowSize() {
+ return deltaSearchWindowSize;
+ }
+
+ /**
+ * Set the number of objects considered when searching for a delta base.
+ * <p>
+ * Default setting: {@value #DEFAULT_DELTA_SEARCH_WINDOW_SIZE}
+ * </p>
+ *
+ * @param objectCount
+ * number of objects to search at once. Must be at least 2.
+ */
+ public void setDeltaSearchWindowSize(int objectCount) {
+ if (objectCount <= 2)
+ setDeltaCompress(false);
+ else
+ deltaSearchWindowSize = objectCount;
+ }
+
+ /**
+ * Get maximum number of bytes to put into the delta search window.
+ * <p>
+ * Default setting is 0, for an unlimited amount of memory usage. Actual
+ * memory used is the lower limit of either this setting, or the sum of
+ * space used by at most {@link #getDeltaSearchWindowSize()} objects.
+ * <p>
+ * This limit is per thread, if 4 threads are used the actual memory
+ * limit will be 4 times this value.
+ *
+ * @return the memory limit.
+ */
+ public long getDeltaSearchMemoryLimit() {
+ return deltaSearchMemoryLimit;
+ }
+
+ /**
+ * Set the maximum number of bytes to put into the delta search window.
+ * <p>
+ * Default setting is 0, for an unlimited amount of memory usage. If the
+ * memory limit is reached before {@link #getDeltaSearchWindowSize()} the
+ * window size is temporarily lowered.
+ *
+ * @param memoryLimit
+ * Maximum number of bytes to load at once, 0 for unlimited.
+ */
+ public void setDeltaSearchMemoryLimit(long memoryLimit) {
+ deltaSearchMemoryLimit = memoryLimit;
+ }
+
+ /**
+ * Get the size of the in-memory delta cache.
+ * <p>
+ * This limit is for the entire writer, even if multiple threads are used.
+ *
+ * @return maximum number of bytes worth of delta data to cache in memory.
+ * If 0 the cache is infinite in size (up to the JVM heap limit
+ * anyway). A very tiny size such as 1 indicates the cache is
+ * effectively disabled.
+ */
+ public long getDeltaCacheSize() {
+ return deltaCacheSize;
+ }
+
+ /**
+ * Set the maximum number of bytes of delta data to cache.
+ * <p>
+ * During delta search, up to this many bytes worth of small or hard to
+ * compute deltas will be stored in memory. This cache speeds up writing by
+ * allowing the cached entry to simply be dumped to the output stream.
+ *
+ * @param size
+ * number of bytes to cache. Set to 0 to enable an infinite
+ * cache, set to 1 (an impossible size for any delta) to disable
+ * the cache.
+ */
+ public void setDeltaCacheSize(long size) {
+ deltaCacheSize = size;
+ }
+
+ /**
+ * Maximum size in bytes of a delta to cache.
+ *
+ * @return maximum size (in bytes) of a delta that should be cached.
+ */
+ public int getDeltaCacheLimit() {
+ return deltaCacheLimit;
+ }
+
+ /**
+ * Set the maximum size of a delta that should be cached.
+ * <p>
+ * During delta search, any delta smaller than this size will be cached, up
+ * to the {@link #getDeltaCacheSize()} maximum limit. This speeds up writing
+ * by allowing these cached deltas to be output as-is.
+ *
+ * @param size
+ * maximum size (in bytes) of a delta to be cached.
+ */
+ public void setDeltaCacheLimit(int size) {
+ deltaCacheLimit = size;
+ }
+
+ /**
+ * Get the maximum file size that will be delta compressed.
+ * <p>
+ * Files bigger than this setting will not be delta compressed, as they are
+ * more than likely already highly compressed binary data files that do not
+ * delta compress well, such as MPEG videos.
+ *
+ * @return the configured big file threshold.
+ */
+ public long getBigFileThreshold() {
+ return bigFileThreshold;
+ }
+
+ /**
+ * Set the maximum file size that should be considered for deltas.
+ *
+ * @param bigFileThreshold
+ * the limit, in bytes.
+ */
+ public void setBigFileThreshold(long bigFileThreshold) {
+ this.bigFileThreshold = bigFileThreshold;
+ }
+
+ /**
+ * Get the compression level applied to objects in the pack.
+ *
+ * @return current compression level, see {@link java.util.zip.Deflater}.
+ */
+ public int getCompressionLevel() {
+ return compressionLevel;
+ }
+
+ /**
+ * Set the compression level applied to objects in the pack.
+ *
+ * @param level
+ * compression level, must be a valid level recognized by the
+ * {@link java.util.zip.Deflater} class. Typically this setting
+ * is {@link java.util.zip.Deflater#BEST_SPEED}.
+ */
+ public void setCompressionLevel(int level) {
+ compressionLevel = level;
+ }
+
+ /** @return number of threads used for delta compression. */
+ public int getThreads() {
+ return threads;
+ }
+
+ /**
+ * Set the number of threads to use for delta compression.
+ * <p>
+ * During delta compression, if there are enough objects to be considered
+ * the writer will start up concurrent threads and allow them to compress
+ * different sections of the repository concurrently.
+ *
+ * @param threads
+ * number of threads to use. If <= 0 the number of available
+ * processors for this JVM is used.
+ */
+ public void setThread(int threads) {
+ this.threads = threads;
+ }
+
+ /** @return true if this writer is producing a thin pack. */
+ public boolean isThin() {
+ return thin;
+ }
+
+ /**
+ * @param packthin
+ * a boolean indicating whether writer may pack objects with
+ * delta base object not within set of objects to pack, but
+ * belonging to party repository (uninteresting/boundary) as
+ * determined by set; this kind of pack is used only for
+ * transport; true - to produce thin pack, false - otherwise.
+ */
+ public void setThin(final boolean packthin) {
+ thin = packthin;
+ }
+
+ /**
+ * @return true to ignore objects that are uninteresting and also not found
+ * on local disk; false to throw a {@link MissingObjectException}
+ * out of {@link #preparePack(ProgressMonitor, Collection, Collection)} if an
+ * uninteresting object is not in the source repository. By default,
+ * true, permitting gracefully ignoring of uninteresting objects.
+ */
+ public boolean isIgnoreMissingUninteresting() {
+ return ignoreMissingUninteresting;
+ }
+
+ /**
+ * @param ignore
+ * true if writer should ignore non existing uninteresting
+ * objects during construction set of objects to pack; false
+ * otherwise - non existing uninteresting objects may cause
+ * {@link MissingObjectException}
+ */
+ public void setIgnoreMissingUninteresting(final boolean ignore) {
+ ignoreMissingUninteresting = ignore;
+ }
+
+ /**
+ * Set the pack index file format version this instance will create.
+ *
+ * @param version
+ * the version to write. The special version 0 designates the
+ * oldest (most compatible) format available for the objects.
+ * @see PackIndexWriter
+ */
+ public void setIndexVersion(final int version) {
+ indexVersion = version;
+ }
+
+ /**
+ * Returns objects number in a pack file that was created by this writer.
+ *
+ * @return number of objects in pack.
+ */
+ public int getObjectsNumber() {
+ return objectsMap.size();
+ }
+
+ /**
+ * Prepare the list of objects to be written to the pack stream.
+ * <p>
+ * Iterator <b>exactly</b> determines which objects are included in a pack
+ * and order they appear in pack (except that objects order by type is not
+ * needed at input). This order should conform general rules of ordering
+ * objects in git - by recency and path (type and delta-base first is
+ * internally secured) and responsibility for guaranteeing this order is on
+ * a caller side. Iterator must return each id of object to write exactly
+ * once.
+ * </p>
+ * <p>
+ * When iterator returns object that has {@link RevFlag#UNINTERESTING} flag,
+ * this object won't be included in an output pack. Instead, it is recorded
+ * as edge-object (known to remote repository) for thin-pack. In such a case
+ * writer may pack objects with delta base object not within set of objects
+ * to pack, but belonging to party repository - those marked with
+ * {@link RevFlag#UNINTERESTING} flag. This type of pack is used only for
+ * transport.
+ * </p>
+ *
+ * @param objectsSource
+ * iterator of object to store in a pack; order of objects within
+ * each type is important, ordering by type is not needed;
+ * allowed types for objects are {@link Constants#OBJ_COMMIT},
+ * {@link Constants#OBJ_TREE}, {@link Constants#OBJ_BLOB} and
+ * {@link Constants#OBJ_TAG}; objects returned by iterator may
+ * be later reused by caller as object id and type are internally
+ * copied in each iteration; if object returned by iterator has
+ * {@link RevFlag#UNINTERESTING} flag set, it won't be included
+ * in a pack, but is considered as edge-object for thin-pack.
+ * @throws IOException
+ * when some I/O problem occur during reading objects.
+ */
+ public void preparePack(final Iterator<RevObject> objectsSource)
+ throws IOException {
+ while (objectsSource.hasNext()) {
+ addObject(objectsSource.next());
+ }
+ }
+
+ /**
+ * Prepare the list of objects to be written to the pack stream.
+ * <p>
+ * Basing on these 2 sets, another set of objects to put in a pack file is
+ * created: this set consists of all objects reachable (ancestors) from
+ * interesting objects, except uninteresting objects and their ancestors.
+ * This method uses class {@link ObjectWalk} extensively to find out that
+ * appropriate set of output objects and their optimal order in output pack.
+ * Order is consistent with general git in-pack rules: sort by object type,
+ * recency, path and delta-base first.
+ * </p>
+ *
+ * @param countingMonitor
+ * progress during object enumeration.
+ * @param interestingObjects
+ * collection of objects to be marked as interesting (start
+ * points of graph traversal).
+ * @param uninterestingObjects
+ * collection of objects to be marked as uninteresting (end
+ * points of graph traversal).
+ * @throws IOException
+ * when some I/O problem occur during reading objects.
+ */
+ public void preparePack(ProgressMonitor countingMonitor,
+ final Collection<? extends ObjectId> interestingObjects,
+ final Collection<? extends ObjectId> uninterestingObjects)
+ throws IOException {
+ if (countingMonitor == null)
+ countingMonitor = NullProgressMonitor.INSTANCE;
+ ObjectWalk walker = setUpWalker(interestingObjects,
+ uninterestingObjects);
+ findObjectsToPack(countingMonitor, walker);
+ }
+
+ /**
+ * Determine if the pack file will contain the requested object.
+ *
+ * @param id
+ * the object to test the existence of.
+ * @return true if the object will appear in the output pack file.
+ */
+ public boolean willInclude(final AnyObjectId id) {
+ return objectsMap.get(id) != null;
+ }
+
+ /**
+ * Computes SHA-1 of lexicographically sorted objects ids written in this
+ * pack, as used to name a pack file in repository.
+ *
+ * @return ObjectId representing SHA-1 name of a pack that was created.
+ */
+ public ObjectId computeName() {
+ final byte[] buf = new byte[Constants.OBJECT_ID_LENGTH];
+ final MessageDigest md = Constants.newMessageDigest();
+ for (ObjectToPack otp : sortByName()) {
+ otp.copyRawTo(buf, 0);
+ md.update(buf, 0, Constants.OBJECT_ID_LENGTH);
+ }
+ return ObjectId.fromRaw(md.digest());
+ }
+
+ /**
+ * Create an index file to match the pack file just written.
+ * <p>
+ * This method can only be invoked after {@link #preparePack(Iterator)} or
+ * {@link #preparePack(ProgressMonitor, Collection, Collection)} has been
+ * invoked and completed successfully. Writing a corresponding index is an
+ * optional feature that not all pack users may require.
+ *
+ * @param indexStream
+ * output for the index data. Caller is responsible for closing
+ * this stream.
+ * @throws IOException
+ * the index data could not be written to the supplied stream.
+ */
+ public void writeIndex(final OutputStream indexStream) throws IOException {
+ final List<ObjectToPack> list = sortByName();
+ final PackIndexWriter iw;
+ if (indexVersion <= 0)
+ iw = PackIndexWriter.createOldestPossible(indexStream, list);
+ else
+ iw = PackIndexWriter.createVersion(indexStream, indexVersion);
+ iw.write(list, packcsum);
+ }
+
+ private List<ObjectToPack> sortByName() {
+ if (sortedByName == null) {
+ sortedByName = new ArrayList<ObjectToPack>(objectsMap.size());
+ for (List<ObjectToPack> list : objectsLists) {
+ for (ObjectToPack otp : list)
+ sortedByName.add(otp);
+ }
+ Collections.sort(sortedByName);
+ }
+ return sortedByName;
+ }
+
+ /**
+ * Write the prepared pack to the supplied stream.
+ * <p>
+ * At first, this method collects and sorts objects to pack, then deltas
+ * search is performed if set up accordingly, finally pack stream is
+ * written. {@link ProgressMonitor} tasks {@value #COMPRESSING_OBJECTS_PROGRESS}
+ * (only if reuseDeltas or reuseObjects is enabled) and
+ * {@value #WRITING_OBJECTS_PROGRESS} are updated during packing.
+ * </p>
+ * <p>
+ * All reused objects data checksum (Adler32/CRC32) is computed and
+ * validated against existing checksum.
+ * </p>
+ *
+ * @param compressMonitor
+ * progress monitor to report object compression work.
+ * @param writeMonitor
+ * progress monitor to report the number of objects written.
+ * @param packStream
+ * output stream of pack data. The stream should be buffered by
+ * the caller. The caller is responsible for closing the stream.
+ * @throws IOException
+ * an error occurred reading a local object's data to include in
+ * the pack, or writing compressed object data to the output
+ * stream.
+ */
+ public void writePack(ProgressMonitor compressMonitor,
+ ProgressMonitor writeMonitor, OutputStream packStream)
+ throws IOException {
+ if (compressMonitor == null)
+ compressMonitor = NullProgressMonitor.INSTANCE;
+ if (writeMonitor == null)
+ writeMonitor = NullProgressMonitor.INSTANCE;
+
+ if ((reuseDeltas || reuseObjects) && reuseSupport != null)
+ searchForReuse();
+ if (deltaCompress)
+ searchForDeltas(compressMonitor);
+
+ final PackOutputStream out = new PackOutputStream(writeMonitor,
+ packStream, this);
+
+ writeMonitor.beginTask(WRITING_OBJECTS_PROGRESS, getObjectsNumber());
+ out.writeFileHeader(PACK_VERSION_GENERATED, getObjectsNumber());
+ writeObjects(writeMonitor, out);
+ writeChecksum(out);
+
+ reader.release();
+ writeMonitor.endTask();
+ }
+
+ /** Release all resources used by this writer. */
+ public void release() {
+ reader.release();
+ if (myDeflater != null) {
+ myDeflater.end();
+ myDeflater = null;
+ }
+ }
+
+ private void searchForReuse() throws IOException {
+ for (List<ObjectToPack> list : objectsLists) {
+ for (ObjectToPack otp : list)
+ reuseSupport.selectObjectRepresentation(this, otp);
+ }
+ }
+
+ private void searchForDeltas(ProgressMonitor monitor)
+ throws MissingObjectException, IncorrectObjectTypeException,
+ IOException {
+ // Commits and annotated tags tend to have too many differences to
+ // really benefit from delta compression. Consequently just don't
+ // bother examining those types here.
+ //
+ ObjectToPack[] list = new ObjectToPack[
+ objectsLists[Constants.OBJ_TREE].size()
+ + objectsLists[Constants.OBJ_BLOB].size()
+ + edgeObjects.size()];
+ int cnt = 0;
+ cnt = findObjectsNeedingDelta(list, cnt, Constants.OBJ_TREE);
+ cnt = findObjectsNeedingDelta(list, cnt, Constants.OBJ_BLOB);
+ if (cnt == 0)
+ return;
+
+ // Queue up any edge objects that we might delta against. We won't
+ // be sending these as we assume the other side has them, but we need
+ // them in the search phase below.
+ //
+ for (ObjectToPack eo : edgeObjects) {
+ try {
+ if (loadSize(eo))
+ list[cnt++] = eo;
+ } catch (IOException notAvailable) {
+ // Skip this object. Since we aren't going to write it out
+ // the only consequence of it being unavailable to us is we
+ // may produce a larger data stream than we could have.
+ //
+ if (!ignoreMissingUninteresting)
+ throw notAvailable;
+ }
+ }
+
+ monitor.beginTask(COMPRESSING_OBJECTS_PROGRESS, cnt);
+
+ // Sort the objects by path hash so like files are near each other,
+ // and then by size descending so that bigger files are first. This
+ // applies "Linus' Law" which states that newer files tend to be the
+ // bigger ones, because source files grow and hardly ever shrink.
+ //
+ Arrays.sort(list, 0, cnt, new Comparator<ObjectToPack>() {
+ public int compare(ObjectToPack a, ObjectToPack b) {
+ int cmp = a.getType() - b.getType();
+ if (cmp == 0)
+ cmp = (a.getPathHash() >>> 1) - (b.getPathHash() >>> 1);
+ if (cmp == 0)
+ cmp = (a.getPathHash() & 1) - (b.getPathHash() & 1);
+ if (cmp == 0)
+ cmp = b.getWeight() - a.getWeight();
+ return cmp;
+ }
+ });
+ searchForDeltas(monitor, list, cnt);
+ monitor.endTask();
+ }
+
+ private int findObjectsNeedingDelta(ObjectToPack[] list, int cnt, int type)
+ throws MissingObjectException, IncorrectObjectTypeException,
+ IOException {
+ for (ObjectToPack otp : objectsLists[type]) {
+ if (otp.isDoNotDelta()) // delta is disabled for this path
+ continue;
+ if (otp.isDeltaRepresentation()) // already reusing a delta
+ continue;
+ if (loadSize(otp))
+ list[cnt++] = otp;
+ }
+ return cnt;
+ }
+
+ private boolean loadSize(ObjectToPack e) throws MissingObjectException,
+ IncorrectObjectTypeException, IOException {
+ long sz = reader.getObjectSize(e, e.getType());
+
+ // If its too big for us to handle, skip over it.
+ //
+ if (bigFileThreshold <= sz || Integer.MAX_VALUE <= sz)
+ return false;
+
+ // If its too tiny for the delta compression to work, skip it.
+ //
+ if (sz <= DeltaIndex.BLKSZ)
+ return false;
+
+ e.setWeight((int) sz);
+ return true;
+ }
+
+ private void searchForDeltas(final ProgressMonitor monitor,
+ final ObjectToPack[] list, final int cnt)
+ throws MissingObjectException, IncorrectObjectTypeException,
+ LargeObjectException, IOException {
+ if (threads == 0)
+ threads = Runtime.getRuntime().availableProcessors();
+
+ if (threads <= 1 || cnt <= 2 * getDeltaSearchWindowSize()) {
+ DeltaCache dc = new DeltaCache(this);
+ DeltaWindow dw = new DeltaWindow(this, dc, reader);
+ dw.search(monitor, list, 0, cnt);
+ return;
+ }
+
+ final List<Throwable> errors = Collections
+ .synchronizedList(new ArrayList<Throwable>());
+ final DeltaCache dc = new ThreadSafeDeltaCache(this);
+ final ProgressMonitor pm = new ThreadSafeProgressMonitor(monitor);
+ final ExecutorService pool = Executors.newFixedThreadPool(threads);
+
+ // Guess at the size of batch we want. Because we don't really
+ // have a way for a thread to steal work from another thread if
+ // it ends early, we over partition slightly so the work units
+ // are a bit smaller.
+ //
+ int estSize = cnt / (threads * 2);
+ if (estSize < 2 * getDeltaSearchWindowSize())
+ estSize = 2 * getDeltaSearchWindowSize();
+
+ for (int i = 0; i < cnt;) {
+ final int start = i;
+ final int batchSize;
+
+ if (cnt - i < estSize) {
+ // If we don't have enough to fill the remaining block,
+ // schedule what is left over as a single block.
+ //
+ batchSize = cnt - i;
+ } else {
+ // Try to split the block at the end of a path.
+ //
+ int end = start + estSize;
+ while (end < cnt) {
+ ObjectToPack a = list[end - 1];
+ ObjectToPack b = list[end];
+ if (a.getPathHash() == b.getPathHash())
+ end++;
+ else
+ break;
+ }
+ batchSize = end - start;
+ }
+ i += batchSize;
+
+ pool.submit(new Runnable() {
+ public void run() {
+ try {
+ final ObjectReader or = reader.newReader();
+ try {
+ DeltaWindow dw;
+ dw = new DeltaWindow(PackWriter.this, dc, or);
+ dw.search(pm, list, start, batchSize);
+ } finally {
+ or.release();
+ }
+ } catch (Throwable err) {
+ errors.add(err);
+ }
+ }
+ });
+ }
+
+ // Tell the pool to stop.
+ //
+ pool.shutdown();
+ for (;;) {
+ try {
+ if (pool.awaitTermination(60, TimeUnit.SECONDS))
+ break;
+ } catch (InterruptedException e) {
+ throw new IOException(
+ JGitText.get().packingCancelledDuringObjectsWriting);
+ }
+ }
+
+ // If any thread threw an error, try to report it back as
+ // though we weren't using a threaded search algorithm.
+ //
+ if (!errors.isEmpty()) {
+ Throwable err = errors.get(0);
+ if (err instanceof Error)
+ throw (Error) err;
+ if (err instanceof RuntimeException)
+ throw (RuntimeException) err;
+ if (err instanceof IOException)
+ throw (IOException) err;
+
+ IOException fail = new IOException(err.getMessage());
+ fail.initCause(err);
+ throw fail;
+ }
+ }
+
+ private void writeObjects(ProgressMonitor writeMonitor, PackOutputStream out)
+ throws IOException {
+ for (List<ObjectToPack> list : objectsLists) {
+ for (ObjectToPack otp : list) {
+ if (writeMonitor.isCancelled())
+ throw new IOException(
+ JGitText.get().packingCancelledDuringObjectsWriting);
+ if (!otp.isWritten())
+ writeObject(out, otp);
+ }
+ }
+ }
+
+ private void writeObject(PackOutputStream out, final ObjectToPack otp)
+ throws IOException {
+ if (otp.isWritten())
+ return; // We shouldn't be here.
+
+ otp.markWantWrite();
+ if (otp.isDeltaRepresentation())
+ writeBaseFirst(out, otp);
+
+ out.resetCRC32();
+ otp.setOffset(out.length());
+
+ while (otp.isReuseAsIs()) {
+ try {
+ reuseSupport.copyObjectAsIs(out, otp);
+ out.endObject();
+ otp.setCRC(out.getCRC32());
+ return;
+ } catch (StoredObjectRepresentationNotAvailableException gone) {
+ if (otp.getOffset() == out.length()) {
+ redoSearchForReuse(otp);
+ continue;
+ } else {
+ // Object writing already started, we cannot recover.
+ //
+ CorruptObjectException coe;
+ coe = new CorruptObjectException(otp, "");
+ coe.initCause(gone);
+ throw coe;
+ }
+ }
+ }
+
+ // If we reached here, reuse wasn't possible.
+ //
+ if (otp.isDeltaRepresentation())
+ writeDeltaObjectDeflate(out, otp);
+ else
+ writeWholeObjectDeflate(out, otp);
+ out.endObject();
+ otp.setCRC(out.getCRC32());
+ }
+
+ private void writeBaseFirst(PackOutputStream out, final ObjectToPack otp)
+ throws IOException {
+ ObjectToPack baseInPack = otp.getDeltaBase();
+ if (baseInPack != null) {
+ if (!baseInPack.isWritten()) {
+ if (baseInPack.wantWrite()) {
+ // There is a cycle. Our caller is trying to write the
+ // object we want as a base, and called us. Turn off
+ // delta reuse so we can find another form.
+ //
+ reuseDeltas = false;
+ redoSearchForReuse(otp);
+ reuseDeltas = true;
+ } else {
+ writeObject(out, baseInPack);
+ }
+ }
+ } else if (!thin) {
+ // This should never occur, the base isn't in the pack and
+ // the pack isn't allowed to reference base outside objects.
+ // Write the object as a whole form, even if that is slow.
+ //
+ otp.clearDeltaBase();
+ otp.clearReuseAsIs();
+ }
+ }
+
+ private void redoSearchForReuse(final ObjectToPack otp) throws IOException,
+ MissingObjectException {
+ otp.clearDeltaBase();
+ otp.clearReuseAsIs();
+ reuseSupport.selectObjectRepresentation(this, otp);
+ }
+
+ private void writeWholeObjectDeflate(PackOutputStream out,
+ final ObjectToPack otp) throws IOException {
+ final Deflater deflater = deflater();
+ final ObjectLoader ldr = reader.open(otp, otp.getType());
+
+ out.writeHeader(otp, ldr.getSize());
+
+ deflater.reset();
+ DeflaterOutputStream dst = new DeflaterOutputStream(out, deflater);
+ ldr.copyTo(dst);
+ dst.finish();
+ }
+
+ private void writeDeltaObjectDeflate(PackOutputStream out,
+ final ObjectToPack otp) throws IOException {
+ DeltaCache.Ref ref = otp.popCachedDelta();
+ if (ref != null) {
+ byte[] zbuf = ref.get();
+ if (zbuf != null) {
+ out.writeHeader(otp, otp.getCachedSize());
+ out.write(zbuf);
+ return;
+ }
+ }
+
+ TemporaryBuffer.Heap delta = delta(otp);
+ out.writeHeader(otp, delta.length());
+
+ Deflater deflater = deflater();
+ deflater.reset();
+ DeflaterOutputStream dst = new DeflaterOutputStream(out, deflater);
+ delta.writeTo(dst, null);
+ dst.finish();
+ }
+
+ private TemporaryBuffer.Heap delta(final ObjectToPack otp)
+ throws IOException {
+ DeltaIndex index = new DeltaIndex(buffer(reader, otp.getDeltaBaseId()));
+ byte[] res = buffer(reader, otp);
+
+ // We never would have proposed this pair if the delta would be
+ // larger than the unpacked version of the object. So using it
+ // as our buffer limit is valid: we will never reach it.
+ //
+ TemporaryBuffer.Heap delta = new TemporaryBuffer.Heap(res.length);
+ index.encode(delta, res);
+ return delta;
+ }
+
+ byte[] buffer(ObjectReader or, AnyObjectId objId) throws IOException {
+ ObjectLoader ldr = or.open(objId);
+ if (!ldr.isLarge())
+ return ldr.getCachedBytes();
+
+ // PackWriter should have already pruned objects that
+ // are above the big file threshold, so our chances of
+ // the object being below it are very good. We really
+ // shouldn't be here, unless the implementation is odd.
+
+ // If it really is too big to work with, abort out now.
+ //
+ long sz = ldr.getSize();
+ if (getBigFileThreshold() <= sz || Integer.MAX_VALUE < sz)
+ throw new LargeObjectException(objId.copy());
+
+ // Its considered to be large by the loader, but we really
+ // want it in byte array format. Try to make it happen.
+ //
+ byte[] buf;
+ try {
+ buf = new byte[(int) sz];
+ } catch (OutOfMemoryError noMemory) {
+ LargeObjectException e;
+
+ e = new LargeObjectException(objId.copy());
+ e.initCause(noMemory);
+ throw e;
+ }
+ InputStream in = ldr.openStream();
+ try {
+ IO.readFully(in, buf, 0, buf.length);
+ } finally {
+ in.close();
+ }
+ return buf;
+ }
+
+ private Deflater deflater() {
+ if (myDeflater == null)
+ myDeflater = new Deflater(compressionLevel);
+ return myDeflater;
+ }
+
+ private void writeChecksum(PackOutputStream out) throws IOException {
+ packcsum = out.getDigest();
+ out.write(packcsum);
+ }
+
+ private ObjectWalk setUpWalker(
+ final Collection<? extends ObjectId> interestingObjects,
+ final Collection<? extends ObjectId> uninterestingObjects)
+ throws MissingObjectException, IOException,
+ IncorrectObjectTypeException {
+ final ObjectWalk walker = new ObjectWalk(reader);
+ walker.setRetainBody(false);
+ walker.sort(RevSort.COMMIT_TIME_DESC);
+ if (thin)
+ walker.sort(RevSort.BOUNDARY, true);
+
+ for (ObjectId id : interestingObjects) {
+ RevObject o = walker.parseAny(id);
+ walker.markStart(o);
+ }
+ if (uninterestingObjects != null) {
+ for (ObjectId id : uninterestingObjects) {
+ final RevObject o;
+ try {
+ o = walker.parseAny(id);
+ } catch (MissingObjectException x) {
+ if (ignoreMissingUninteresting)
+ continue;
+ throw x;
+ }
+ walker.markUninteresting(o);
+ }
+ }
+ return walker;
+ }
+
+ private void findObjectsToPack(final ProgressMonitor countingMonitor,
+ final ObjectWalk walker) throws MissingObjectException,
+ IncorrectObjectTypeException, IOException {
+ countingMonitor.beginTask(COUNTING_OBJECTS_PROGRESS,
+ ProgressMonitor.UNKNOWN);
+ RevObject o;
+
+ while ((o = walker.next()) != null) {
+ addObject(o, 0);
+ countingMonitor.update(1);
+ }
+ while ((o = walker.nextObject()) != null) {
+ addObject(o, walker.getPathHashCode());
+ countingMonitor.update(1);
+ }
+ countingMonitor.endTask();
+ }
+
+ /**
+ * Include one object to the output file.
+ * <p>
+ * Objects are written in the order they are added. If the same object is
+ * added twice, it may be written twice, creating a larger than necessary
+ * file.
+ *
+ * @param object
+ * the object to add.
+ * @throws IncorrectObjectTypeException
+ * the object is an unsupported type.
+ */
+ public void addObject(final RevObject object)
+ throws IncorrectObjectTypeException {
+ addObject(object, 0);
+ }
+
+ private void addObject(final RevObject object, final int pathHashCode)
+ throws IncorrectObjectTypeException {
+ if (object.has(RevFlag.UNINTERESTING)) {
+ switch (object.getType()) {
+ case Constants.OBJ_TREE:
+ case Constants.OBJ_BLOB:
+ ObjectToPack otp = new ObjectToPack(object);
+ otp.setPathHash(pathHashCode);
+ otp.setDoNotDelta(true);
+ edgeObjects.add(otp);
+ thin = true;
+ break;
+ }
+ return;
+ }
+
+ final ObjectToPack otp;
+ if (reuseSupport != null)
+ otp = reuseSupport.newObjectToPack(object);
+ else
+ otp = new ObjectToPack(object);
+ otp.setPathHash(pathHashCode);
+
+ try {
+ objectsLists[object.getType()].add(otp);
+ } catch (ArrayIndexOutOfBoundsException x) {
+ throw new IncorrectObjectTypeException(object,
+ JGitText.get().incorrectObjectType_COMMITnorTREEnorBLOBnorTAG);
+ } catch (UnsupportedOperationException x) {
+ // index pointing to "dummy" empty list
+ throw new IncorrectObjectTypeException(object,
+ JGitText.get().incorrectObjectType_COMMITnorTREEnorBLOBnorTAG);
+ }
+ objectsMap.add(otp);
+ }
+
+ /**
+ * Select an object representation for this writer.
+ * <p>
+ * An {@link ObjectReader} implementation should invoke this method once for
+ * each representation available for an object, to allow the writer to find
+ * the most suitable one for the output.
+ *
+ * @param otp
+ * the object being packed.
+ * @param next
+ * the next available representation from the repository.
+ */
+ public void select(ObjectToPack otp, StoredObjectRepresentation next) {
+ int nFmt = next.getFormat();
+ int nWeight;
+ if (otp.isReuseAsIs()) {
+ // We've already chosen to reuse a packed form, if next
+ // cannot beat that break out early.
+ //
+ if (PACK_WHOLE < nFmt)
+ return; // next isn't packed
+ else if (PACK_DELTA < nFmt && otp.isDeltaRepresentation())
+ return; // next isn't a delta, but we are
+
+ nWeight = next.getWeight();
+ if (otp.getWeight() <= nWeight)
+ return; // next would be bigger
+ } else
+ nWeight = next.getWeight();
+
+ if (nFmt == PACK_DELTA && reuseDeltas) {
+ ObjectId baseId = next.getDeltaBase();
+ ObjectToPack ptr = objectsMap.get(baseId);
+ if (ptr != null) {
+ otp.setDeltaBase(ptr);
+ otp.setReuseAsIs();
+ otp.setWeight(nWeight);
+ } else if (thin && edgeObjects.contains(baseId)) {
+ otp.setDeltaBase(baseId);
+ otp.setReuseAsIs();
+ otp.setWeight(nWeight);
+ } else {
+ otp.clearDeltaBase();
+ otp.clearReuseAsIs();
+ }
+ } else if (nFmt == PACK_WHOLE && reuseObjects) {
+ otp.clearDeltaBase();
+ otp.setReuseAsIs();
+ otp.setWeight(nWeight);
+ } else {
+ otp.clearDeltaBase();
+ otp.clearReuseAsIs();
+ }
+
+ otp.select(next);
+ }
+}
diff --git a/org.eclipse.jgit/src/org/eclipse/jgit/storage/pack/StoredObjectRepresentation.java b/org.eclipse.jgit/src/org/eclipse/jgit/storage/pack/StoredObjectRepresentation.java
new file mode 100644
index 0000000000..334ea5ea19
--- /dev/null
+++ b/org.eclipse.jgit/src/org/eclipse/jgit/storage/pack/StoredObjectRepresentation.java
@@ -0,0 +1,90 @@
+/*
+ * Copyright (C) 2010, Google Inc.
+ * and other copyright owners as documented in the project's IP log.
+ *
+ * This program and the accompanying materials are made available
+ * under the terms of the Eclipse Distribution License v1.0 which
+ * accompanies this distribution, is reproduced below, and is
+ * available at http://www.eclipse.org/org/documents/edl-v10.php
+ *
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or
+ * without modification, are permitted provided that the following
+ * conditions are met:
+ *
+ * - Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ *
+ * - Redistributions in binary form must reproduce the above
+ * copyright notice, this list of conditions and the following
+ * disclaimer in the documentation and/or other materials provided
+ * with the distribution.
+ *
+ * - Neither the name of the Eclipse Foundation, Inc. nor the
+ * names of its contributors may be used to endorse or promote
+ * products derived from this software without specific prior
+ * written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND
+ * CONTRIBUTORS "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES,
+ * INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
+ * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR
+ * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
+ * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT
+ * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
+ * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER
+ * CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT,
+ * STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
+ * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF
+ * ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ */
+
+package org.eclipse.jgit.storage.pack;
+
+import org.eclipse.jgit.lib.ObjectId;
+
+/**
+ * An object representation {@link PackWriter} can consider for packing.
+ */
+public class StoredObjectRepresentation {
+ /** Special unknown value for {@link #getWeight()}. */
+ public static final int WEIGHT_UNKNOWN = Integer.MAX_VALUE;
+
+ /** Stored in pack format, as a delta to another object. */
+ public static final int PACK_DELTA = 0;
+
+ /** Stored in pack format, without delta. */
+ public static final int PACK_WHOLE = 1;
+
+ /** Only available after inflating to canonical format. */
+ public static final int FORMAT_OTHER = 2;
+
+ /**
+ * @return relative size of this object's packed form. The special value
+ * {@link #WEIGHT_UNKNOWN} can be returned to indicate the
+ * implementation doesn't know, or cannot supply the weight up
+ * front.
+ */
+ public int getWeight() {
+ return WEIGHT_UNKNOWN;
+ }
+
+ /**
+ * @return true if this is a delta against another object and this is stored
+ * in pack delta format.
+ */
+ public int getFormat() {
+ return FORMAT_OTHER;
+ }
+
+ /**
+ * @return identity of the object this delta applies to in order to recover
+ * the original object content. This method should only be called if
+ * {@link #getFormat()} returned {@link #PACK_DELTA}.
+ */
+ public ObjectId getDeltaBase() {
+ return null;
+ }
+}
diff --git a/org.eclipse.jgit/src/org/eclipse/jgit/storage/pack/ThreadSafeDeltaCache.java b/org.eclipse.jgit/src/org/eclipse/jgit/storage/pack/ThreadSafeDeltaCache.java
new file mode 100644
index 0000000000..141289190a
--- /dev/null
+++ b/org.eclipse.jgit/src/org/eclipse/jgit/storage/pack/ThreadSafeDeltaCache.java
@@ -0,0 +1,86 @@
+/*
+ * Copyright (C) 2010, Google Inc.
+ * and other copyright owners as documented in the project's IP log.
+ *
+ * This program and the accompanying materials are made available
+ * under the terms of the Eclipse Distribution License v1.0 which
+ * accompanies this distribution, is reproduced below, and is
+ * available at http://www.eclipse.org/org/documents/edl-v10.php
+ *
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or
+ * without modification, are permitted provided that the following
+ * conditions are met:
+ *
+ * - Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ *
+ * - Redistributions in binary form must reproduce the above
+ * copyright notice, this list of conditions and the following
+ * disclaimer in the documentation and/or other materials provided
+ * with the distribution.
+ *
+ * - Neither the name of the Eclipse Foundation, Inc. nor the
+ * names of its contributors may be used to endorse or promote
+ * products derived from this software without specific prior
+ * written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND
+ * CONTRIBUTORS "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES,
+ * INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
+ * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR
+ * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
+ * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT
+ * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
+ * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER
+ * CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT,
+ * STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
+ * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF
+ * ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ */
+
+package org.eclipse.jgit.storage.pack;
+
+import java.util.concurrent.locks.ReentrantLock;
+
+class ThreadSafeDeltaCache extends DeltaCache {
+ private final ReentrantLock lock;
+
+ ThreadSafeDeltaCache(PackWriter pw) {
+ super(pw);
+ lock = new ReentrantLock();
+ }
+
+ @Override
+ boolean canCache(int length, ObjectToPack src, ObjectToPack res) {
+ lock.lock();
+ try {
+ return super.canCache(length, src, res);
+ } finally {
+ lock.unlock();
+ }
+ }
+
+ @Override
+ void credit(int reservedSize) {
+ lock.lock();
+ try {
+ super.credit(reservedSize);
+ } finally {
+ lock.unlock();
+ }
+ }
+
+ @Override
+ Ref cache(byte[] data, int actLen, int reservedSize) {
+ data = resize(data, actLen);
+ lock.lock();
+ try {
+ return super.cache(data, actLen, reservedSize);
+ } finally {
+ lock.unlock();
+ }
+ }
+}
diff --git a/org.eclipse.jgit/src/org/eclipse/jgit/transport/BasePackFetchConnection.java b/org.eclipse.jgit/src/org/eclipse/jgit/transport/BasePackFetchConnection.java
index 2819ae26de..af18f18d8e 100644
--- a/org.eclipse.jgit/src/org/eclipse/jgit/transport/BasePackFetchConnection.java
+++ b/org.eclipse.jgit/src/org/eclipse/jgit/transport/BasePackFetchConnection.java
@@ -61,7 +61,6 @@ import org.eclipse.jgit.lib.Config;
import org.eclipse.jgit.lib.Constants;
import org.eclipse.jgit.lib.MutableObjectId;
import org.eclipse.jgit.lib.ObjectId;
-import org.eclipse.jgit.lib.PackLock;
import org.eclipse.jgit.lib.ProgressMonitor;
import org.eclipse.jgit.lib.Ref;
import org.eclipse.jgit.lib.Config.SectionParser;
@@ -73,6 +72,7 @@ import org.eclipse.jgit.revwalk.RevSort;
import org.eclipse.jgit.revwalk.RevWalk;
import org.eclipse.jgit.revwalk.filter.CommitTimeRevFilter;
import org.eclipse.jgit.revwalk.filter.RevFilter;
+import org.eclipse.jgit.storage.file.PackLock;
import org.eclipse.jgit.transport.PacketLineIn.AckNackResult;
import org.eclipse.jgit.util.TemporaryBuffer;
@@ -270,6 +270,12 @@ abstract class BasePackFetchConnection extends BasePackConnection implements
}
}
+ @Override
+ public void close() {
+ walk.release();
+ super.close();
+ }
+
private int maxTimeWanted(final Collection<Ref> wants) {
int maxTime = 0;
for (final Ref r : wants) {
diff --git a/org.eclipse.jgit/src/org/eclipse/jgit/transport/BasePackPushConnection.java b/org.eclipse.jgit/src/org/eclipse/jgit/transport/BasePackPushConnection.java
index 44ccd2d6ad..297105d468 100644
--- a/org.eclipse.jgit/src/org/eclipse/jgit/transport/BasePackPushConnection.java
+++ b/org.eclipse.jgit/src/org/eclipse/jgit/transport/BasePackPushConnection.java
@@ -48,6 +48,7 @@ import java.io.IOException;
import java.text.MessageFormat;
import java.util.ArrayList;
import java.util.Collection;
+import java.util.List;
import java.util.Map;
import org.eclipse.jgit.JGitText;
@@ -56,9 +57,9 @@ import org.eclipse.jgit.errors.NotSupportedException;
import org.eclipse.jgit.errors.PackProtocolException;
import org.eclipse.jgit.errors.TransportException;
import org.eclipse.jgit.lib.ObjectId;
-import org.eclipse.jgit.lib.PackWriter;
import org.eclipse.jgit.lib.ProgressMonitor;
import org.eclipse.jgit.lib.Ref;
+import org.eclipse.jgit.storage.pack.PackWriter;
import org.eclipse.jgit.transport.RemoteRefUpdate.Status;
/**
@@ -226,25 +227,29 @@ class BasePackPushConnection extends BasePackConnection implements
private void writePack(final Map<String, RemoteRefUpdate> refUpdates,
final ProgressMonitor monitor) throws IOException {
- final PackWriter writer = new PackWriter(local, monitor);
- final ArrayList<ObjectId> remoteObjects = new ArrayList<ObjectId>(
- getRefs().size());
- final ArrayList<ObjectId> newObjects = new ArrayList<ObjectId>(
- refUpdates.size());
-
- for (final Ref r : getRefs())
- remoteObjects.add(r.getObjectId());
- remoteObjects.addAll(additionalHaves);
- for (final RemoteRefUpdate r : refUpdates.values()) {
- if (!ObjectId.zeroId().equals(r.getNewObjectId()))
- newObjects.add(r.getNewObjectId());
- }
+ List<ObjectId> remoteObjects = new ArrayList<ObjectId>(getRefs().size());
+ List<ObjectId> newObjects = new ArrayList<ObjectId>(refUpdates.size());
+
+ final long start;
+ final PackWriter writer = new PackWriter(local);
+ try {
- writer.setThin(thinPack);
- writer.setDeltaBaseAsOffset(capableOfsDelta);
- writer.preparePack(newObjects, remoteObjects);
- final long start = System.currentTimeMillis();
- writer.writePack(out);
+ for (final Ref r : getRefs())
+ remoteObjects.add(r.getObjectId());
+ remoteObjects.addAll(additionalHaves);
+ for (final RemoteRefUpdate r : refUpdates.values()) {
+ if (!ObjectId.zeroId().equals(r.getNewObjectId()))
+ newObjects.add(r.getNewObjectId());
+ }
+
+ writer.setThin(thinPack);
+ writer.setDeltaBaseAsOffset(capableOfsDelta);
+ writer.preparePack(monitor, newObjects, remoteObjects);
+ start = System.currentTimeMillis();
+ writer.writePack(monitor, monitor, out);
+ } finally {
+ writer.release();
+ }
out.flush();
packTransferTime = System.currentTimeMillis() - start;
}
diff --git a/org.eclipse.jgit/src/org/eclipse/jgit/transport/BundleFetchConnection.java b/org.eclipse.jgit/src/org/eclipse/jgit/transport/BundleFetchConnection.java
index 3b97dfc0d6..126acab48d 100644
--- a/org.eclipse.jgit/src/org/eclipse/jgit/transport/BundleFetchConnection.java
+++ b/org.eclipse.jgit/src/org/eclipse/jgit/transport/BundleFetchConnection.java
@@ -68,13 +68,13 @@ import org.eclipse.jgit.errors.TransportException;
import org.eclipse.jgit.lib.Constants;
import org.eclipse.jgit.lib.ObjectId;
import org.eclipse.jgit.lib.ObjectIdRef;
-import org.eclipse.jgit.lib.PackLock;
import org.eclipse.jgit.lib.ProgressMonitor;
import org.eclipse.jgit.lib.Ref;
import org.eclipse.jgit.revwalk.RevCommit;
import org.eclipse.jgit.revwalk.RevFlag;
import org.eclipse.jgit.revwalk.RevObject;
import org.eclipse.jgit.revwalk.RevWalk;
+import org.eclipse.jgit.storage.file.PackLock;
import org.eclipse.jgit.util.IO;
import org.eclipse.jgit.util.RawParseUtils;
@@ -213,57 +213,65 @@ class BundleFetchConnection extends BaseFetchConnection {
return;
final RevWalk rw = new RevWalk(transport.local);
- final RevFlag PREREQ = rw.newFlag("PREREQ");
- final RevFlag SEEN = rw.newFlag("SEEN");
+ try {
+ final RevFlag PREREQ = rw.newFlag("PREREQ");
+ final RevFlag SEEN = rw.newFlag("SEEN");
- final Map<ObjectId, String> missing = new HashMap<ObjectId, String>();
- final List<RevObject> commits = new ArrayList<RevObject>();
- for (final Map.Entry<ObjectId, String> e : prereqs.entrySet()) {
- ObjectId p = e.getKey();
- try {
- final RevCommit c = rw.parseCommit(p);
- if (!c.has(PREREQ)) {
- c.add(PREREQ);
- commits.add(c);
+ final Map<ObjectId, String> missing = new HashMap<ObjectId, String>();
+ final List<RevObject> commits = new ArrayList<RevObject>();
+ for (final Map.Entry<ObjectId, String> e : prereqs.entrySet()) {
+ ObjectId p = e.getKey();
+ try {
+ final RevCommit c = rw.parseCommit(p);
+ if (!c.has(PREREQ)) {
+ c.add(PREREQ);
+ commits.add(c);
+ }
+ } catch (MissingObjectException notFound) {
+ missing.put(p, e.getValue());
+ } catch (IOException err) {
+ throw new TransportException(transport.uri, MessageFormat
+ .format(JGitText.get().cannotReadCommit, p.name()),
+ err);
}
- } catch (MissingObjectException notFound) {
- missing.put(p, e.getValue());
- } catch (IOException err) {
- throw new TransportException(transport.uri
- , MessageFormat.format(JGitText.get().cannotReadCommit, p.name()), err);
}
- }
- if (!missing.isEmpty())
- throw new MissingBundlePrerequisiteException(transport.uri, missing);
+ if (!missing.isEmpty())
+ throw new MissingBundlePrerequisiteException(transport.uri,
+ missing);
- for (final Ref r : transport.local.getAllRefs().values()) {
- try {
- rw.markStart(rw.parseCommit(r.getObjectId()));
- } catch (IOException readError) {
- // If we cannot read the value of the ref skip it.
+ for (final Ref r : transport.local.getAllRefs().values()) {
+ try {
+ rw.markStart(rw.parseCommit(r.getObjectId()));
+ } catch (IOException readError) {
+ // If we cannot read the value of the ref skip it.
+ }
}
- }
- int remaining = commits.size();
- try {
- RevCommit c;
- while ((c = rw.next()) != null) {
- if (c.has(PREREQ)) {
- c.add(SEEN);
- if (--remaining == 0)
- break;
+ int remaining = commits.size();
+ try {
+ RevCommit c;
+ while ((c = rw.next()) != null) {
+ if (c.has(PREREQ)) {
+ c.add(SEEN);
+ if (--remaining == 0)
+ break;
+ }
}
+ } catch (IOException err) {
+ throw new TransportException(transport.uri,
+ JGitText.get().cannotReadObject, err);
}
- } catch (IOException err) {
- throw new TransportException(transport.uri, JGitText.get().cannotReadObject, err);
- }
- if (remaining > 0) {
- for (final RevObject o : commits) {
- if (!o.has(SEEN))
- missing.put(o, prereqs.get(o));
+ if (remaining > 0) {
+ for (final RevObject o : commits) {
+ if (!o.has(SEEN))
+ missing.put(o, prereqs.get(o));
+ }
+ throw new MissingBundlePrerequisiteException(transport.uri,
+ missing);
}
- throw new MissingBundlePrerequisiteException(transport.uri, missing);
+ } finally {
+ rw.release();
}
}
diff --git a/org.eclipse.jgit/src/org/eclipse/jgit/transport/BundleWriter.java b/org.eclipse.jgit/src/org/eclipse/jgit/transport/BundleWriter.java
index 7e91557b09..79fa58c368 100644
--- a/org.eclipse.jgit/src/org/eclipse/jgit/transport/BundleWriter.java
+++ b/org.eclipse.jgit/src/org/eclipse/jgit/transport/BundleWriter.java
@@ -57,11 +57,11 @@ import org.eclipse.jgit.JGitText;
import org.eclipse.jgit.lib.AnyObjectId;
import org.eclipse.jgit.lib.Constants;
import org.eclipse.jgit.lib.ObjectId;
-import org.eclipse.jgit.lib.PackWriter;
import org.eclipse.jgit.lib.ProgressMonitor;
import org.eclipse.jgit.lib.Ref;
import org.eclipse.jgit.lib.Repository;
import org.eclipse.jgit.revwalk.RevCommit;
+import org.eclipse.jgit.storage.pack.PackWriter;
/**
* Creates a Git bundle file, for sneaker-net transport to another system.
@@ -92,11 +92,9 @@ public class BundleWriter {
*
* @param repo
* repository where objects are stored.
- * @param monitor
- * operations progress monitor.
*/
- public BundleWriter(final Repository repo, final ProgressMonitor monitor) {
- packWriter = new PackWriter(repo, monitor);
+ public BundleWriter(final Repository repo) {
+ packWriter = new PackWriter(repo);
include = new TreeMap<String, ObjectId>();
assume = new HashSet<RevCommit>();
}
@@ -155,6 +153,8 @@ public class BundleWriter {
* <p>
* This method can only be called once per BundleWriter instance.
*
+ * @param monitor
+ * progress monitor to report bundle writing status to.
* @param os
* the stream the bundle is written to. The stream should be
* buffered by the caller. The caller is responsible for closing
@@ -164,38 +164,43 @@ public class BundleWriter {
* the bundle, or writing compressed object data to the output
* stream.
*/
- public void writeBundle(OutputStream os) throws IOException {
- final HashSet<ObjectId> inc = new HashSet<ObjectId>();
- final HashSet<ObjectId> exc = new HashSet<ObjectId>();
- inc.addAll(include.values());
- for (final RevCommit r : assume)
- exc.add(r.getId());
- packWriter.setThin(exc.size() > 0);
- packWriter.preparePack(inc, exc);
+ public void writeBundle(ProgressMonitor monitor, OutputStream os)
+ throws IOException {
+ try {
+ final HashSet<ObjectId> inc = new HashSet<ObjectId>();
+ final HashSet<ObjectId> exc = new HashSet<ObjectId>();
+ inc.addAll(include.values());
+ for (final RevCommit r : assume)
+ exc.add(r.getId());
+ packWriter.setThin(exc.size() > 0);
+ packWriter.preparePack(monitor, inc, exc);
- final Writer w = new OutputStreamWriter(os, Constants.CHARSET);
- w.write(TransportBundle.V2_BUNDLE_SIGNATURE);
- w.write('\n');
+ final Writer w = new OutputStreamWriter(os, Constants.CHARSET);
+ w.write(TransportBundle.V2_BUNDLE_SIGNATURE);
+ w.write('\n');
- final char[] tmp = new char[Constants.OBJECT_ID_STRING_LENGTH];
- for (final RevCommit a : assume) {
- w.write('-');
- a.copyTo(tmp, w);
- if (a.getRawBuffer() != null) {
+ final char[] tmp = new char[Constants.OBJECT_ID_STRING_LENGTH];
+ for (final RevCommit a : assume) {
+ w.write('-');
+ a.copyTo(tmp, w);
+ if (a.getRawBuffer() != null) {
+ w.write(' ');
+ w.write(a.getShortMessage());
+ }
+ w.write('\n');
+ }
+ for (final Map.Entry<String, ObjectId> e : include.entrySet()) {
+ e.getValue().copyTo(tmp, w);
w.write(' ');
- w.write(a.getShortMessage());
+ w.write(e.getKey());
+ w.write('\n');
}
+
w.write('\n');
+ w.flush();
+ packWriter.writePack(monitor, monitor, os);
+ } finally {
+ packWriter.release();
}
- for (final Map.Entry<String, ObjectId> e : include.entrySet()) {
- e.getValue().copyTo(tmp, w);
- w.write(' ');
- w.write(e.getKey());
- w.write('\n');
- }
-
- w.write('\n');
- w.flush();
- packWriter.writePack(os);
}
}
diff --git a/org.eclipse.jgit/src/org/eclipse/jgit/transport/FetchConnection.java b/org.eclipse.jgit/src/org/eclipse/jgit/transport/FetchConnection.java
index 50c0866f23..9dc54da00b 100644
--- a/org.eclipse.jgit/src/org/eclipse/jgit/transport/FetchConnection.java
+++ b/org.eclipse.jgit/src/org/eclipse/jgit/transport/FetchConnection.java
@@ -51,9 +51,9 @@ import java.util.Set;
import org.eclipse.jgit.errors.TransportException;
import org.eclipse.jgit.lib.ObjectId;
-import org.eclipse.jgit.lib.PackLock;
import org.eclipse.jgit.lib.ProgressMonitor;
import org.eclipse.jgit.lib.Ref;
+import org.eclipse.jgit.storage.file.PackLock;
/**
* Lists known refs from the remote and copies objects of selected refs.
diff --git a/org.eclipse.jgit/src/org/eclipse/jgit/transport/FetchProcess.java b/org.eclipse.jgit/src/org/eclipse/jgit/transport/FetchProcess.java
index fc203f69c8..ca68858059 100644
--- a/org.eclipse.jgit/src/org/eclipse/jgit/transport/FetchProcess.java
+++ b/org.eclipse.jgit/src/org/eclipse/jgit/transport/FetchProcess.java
@@ -63,14 +63,14 @@ import org.eclipse.jgit.errors.MissingObjectException;
import org.eclipse.jgit.errors.NotSupportedException;
import org.eclipse.jgit.errors.TransportException;
import org.eclipse.jgit.lib.Constants;
-import org.eclipse.jgit.lib.LockFile;
import org.eclipse.jgit.lib.ObjectId;
-import org.eclipse.jgit.lib.PackLock;
import org.eclipse.jgit.lib.ProgressMonitor;
import org.eclipse.jgit.lib.Ref;
import org.eclipse.jgit.lib.Repository;
import org.eclipse.jgit.revwalk.ObjectWalk;
import org.eclipse.jgit.revwalk.RevWalk;
+import org.eclipse.jgit.storage.file.LockFile;
+import org.eclipse.jgit.storage.file.PackLock;
class FetchProcess {
/** Transport we will fetch over. */
@@ -176,16 +176,21 @@ class FetchProcess {
}
final RevWalk walk = new RevWalk(transport.local);
- if (transport.isRemoveDeletedRefs())
- deleteStaleTrackingRefs(result, walk);
- for (TrackingRefUpdate u : localUpdates) {
- try {
- u.update(walk);
- result.add(u);
- } catch (IOException err) {
- throw new TransportException(MessageFormat.format(
- JGitText.get().failureUpdatingTrackingRef, u.getLocalName(), err.getMessage()), err);
+ try {
+ if (transport.isRemoveDeletedRefs())
+ deleteStaleTrackingRefs(result, walk);
+ for (TrackingRefUpdate u : localUpdates) {
+ try {
+ u.update(walk);
+ result.add(u);
+ } catch (IOException err) {
+ throw new TransportException(MessageFormat.format(JGitText
+ .get().failureUpdatingTrackingRef,
+ u.getLocalName(), err.getMessage()), err);
+ }
}
+ } finally {
+ walk.release();
}
if (!fetchHeadUpdates.isEmpty()) {
@@ -271,8 +276,10 @@ class FetchProcess {
}
private void updateFETCH_HEAD(final FetchResult result) throws IOException {
- final LockFile lock = new LockFile(new File(transport.local
- .getDirectory(), "FETCH_HEAD"));
+ File meta = transport.local.getDirectory();
+ if (meta == null)
+ return;
+ final LockFile lock = new LockFile(new File(meta, "FETCH_HEAD"));
try {
if (lock.lock()) {
final Writer w = new OutputStreamWriter(lock.getOutputStream());
@@ -294,11 +301,15 @@ class FetchProcess {
private boolean askForIsComplete() throws TransportException {
try {
final ObjectWalk ow = new ObjectWalk(transport.local);
- for (final ObjectId want : askFor.keySet())
- ow.markStart(ow.parseAny(want));
- for (final Ref ref : transport.local.getAllRefs().values())
- ow.markUninteresting(ow.parseAny(ref.getObjectId()));
- ow.checkConnectivity();
+ try {
+ for (final ObjectId want : askFor.keySet())
+ ow.markStart(ow.parseAny(want));
+ for (final Ref ref : transport.local.getAllRefs().values())
+ ow.markUninteresting(ow.parseAny(ref.getObjectId()));
+ ow.checkConnectivity();
+ } finally {
+ ow.release();
+ }
return true;
} catch (MissingObjectException e) {
return false;
diff --git a/org.eclipse.jgit/src/org/eclipse/jgit/transport/IndexPack.java b/org.eclipse.jgit/src/org/eclipse/jgit/transport/IndexPack.java
index 491227d091..25b499b32e 100644
--- a/org.eclipse.jgit/src/org/eclipse/jgit/transport/IndexPack.java
+++ b/org.eclipse.jgit/src/org/eclipse/jgit/transport/IndexPack.java
@@ -65,8 +65,8 @@ import org.eclipse.jgit.JGitText;
import org.eclipse.jgit.errors.CorruptObjectException;
import org.eclipse.jgit.errors.MissingObjectException;
import org.eclipse.jgit.lib.AnyObjectId;
-import org.eclipse.jgit.lib.BinaryDelta;
import org.eclipse.jgit.lib.Constants;
+import org.eclipse.jgit.lib.CoreConfig;
import org.eclipse.jgit.lib.InflaterCache;
import org.eclipse.jgit.lib.MutableObjectId;
import org.eclipse.jgit.lib.ObjectChecker;
@@ -74,11 +74,12 @@ import org.eclipse.jgit.lib.ObjectDatabase;
import org.eclipse.jgit.lib.ObjectId;
import org.eclipse.jgit.lib.ObjectIdSubclassMap;
import org.eclipse.jgit.lib.ObjectLoader;
-import org.eclipse.jgit.lib.PackIndexWriter;
-import org.eclipse.jgit.lib.PackLock;
import org.eclipse.jgit.lib.ProgressMonitor;
import org.eclipse.jgit.lib.Repository;
-import org.eclipse.jgit.lib.WindowCursor;
+import org.eclipse.jgit.lib.ObjectReader;
+import org.eclipse.jgit.storage.file.PackIndexWriter;
+import org.eclipse.jgit.storage.file.PackLock;
+import org.eclipse.jgit.storage.pack.BinaryDelta;
import org.eclipse.jgit.util.NB;
/** Indexes Git pack files for local use. */
@@ -127,7 +128,8 @@ public class IndexPack {
base = new File(objdir, n.substring(0, n.length() - suffix.length()));
final IndexPack ip = new IndexPack(db, is, base);
- ip.setIndexVersion(db.getConfig().getCore().getPackIndexVersion());
+ ip.setIndexVersion(db.getConfig().get(CoreConfig.KEY)
+ .getPackIndexVersion());
return ip;
}
@@ -224,7 +226,7 @@ public class IndexPack {
/** If {@link #fixThin} this is the last byte of the original checksum. */
private long originalEOF;
- private WindowCursor readCurs;
+ private ObjectReader readCurs;
/**
* Create a new pack indexer utility.
@@ -243,7 +245,7 @@ public class IndexPack {
objectDatabase = db.getObjectDatabase().newCachedDatabase();
in = src;
inflater = InflaterCache.get();
- readCurs = new WindowCursor();
+ readCurs = objectDatabase.newReader();
buf = new byte[BUFFER_SIZE];
skipBuffer = new byte[512];
objectDigest = Constants.newMessageDigest();
@@ -436,12 +438,18 @@ public class IndexPack {
} finally {
try {
+ if (readCurs != null)
+ readCurs.release();
+ } finally {
+ readCurs = null;
+ }
+
+ try {
InflaterCache.release(inflater);
} finally {
inflater = null;
objectDatabase.close();
}
- readCurs = WindowCursor.release(readCurs);
progress.endTask();
if (packOut != null)
@@ -598,8 +606,10 @@ public class IndexPack {
continue;
if (needBaseObjectIds)
baseObjectIds.add(baseId);
- final ObjectLoader ldr = repo.openObject(readCurs, baseId);
- if (ldr == null) {
+ final ObjectLoader ldr;
+ try {
+ ldr = readCurs.open(baseId);
+ } catch (MissingObjectException notFound) {
missing.add(baseId);
continue;
}
@@ -858,12 +868,16 @@ public class IndexPack {
}
}
- final ObjectLoader ldr = objectDatabase.openObject(readCurs, id);
- if (ldr != null) {
+ try {
+ final ObjectLoader ldr = readCurs.open(id, type);
final byte[] existingData = ldr.getCachedBytes();
- if (ldr.getType() != type || !Arrays.equals(data, existingData)) {
+ if (!Arrays.equals(data, existingData)) {
throw new IOException(MessageFormat.format(JGitText.get().collisionOn, id.name()));
}
+ } catch (MissingObjectException notLocal) {
+ // This is OK, we don't have a copy of the object locally
+ // but the API throws when we try to read it as usually its
+ // an error to read something that doesn't exist.
}
}
diff --git a/org.eclipse.jgit/src/org/eclipse/jgit/transport/PushProcess.java b/org.eclipse.jgit/src/org/eclipse/jgit/transport/PushProcess.java
index 02497cb06f..6cd796a0dc 100644
--- a/org.eclipse.jgit/src/org/eclipse/jgit/transport/PushProcess.java
+++ b/org.eclipse.jgit/src/org/eclipse/jgit/transport/PushProcess.java
@@ -122,32 +122,38 @@ class PushProcess {
*/
PushResult execute(final ProgressMonitor monitor)
throws NotSupportedException, TransportException {
- monitor.beginTask(PROGRESS_OPENING_CONNECTION, ProgressMonitor.UNKNOWN);
-
- final PushResult res = new PushResult();
- connection = transport.openPush();
try {
- res.setAdvertisedRefs(transport.getURI(), connection.getRefsMap());
- res.setRemoteUpdates(toPush);
- monitor.endTask();
-
- final Map<String, RemoteRefUpdate> preprocessed = prepareRemoteUpdates();
- if (transport.isDryRun())
- modifyUpdatesForDryRun();
- else if (!preprocessed.isEmpty())
- connection.push(monitor, preprocessed);
+ monitor.beginTask(PROGRESS_OPENING_CONNECTION,
+ ProgressMonitor.UNKNOWN);
+
+ final PushResult res = new PushResult();
+ connection = transport.openPush();
+ try {
+ res.setAdvertisedRefs(transport.getURI(), connection
+ .getRefsMap());
+ res.setRemoteUpdates(toPush);
+ monitor.endTask();
+
+ final Map<String, RemoteRefUpdate> preprocessed = prepareRemoteUpdates();
+ if (transport.isDryRun())
+ modifyUpdatesForDryRun();
+ else if (!preprocessed.isEmpty())
+ connection.push(monitor, preprocessed);
+ } finally {
+ connection.close();
+ res.addMessages(connection.getMessages());
+ }
+ if (!transport.isDryRun())
+ updateTrackingRefs();
+ for (final RemoteRefUpdate rru : toPush.values()) {
+ final TrackingRefUpdate tru = rru.getTrackingRefUpdate();
+ if (tru != null)
+ res.add(tru);
+ }
+ return res;
} finally {
- connection.close();
- res.addMessages(connection.getMessages());
- }
- if (!transport.isDryRun())
- updateTrackingRefs();
- for (final RemoteRefUpdate rru : toPush.values()) {
- final TrackingRefUpdate tru = rru.getTrackingRefUpdate();
- if (tru != null)
- res.add(tru);
+ walker.release();
}
- return res;
}
private Map<String, RemoteRefUpdate> prepareRemoteUpdates()
diff --git a/org.eclipse.jgit/src/org/eclipse/jgit/transport/ReceivePack.java b/org.eclipse.jgit/src/org/eclipse/jgit/transport/ReceivePack.java
index e42b7fe0c9..6b0a9b6cff 100644
--- a/org.eclipse.jgit/src/org/eclipse/jgit/transport/ReceivePack.java
+++ b/org.eclipse.jgit/src/org/eclipse/jgit/transport/ReceivePack.java
@@ -73,7 +73,6 @@ import org.eclipse.jgit.lib.Constants;
import org.eclipse.jgit.lib.NullProgressMonitor;
import org.eclipse.jgit.lib.ObjectId;
import org.eclipse.jgit.lib.ObjectIdSubclassMap;
-import org.eclipse.jgit.lib.PackLock;
import org.eclipse.jgit.lib.PersonIdent;
import org.eclipse.jgit.lib.Ref;
import org.eclipse.jgit.lib.RefUpdate;
@@ -84,9 +83,9 @@ import org.eclipse.jgit.revwalk.RevBlob;
import org.eclipse.jgit.revwalk.RevCommit;
import org.eclipse.jgit.revwalk.RevFlag;
import org.eclipse.jgit.revwalk.RevObject;
-import org.eclipse.jgit.revwalk.RevTag;
import org.eclipse.jgit.revwalk.RevTree;
import org.eclipse.jgit.revwalk.RevWalk;
+import org.eclipse.jgit.storage.file.PackLock;
import org.eclipse.jgit.transport.ReceiveCommand.Result;
import org.eclipse.jgit.transport.RefAdvertiser.PacketLineOutRefAdvertiser;
import org.eclipse.jgit.util.io.InterruptTimer;
@@ -575,6 +574,7 @@ public class ReceivePack {
service();
} finally {
+ walk.release();
try {
if (pckOut != null)
pckOut.flush();
@@ -697,7 +697,7 @@ public class ReceivePack {
adv.send(refs);
if (head != null && !head.isSymbolic())
adv.advertiseHave(head.getObjectId());
- adv.includeAdditionalHaves();
+ adv.includeAdditionalHaves(db);
if (adv.isEmpty())
adv.advertiseId(ObjectId.zeroId(), "capabilities^{}");
adv.end();
@@ -818,8 +818,7 @@ public class ReceivePack {
ow.markUninteresting(o);
if (checkReferencedIsReachable && !baseObjects.isEmpty()) {
- while (o instanceof RevTag)
- o = ((RevTag) o).getObject();
+ o = ow.peel(o);
if (o instanceof RevCommit)
o = ((RevCommit) o).getTree();
if (o instanceof RevTree)
diff --git a/org.eclipse.jgit/src/org/eclipse/jgit/transport/RefAdvertiser.java b/org.eclipse.jgit/src/org/eclipse/jgit/transport/RefAdvertiser.java
index 694a2e0f16..df0afe73fa 100644
--- a/org.eclipse.jgit/src/org/eclipse/jgit/transport/RefAdvertiser.java
+++ b/org.eclipse.jgit/src/org/eclipse/jgit/transport/RefAdvertiser.java
@@ -49,10 +49,9 @@ import java.util.Map;
import java.util.Set;
import java.util.SortedMap;
-import org.eclipse.jgit.lib.AlternateRepositoryDatabase;
import org.eclipse.jgit.lib.AnyObjectId;
import org.eclipse.jgit.lib.Constants;
-import org.eclipse.jgit.lib.ObjectDatabase;
+import org.eclipse.jgit.lib.ObjectId;
import org.eclipse.jgit.lib.Ref;
import org.eclipse.jgit.lib.RefComparator;
import org.eclipse.jgit.lib.Repository;
@@ -126,7 +125,7 @@ public abstract class RefAdvertiser {
* <ul>
* <li>{@link #send(Map)}
* <li>{@link #advertiseHave(AnyObjectId)}
- * <li>{@link #includeAdditionalHaves()}
+ * <li>{@link #includeAdditionalHaves(Repository)}
* </ul>
*
* @param deref
@@ -144,7 +143,7 @@ public abstract class RefAdvertiser {
* <ul>
* <li>{@link #send(Map)}
* <li>{@link #advertiseHave(AnyObjectId)}
- * <li>{@link #includeAdditionalHaves()}
+ * <li>{@link #includeAdditionalHaves(Repository)}
* </ul>
*
* @param name
@@ -212,24 +211,15 @@ public abstract class RefAdvertiser {
/**
* Include references of alternate repositories as {@code .have} lines.
*
+ * @param src
+ * repository to get the additional reachable objects from.
* @throws IOException
* the underlying output stream failed to write out an
* advertisement record.
*/
- public void includeAdditionalHaves() throws IOException {
- additionalHaves(walk.getRepository().getObjectDatabase());
- }
-
- private void additionalHaves(final ObjectDatabase db) throws IOException {
- if (db instanceof AlternateRepositoryDatabase)
- additionalHaves(((AlternateRepositoryDatabase) db).getRepository());
- for (ObjectDatabase alt : db.getAlternates())
- additionalHaves(alt);
- }
-
- private void additionalHaves(final Repository alt) throws IOException {
- for (final Ref r : alt.getAllRefs().values())
- advertiseHave(r.getObjectId());
+ public void includeAdditionalHaves(Repository src) throws IOException {
+ for (ObjectId id : src.getAdditionalHaves())
+ advertiseHave(id);
}
/** @return true if no advertisements have been sent yet. */
diff --git a/org.eclipse.jgit/src/org/eclipse/jgit/transport/RemoteRefUpdate.java b/org.eclipse.jgit/src/org/eclipse/jgit/transport/RemoteRefUpdate.java
index 1b17c9f0f4..37e03fd62a 100644
--- a/org.eclipse.jgit/src/org/eclipse/jgit/transport/RemoteRefUpdate.java
+++ b/org.eclipse.jgit/src/org/eclipse/jgit/transport/RemoteRefUpdate.java
@@ -357,6 +357,6 @@ public class RemoteRefUpdate {
+ "..." + (newObjectId != null ? newObjectId.abbreviate(localDb).name() : "(null)")
+ (fastForward ? ", fastForward" : "")
+ ", srcRef=" + srcRef + (forceUpdate ? ", forceUpdate" : "") + ", message=" + (message != null ? "\""
- + message + "\"" : "null") + ", " + localDb.getDirectory() + "]";
+ + message + "\"" : "null") + "]";
}
}
diff --git a/org.eclipse.jgit/src/org/eclipse/jgit/transport/Transport.java b/org.eclipse.jgit/src/org/eclipse/jgit/transport/Transport.java
index e1988a6c85..a8e47afd38 100644
--- a/org.eclipse.jgit/src/org/eclipse/jgit/transport/Transport.java
+++ b/org.eclipse.jgit/src/org/eclipse/jgit/transport/Transport.java
@@ -566,7 +566,7 @@ public abstract class Transport {
* URI passed to {@link #open(Repository, URIish)}.
*/
protected Transport(final Repository local, final URIish uri) {
- final TransferConfig tc = local.getConfig().getTransfer();
+ final TransferConfig tc = local.getConfig().get(TransferConfig.KEY);
this.local = local;
this.uri = uri;
this.checkFetchedObjects = tc.isFsckObjects();
diff --git a/org.eclipse.jgit/src/org/eclipse/jgit/transport/TransportAmazonS3.java b/org.eclipse.jgit/src/org/eclipse/jgit/transport/TransportAmazonS3.java
index 56a5c9796d..79b88b6a73 100644
--- a/org.eclipse.jgit/src/org/eclipse/jgit/transport/TransportAmazonS3.java
+++ b/org.eclipse.jgit/src/org/eclipse/jgit/transport/TransportAmazonS3.java
@@ -126,23 +126,7 @@ public class TransportAmazonS3 extends HttpTransport implements WalkTransport {
throws NotSupportedException {
super(local, uri);
- Properties props = null;
- File propsFile = new File(local.getDirectory(), uri.getUser());
- if (!propsFile.isFile())
- propsFile = new File(local.getFS().userHome(), uri.getUser());
- if (propsFile.isFile()) {
- try {
- props = AmazonS3.properties(propsFile);
- } catch (IOException e) {
- throw new NotSupportedException(MessageFormat.format(JGitText.get().cannotReadFile, propsFile), e);
- }
- } else {
- props = new Properties();
- props.setProperty("accesskey", uri.getUser());
- props.setProperty("secretkey", uri.getPass());
- }
-
- s3 = new AmazonS3(props);
+ s3 = new AmazonS3(loadProperties());
bucket = uri.getHost();
String p = uri.getPath();
@@ -153,6 +137,33 @@ public class TransportAmazonS3 extends HttpTransport implements WalkTransport {
keyPrefix = p;
}
+ private Properties loadProperties() throws NotSupportedException {
+ if (local.getDirectory() != null) {
+ File propsFile = new File(local.getDirectory(), uri.getUser());
+ if (propsFile.isFile())
+ return loadPropertiesFile(propsFile);
+ }
+
+ File propsFile = new File(local.getFS().userHome(), uri.getUser());
+ if (propsFile.isFile())
+ return loadPropertiesFile(propsFile);
+
+ Properties props = new Properties();
+ props.setProperty("accesskey", uri.getUser());
+ props.setProperty("secretkey", uri.getPass());
+ return props;
+ }
+
+ private static Properties loadPropertiesFile(File propsFile)
+ throws NotSupportedException {
+ try {
+ return AmazonS3.properties(propsFile);
+ } catch (IOException e) {
+ throw new NotSupportedException(MessageFormat.format(
+ JGitText.get().cannotReadFile, propsFile), e);
+ }
+ }
+
@Override
public FetchConnection openFetch() throws TransportException {
final DatabaseS3 c = new DatabaseS3(bucket, keyPrefix + "/objects");
diff --git a/org.eclipse.jgit/src/org/eclipse/jgit/transport/TransportHttp.java b/org.eclipse.jgit/src/org/eclipse/jgit/transport/TransportHttp.java
index 71e7bf285e..0f4c1314a3 100644
--- a/org.eclipse.jgit/src/org/eclipse/jgit/transport/TransportHttp.java
+++ b/org.eclipse.jgit/src/org/eclipse/jgit/transport/TransportHttp.java
@@ -85,10 +85,10 @@ import org.eclipse.jgit.lib.ObjectId;
import org.eclipse.jgit.lib.ObjectIdRef;
import org.eclipse.jgit.lib.ProgressMonitor;
import org.eclipse.jgit.lib.Ref;
-import org.eclipse.jgit.lib.RefDirectory;
import org.eclipse.jgit.lib.Repository;
import org.eclipse.jgit.lib.SymbolicRef;
import org.eclipse.jgit.lib.Config.SectionParser;
+import org.eclipse.jgit.storage.file.RefDirectory;
import org.eclipse.jgit.util.HttpSupport;
import org.eclipse.jgit.util.IO;
import org.eclipse.jgit.util.RawParseUtils;
diff --git a/org.eclipse.jgit/src/org/eclipse/jgit/transport/TransportLocal.java b/org.eclipse.jgit/src/org/eclipse/jgit/transport/TransportLocal.java
index 08fd8901d8..c9b18be1f8 100644
--- a/org.eclipse.jgit/src/org/eclipse/jgit/transport/TransportLocal.java
+++ b/org.eclipse.jgit/src/org/eclipse/jgit/transport/TransportLocal.java
@@ -61,6 +61,7 @@ import org.eclipse.jgit.errors.NotSupportedException;
import org.eclipse.jgit.errors.TransportException;
import org.eclipse.jgit.lib.Constants;
import org.eclipse.jgit.lib.Repository;
+import org.eclipse.jgit.storage.file.FileRepository;
import org.eclipse.jgit.util.FS;
import org.eclipse.jgit.util.io.MessageWriter;
import org.eclipse.jgit.util.io.StreamCopyThread;
@@ -174,7 +175,7 @@ class TransportLocal extends Transport implements PackTransport {
final Repository dst;
try {
- dst = new Repository(remoteGitDir);
+ dst = new FileRepository(remoteGitDir);
} catch (IOException err) {
throw new TransportException(uri, JGitText.get().notAGitDirectory);
}
@@ -314,7 +315,7 @@ class TransportLocal extends Transport implements PackTransport {
final Repository dst;
try {
- dst = new Repository(remoteGitDir);
+ dst = new FileRepository(remoteGitDir);
} catch (IOException err) {
throw new TransportException(uri, JGitText.get().notAGitDirectory);
}
diff --git a/org.eclipse.jgit/src/org/eclipse/jgit/transport/UploadPack.java b/org.eclipse.jgit/src/org/eclipse/jgit/transport/UploadPack.java
index 77cc1a6f0a..02ce251bea 100644
--- a/org.eclipse.jgit/src/org/eclipse/jgit/transport/UploadPack.java
+++ b/org.eclipse.jgit/src/org/eclipse/jgit/transport/UploadPack.java
@@ -56,10 +56,10 @@ import java.util.Map;
import java.util.Set;
import org.eclipse.jgit.JGitText;
+import org.eclipse.jgit.errors.MissingObjectException;
import org.eclipse.jgit.errors.PackProtocolException;
import org.eclipse.jgit.lib.NullProgressMonitor;
import org.eclipse.jgit.lib.ObjectId;
-import org.eclipse.jgit.lib.PackWriter;
import org.eclipse.jgit.lib.ProgressMonitor;
import org.eclipse.jgit.lib.Ref;
import org.eclipse.jgit.lib.Repository;
@@ -69,6 +69,7 @@ import org.eclipse.jgit.revwalk.RevFlagSet;
import org.eclipse.jgit.revwalk.RevObject;
import org.eclipse.jgit.revwalk.RevTag;
import org.eclipse.jgit.revwalk.RevWalk;
+import org.eclipse.jgit.storage.pack.PackWriter;
import org.eclipse.jgit.transport.BasePackFetchConnection.MultiAck;
import org.eclipse.jgit.transport.RefAdvertiser.PacketLineOutRefAdvertiser;
import org.eclipse.jgit.util.io.InterruptTimer;
@@ -295,6 +296,7 @@ public class UploadPack {
pckOut = new PacketLineOut(rawOut);
service();
} finally {
+ walk.release();
if (timer != null) {
try {
timer.terminate();
@@ -393,11 +395,15 @@ public class UploadPack {
}
if (!o.has(ADVERTISED))
throw new PackProtocolException(MessageFormat.format(JGitText.get().notValid, id.name()));
- want(o);
+ try {
+ want(o);
+ } catch (IOException e) {
+ throw new PackProtocolException(MessageFormat.format(JGitText.get().notValid, id.name()), e);
+ }
}
}
- private void want(RevObject o) {
+ private void want(RevObject o) throws MissingObjectException, IOException {
if (!o.has(WANT)) {
o.add(WANT);
wantAll.add(o);
@@ -406,9 +412,7 @@ public class UploadPack {
wantCommits.add((RevCommit) o);
else if (o instanceof RevTag) {
- do {
- o = ((RevTag) o).getObject();
- } while (o instanceof RevTag);
+ o = walk.peel(o);
if (o instanceof RevCommit)
want(o);
}
@@ -564,27 +568,30 @@ public class UploadPack {
SideBandOutputStream.CH_PROGRESS, bufsz, rawOut));
}
- final PackWriter pw;
- pw = new PackWriter(db, pm, NullProgressMonitor.INSTANCE);
- pw.setDeltaBaseAsOffset(options.contains(OPTION_OFS_DELTA));
- pw.setThin(thin);
- pw.preparePack(wantAll, commonBase);
- if (options.contains(OPTION_INCLUDE_TAG)) {
- for (final Ref r : refs.values()) {
- final RevObject o;
- try {
- o = walk.parseAny(r.getObjectId());
- } catch (IOException e) {
- continue;
+ final PackWriter pw = new PackWriter(db, walk.getObjectReader());
+ try {
+ pw.setDeltaBaseAsOffset(options.contains(OPTION_OFS_DELTA));
+ pw.setThin(thin);
+ pw.preparePack(pm, wantAll, commonBase);
+ if (options.contains(OPTION_INCLUDE_TAG)) {
+ for (final Ref r : refs.values()) {
+ final RevObject o;
+ try {
+ o = walk.parseAny(r.getObjectId());
+ } catch (IOException e) {
+ continue;
+ }
+ if (o.has(WANT) || !(o instanceof RevTag))
+ continue;
+ final RevTag t = (RevTag) o;
+ if (!pw.willInclude(t) && pw.willInclude(t.getObject()))
+ pw.addObject(t);
}
- if (o.has(WANT) || !(o instanceof RevTag))
- continue;
- final RevTag t = (RevTag) o;
- if (!pw.willInclude(t) && pw.willInclude(t.getObject()))
- pw.addObject(t);
}
+ pw.writePack(pm, NullProgressMonitor.INSTANCE, packOut);
+ } finally {
+ pw.release();
}
- pw.writePack(packOut);
packOut.flush();
if (sideband)
diff --git a/org.eclipse.jgit/src/org/eclipse/jgit/transport/WalkFetchConnection.java b/org.eclipse.jgit/src/org/eclipse/jgit/transport/WalkFetchConnection.java
index 6254745476..237b4314c4 100644
--- a/org.eclipse.jgit/src/org/eclipse/jgit/transport/WalkFetchConnection.java
+++ b/org.eclipse.jgit/src/org/eclipse/jgit/transport/WalkFetchConnection.java
@@ -48,7 +48,6 @@ import java.io.File;
import java.io.FileNotFoundException;
import java.io.FileOutputStream;
import java.io.IOException;
-import java.security.MessageDigest;
import java.text.MessageFormat;
import java.util.ArrayList;
import java.util.Collection;
@@ -63,7 +62,6 @@ import org.eclipse.jgit.JGitText;
import org.eclipse.jgit.errors.CompoundException;
import org.eclipse.jgit.errors.CorruptObjectException;
import org.eclipse.jgit.errors.MissingObjectException;
-import org.eclipse.jgit.errors.ObjectWritingException;
import org.eclipse.jgit.errors.TransportException;
import org.eclipse.jgit.lib.AnyObjectId;
import org.eclipse.jgit.lib.Constants;
@@ -71,12 +69,12 @@ import org.eclipse.jgit.lib.FileMode;
import org.eclipse.jgit.lib.MutableObjectId;
import org.eclipse.jgit.lib.ObjectChecker;
import org.eclipse.jgit.lib.ObjectId;
-import org.eclipse.jgit.lib.PackIndex;
-import org.eclipse.jgit.lib.PackLock;
+import org.eclipse.jgit.lib.ObjectInserter;
+import org.eclipse.jgit.lib.ObjectLoader;
+import org.eclipse.jgit.lib.ObjectReader;
import org.eclipse.jgit.lib.ProgressMonitor;
import org.eclipse.jgit.lib.Ref;
import org.eclipse.jgit.lib.Repository;
-import org.eclipse.jgit.lib.UnpackedObjectLoader;
import org.eclipse.jgit.revwalk.DateRevQueue;
import org.eclipse.jgit.revwalk.RevCommit;
import org.eclipse.jgit.revwalk.RevFlag;
@@ -84,6 +82,10 @@ import org.eclipse.jgit.revwalk.RevObject;
import org.eclipse.jgit.revwalk.RevTag;
import org.eclipse.jgit.revwalk.RevTree;
import org.eclipse.jgit.revwalk.RevWalk;
+import org.eclipse.jgit.storage.file.ObjectDirectory;
+import org.eclipse.jgit.storage.file.PackIndex;
+import org.eclipse.jgit.storage.file.PackLock;
+import org.eclipse.jgit.storage.file.UnpackedObject;
import org.eclipse.jgit.treewalk.TreeWalk;
/**
@@ -165,8 +167,6 @@ class WalkFetchConnection extends BaseFetchConnection {
private final MutableObjectId idBuffer = new MutableObjectId();
- private final MessageDigest objectDigest = Constants.newMessageDigest();
-
/**
* Errors received while trying to obtain an object.
* <p>
@@ -180,10 +180,18 @@ class WalkFetchConnection extends BaseFetchConnection {
private final List<PackLock> packLocks;
+ /** Inserter to write objects onto {@link #local}. */
+ private final ObjectInserter inserter;
+
+ /** Inserter to read objects from {@link #local}. */
+ private final ObjectReader reader;
+
WalkFetchConnection(final WalkTransport t, final WalkRemoteObjectDatabase w) {
Transport wt = (Transport)t;
local = wt.local;
objCheck = wt.isCheckFetchedObjects() ? new ObjectChecker() : null;
+ inserter = local.newObjectInserter();
+ reader = local.newObjectReader();
remotes = new ArrayList<WalkRemoteObjectDatabase>();
remotes.add(w);
@@ -200,9 +208,9 @@ class WalkFetchConnection extends BaseFetchConnection {
fetchErrors = new HashMap<ObjectId, List<Throwable>>();
packLocks = new ArrayList<PackLock>(4);
- revWalk = new RevWalk(local);
+ revWalk = new RevWalk(reader);
revWalk.setRetainBody(false);
- treeWalk = new TreeWalk(local);
+ treeWalk = new TreeWalk(reader);
COMPLETE = revWalk.newFlag("COMPLETE");
IN_WORK_QUEUE = revWalk.newFlag("IN_WORK_QUEUE");
LOCALLY_SEEN = revWalk.newFlag("LOCALLY_SEEN");
@@ -240,8 +248,12 @@ class WalkFetchConnection extends BaseFetchConnection {
@Override
public void close() {
- for (final RemotePack p : unfetchedPacks)
- p.tmpIdx.delete();
+ inserter.release();
+ reader.release();
+ for (final RemotePack p : unfetchedPacks) {
+ if (p.tmpIdx != null)
+ p.tmpIdx.delete();
+ }
for (final WalkRemoteObjectDatabase r : remotes)
r.close();
}
@@ -309,10 +321,17 @@ class WalkFetchConnection extends BaseFetchConnection {
}
private void processBlob(final RevObject obj) throws TransportException {
- if (!local.hasObject(obj))
- throw new TransportException(MessageFormat.format(JGitText.get().cannotReadBlob, obj.name()),
- new MissingObjectException(obj, Constants.TYPE_BLOB));
- obj.add(COMPLETE);
+ try {
+ if (reader.has(obj, Constants.OBJ_BLOB))
+ obj.add(COMPLETE);
+ else
+ throw new TransportException(MessageFormat.format(JGitText
+ .get().cannotReadBlob, obj.name()),
+ new MissingObjectException(obj, Constants.TYPE_BLOB));
+ } catch (IOException error) {
+ throw new TransportException(MessageFormat.format(
+ JGitText.get().cannotReadBlob, obj.name()), error);
+ }
}
private void processTree(final RevObject obj) throws TransportException {
@@ -369,7 +388,7 @@ class WalkFetchConnection extends BaseFetchConnection {
private void downloadObject(final ProgressMonitor pm, final AnyObjectId id)
throws TransportException {
- if (local.hasObject(id))
+ if (alreadyHave(id))
return;
for (;;) {
@@ -456,6 +475,15 @@ class WalkFetchConnection extends BaseFetchConnection {
}
}
+ private boolean alreadyHave(final AnyObjectId id) throws TransportException {
+ try {
+ return reader.has(id);
+ } catch (IOException error) {
+ throw new TransportException(MessageFormat.format(
+ JGitText.get().cannotReadObject, id.name()), error);
+ }
+ }
+
private boolean downloadPackedObject(final ProgressMonitor monitor,
final AnyObjectId id) throws TransportException {
// Search for the object in a remote pack whose index we have,
@@ -512,11 +540,12 @@ class WalkFetchConnection extends BaseFetchConnection {
// it failed the index and pack are unusable and we
// shouldn't consult them again.
//
- pack.tmpIdx.delete();
+ if (pack.tmpIdx != null)
+ pack.tmpIdx.delete();
packItr.remove();
}
- if (!local.hasObject(id)) {
+ if (!alreadyHave(id)) {
// What the hell? This pack claimed to have
// the object, but after indexing we didn't
// actually find it in the pack.
@@ -555,8 +584,7 @@ class WalkFetchConnection extends BaseFetchConnection {
throws TransportException {
try {
final byte[] compressed = remote.open(looseName).toArray();
- verifyLooseObject(id, compressed);
- saveLooseObject(id, compressed);
+ verifyAndInsertLooseObject(id, compressed);
return true;
} catch (FileNotFoundException e) {
// Not available in a loose format from this alternate?
@@ -569,11 +597,11 @@ class WalkFetchConnection extends BaseFetchConnection {
}
}
- private void verifyLooseObject(final AnyObjectId id, final byte[] compressed)
- throws IOException {
- final UnpackedObjectLoader uol;
+ private void verifyAndInsertLooseObject(final AnyObjectId id,
+ final byte[] compressed) throws IOException {
+ final ObjectLoader uol;
try {
- uol = new UnpackedObjectLoader(compressed);
+ uol = UnpackedObject.parse(compressed, id);
} catch (CorruptObjectException parsingError) {
// Some HTTP servers send back a "200 OK" status with an HTML
// page that explains the requested file could not be found.
@@ -592,62 +620,23 @@ class WalkFetchConnection extends BaseFetchConnection {
throw e;
}
- objectDigest.reset();
- objectDigest.update(Constants.encodedTypeString(uol.getType()));
- objectDigest.update((byte) ' ');
- objectDigest.update(Constants.encodeASCII(uol.getSize()));
- objectDigest.update((byte) 0);
- objectDigest.update(uol.getCachedBytes());
- idBuffer.fromRaw(objectDigest.digest(), 0);
-
- if (!AnyObjectId.equals(id, idBuffer)) {
- throw new TransportException(MessageFormat.format(JGitText.get().incorrectHashFor
- , id.name(), idBuffer.name(), Constants.typeString(uol.getType()), compressed.length));
- }
+ final int type = uol.getType();
+ final byte[] raw = uol.getCachedBytes();
if (objCheck != null) {
try {
- objCheck.check(uol.getType(), uol.getCachedBytes());
+ objCheck.check(type, raw);
} catch (CorruptObjectException e) {
throw new TransportException(MessageFormat.format(JGitText.get().transportExceptionInvalid
- , Constants.typeString(uol.getType()), id.name(), e.getMessage()));
+ , Constants.typeString(type), id.name(), e.getMessage()));
}
}
- }
-
- private void saveLooseObject(final AnyObjectId id, final byte[] compressed)
- throws IOException, ObjectWritingException {
- final File tmp;
- tmp = File.createTempFile("noz", null, local.getObjectsDirectory());
- try {
- final FileOutputStream out = new FileOutputStream(tmp);
- try {
- out.write(compressed);
- } finally {
- out.close();
- }
- tmp.setReadOnly();
- } catch (IOException e) {
- tmp.delete();
- throw e;
+ ObjectId act = inserter.insert(type, raw);
+ if (!AnyObjectId.equals(id, act)) {
+ throw new TransportException(MessageFormat.format(JGitText.get().incorrectHashFor
+ , id.name(), act.name(), Constants.typeString(type), compressed.length));
}
-
- final File o = local.toFile(id);
- if (tmp.renameTo(o))
- return;
-
- // Maybe the directory doesn't exist yet as the object
- // directories are always lazily created. Note that we
- // try the rename first as the directory likely does exist.
- //
- o.getParentFile().mkdir();
- if (tmp.renameTo(o))
- return;
-
- tmp.delete();
- if (local.hasObject(id))
- return;
- throw new ObjectWritingException(MessageFormat.format(JGitText.get().unableToStore, id.name()));
+ inserter.flush();
}
private Collection<WalkRemoteObjectDatabase> expandOneAlternate(
@@ -788,12 +777,11 @@ class WalkFetchConnection extends BaseFetchConnection {
final String idxName;
- final File tmpIdx;
+ File tmpIdx;
PackIndex index;
RemotePack(final WalkRemoteObjectDatabase c, final String pn) {
- final File objdir = local.getObjectsDirectory();
connection = c;
packName = pn;
idxName = packName.substring(0, packName.length() - 5) + ".idx";
@@ -803,13 +791,19 @@ class WalkFetchConnection extends BaseFetchConnection {
tn = tn.substring(5);
if (tn.endsWith(".idx"))
tn = tn.substring(0, tn.length() - 4);
- tmpIdx = new File(objdir, "walk-" + tn + ".walkidx");
+
+ if (local.getObjectDatabase() instanceof ObjectDirectory) {
+ tmpIdx = new File(((ObjectDirectory) local.getObjectDatabase())
+ .getDirectory(), "walk-" + tn + ".walkidx");
+ }
}
void openIndex(final ProgressMonitor pm) throws IOException {
if (index != null)
return;
- if (tmpIdx.isFile()) {
+ if (tmpIdx == null)
+ tmpIdx = File.createTempFile("jgit-walk-", ".idx");
+ else if (tmpIdx.isFile()) {
try {
index = PackIndex.open(tmpIdx);
return;
diff --git a/org.eclipse.jgit/src/org/eclipse/jgit/transport/WalkPushConnection.java b/org.eclipse.jgit/src/org/eclipse/jgit/transport/WalkPushConnection.java
index 0edf9678e4..bbc918f256 100644
--- a/org.eclipse.jgit/src/org/eclipse/jgit/transport/WalkPushConnection.java
+++ b/org.eclipse.jgit/src/org/eclipse/jgit/transport/WalkPushConnection.java
@@ -61,12 +61,12 @@ import org.eclipse.jgit.lib.AnyObjectId;
import org.eclipse.jgit.lib.Constants;
import org.eclipse.jgit.lib.ObjectId;
import org.eclipse.jgit.lib.ObjectIdRef;
-import org.eclipse.jgit.lib.PackWriter;
import org.eclipse.jgit.lib.ProgressMonitor;
import org.eclipse.jgit.lib.Ref;
import org.eclipse.jgit.lib.RefWriter;
import org.eclipse.jgit.lib.Repository;
import org.eclipse.jgit.lib.Ref.Storage;
+import org.eclipse.jgit.storage.pack.PackWriter;
import org.eclipse.jgit.transport.RemoteRefUpdate.Status;
/**
@@ -209,8 +209,8 @@ class WalkPushConnection extends BaseConnection implements PushConnection {
String pathPack = null;
String pathIdx = null;
+ final PackWriter pw = new PackWriter(local);
try {
- final PackWriter pw = new PackWriter(local, monitor);
final List<ObjectId> need = new ArrayList<ObjectId>();
final List<ObjectId> have = new ArrayList<ObjectId>();
for (final RemoteRefUpdate r : updates)
@@ -220,7 +220,7 @@ class WalkPushConnection extends BaseConnection implements PushConnection {
if (r.getPeeledObjectId() != null)
have.add(r.getPeeledObjectId());
}
- pw.preparePack(need, have);
+ pw.preparePack(monitor, need, have);
// We don't have to continue further if the pack will
// be an empty pack, as the remote has all objects it
@@ -254,7 +254,7 @@ class WalkPushConnection extends BaseConnection implements PushConnection {
OutputStream os = dest.writeFile(pathPack, monitor, wt + "..pack");
try {
os = new BufferedOutputStream(os);
- pw.writePack(os);
+ pw.writePack(monitor, monitor, os);
} finally {
os.close();
}
@@ -281,6 +281,8 @@ class WalkPushConnection extends BaseConnection implements PushConnection {
safeDelete(pathPack);
throw new TransportException(uri, JGitText.get().cannotStoreObjects, err);
+ } finally {
+ pw.release();
}
}
diff --git a/org.eclipse.jgit/src/org/eclipse/jgit/transport/WalkRemoteObjectDatabase.java b/org.eclipse.jgit/src/org/eclipse/jgit/transport/WalkRemoteObjectDatabase.java
index f1743b378d..0e2adae503 100644
--- a/org.eclipse.jgit/src/org/eclipse/jgit/transport/WalkRemoteObjectDatabase.java
+++ b/org.eclipse.jgit/src/org/eclipse/jgit/transport/WalkRemoteObjectDatabase.java
@@ -62,7 +62,7 @@ import org.eclipse.jgit.lib.ObjectId;
import org.eclipse.jgit.lib.ObjectIdRef;
import org.eclipse.jgit.lib.ProgressMonitor;
import org.eclipse.jgit.lib.Ref;
-import org.eclipse.jgit.lib.RefDirectory;
+import org.eclipse.jgit.storage.file.RefDirectory;
import org.eclipse.jgit.util.IO;
/**
diff --git a/org.eclipse.jgit/src/org/eclipse/jgit/treewalk/AbstractTreeIterator.java b/org.eclipse.jgit/src/org/eclipse/jgit/treewalk/AbstractTreeIterator.java
index 3b68abca99..a54b3e9cfa 100644
--- a/org.eclipse.jgit/src/org/eclipse/jgit/treewalk/AbstractTreeIterator.java
+++ b/org.eclipse.jgit/src/org/eclipse/jgit/treewalk/AbstractTreeIterator.java
@@ -55,8 +55,7 @@ import org.eclipse.jgit.lib.Constants;
import org.eclipse.jgit.lib.FileMode;
import org.eclipse.jgit.lib.MutableObjectId;
import org.eclipse.jgit.lib.ObjectId;
-import org.eclipse.jgit.lib.Repository;
-import org.eclipse.jgit.lib.WindowCursor;
+import org.eclipse.jgit.lib.ObjectReader;
import org.eclipse.jgit.treewalk.filter.TreeFilter;
/**
@@ -405,6 +404,24 @@ public abstract class AbstractTreeIterator {
}
/**
+ * Get the current entry's path hash code.
+ * <p>
+ * This method computes a hash code on the fly for this path, the hash is
+ * suitable to cluster objects that may have similar paths together.
+ *
+ * @return path hash code; any integer may be returned.
+ */
+ public int getEntryPathHashCode() {
+ int hash = 0;
+ for (int i = Math.max(0, pathLen - 16); i < pathLen; i++) {
+ byte c = path[i];
+ if (c != ' ')
+ hash = (hash >>> 2) + (c << 24);
+ }
+ return hash;
+ }
+
+ /**
* Get the byte array buffer object IDs must be copied out of.
* <p>
* The id buffer contains the bytes necessary to construct an ObjectId for
@@ -433,8 +450,8 @@ public abstract class AbstractTreeIterator {
* otherwise the caller would not be able to exit out of the subtree
* iterator correctly and return to continue walking <code>this</code>.
*
- * @param repo
- * repository to load the tree data from.
+ * @param reader
+ * reader to load the tree data from.
* @return a new parser that walks over the current subtree.
* @throws IncorrectObjectTypeException
* the current entry is not actually a tree and cannot be parsed
@@ -442,8 +459,9 @@ public abstract class AbstractTreeIterator {
* @throws IOException
* a loose object or pack file could not be read.
*/
- public abstract AbstractTreeIterator createSubtreeIterator(Repository repo)
- throws IncorrectObjectTypeException, IOException;
+ public abstract AbstractTreeIterator createSubtreeIterator(
+ ObjectReader reader) throws IncorrectObjectTypeException,
+ IOException;
/**
* Create a new iterator as though the current entry were a subtree.
@@ -461,12 +479,10 @@ public abstract class AbstractTreeIterator {
* the caller would not be able to exit out of the subtree iterator
* correctly and return to continue walking <code>this</code>.
*
- * @param repo
- * repository to load the tree data from.
+ * @param reader
+ * reader to load the tree data from.
* @param idBuffer
* temporary ObjectId buffer for use by this method.
- * @param curs
- * window cursor to use during repository access.
* @return a new parser that walks over the current subtree.
* @throws IncorrectObjectTypeException
* the current entry is not actually a tree and cannot be parsed
@@ -474,10 +490,10 @@ public abstract class AbstractTreeIterator {
* @throws IOException
* a loose object or pack file could not be read.
*/
- public AbstractTreeIterator createSubtreeIterator(final Repository repo,
- final MutableObjectId idBuffer, final WindowCursor curs)
+ public AbstractTreeIterator createSubtreeIterator(
+ final ObjectReader reader, final MutableObjectId idBuffer)
throws IncorrectObjectTypeException, IOException {
- return createSubtreeIterator(repo);
+ return createSubtreeIterator(reader);
}
/**
diff --git a/org.eclipse.jgit/src/org/eclipse/jgit/treewalk/CanonicalTreeParser.java b/org.eclipse.jgit/src/org/eclipse/jgit/treewalk/CanonicalTreeParser.java
index 0b9dc00446..8e4094a055 100644
--- a/org.eclipse.jgit/src/org/eclipse/jgit/treewalk/CanonicalTreeParser.java
+++ b/org.eclipse.jgit/src/org/eclipse/jgit/treewalk/CanonicalTreeParser.java
@@ -54,9 +54,7 @@ import org.eclipse.jgit.lib.Constants;
import org.eclipse.jgit.lib.FileMode;
import org.eclipse.jgit.lib.MutableObjectId;
import org.eclipse.jgit.lib.ObjectId;
-import org.eclipse.jgit.lib.ObjectLoader;
-import org.eclipse.jgit.lib.Repository;
-import org.eclipse.jgit.lib.WindowCursor;
+import org.eclipse.jgit.lib.ObjectReader;
/** Parses raw Git trees from the canonical semi-text/semi-binary format. */
public class CanonicalTreeParser extends AbstractTreeIterator {
@@ -86,13 +84,11 @@ public class CanonicalTreeParser extends AbstractTreeIterator {
* may be null or the empty array to indicate the prefix is the
* root of the repository. A trailing slash ('/') is
* automatically appended if the prefix does not end in '/'.
- * @param repo
- * repository to load the tree data from.
+ * @param reader
+ * reader to load the tree data from.
* @param treeId
* identity of the tree being parsed; used only in exception
* messages if data corruption is found.
- * @param curs
- * a window cursor to use during data access from the repository.
* @throws MissingObjectException
* the object supplied is not available from the repository.
* @throws IncorrectObjectTypeException
@@ -101,11 +97,11 @@ public class CanonicalTreeParser extends AbstractTreeIterator {
* @throws IOException
* a loose object or pack file could not be read.
*/
- public CanonicalTreeParser(final byte[] prefix, final Repository repo,
- final AnyObjectId treeId, final WindowCursor curs)
- throws IncorrectObjectTypeException, IOException {
+ public CanonicalTreeParser(final byte[] prefix, final ObjectReader reader,
+ final AnyObjectId treeId) throws IncorrectObjectTypeException,
+ IOException {
super(prefix);
- reset(repo, treeId, curs);
+ reset(reader, treeId);
}
private CanonicalTreeParser(final CanonicalTreeParser p) {
@@ -131,13 +127,11 @@ public class CanonicalTreeParser extends AbstractTreeIterator {
/**
* Reset this parser to walk through the given tree.
*
- * @param repo
- * repository to load the tree data from.
+ * @param reader
+ * reader to use during repository access.
* @param id
* identity of the tree being parsed; used only in exception
* messages if data corruption is found.
- * @param curs
- * window cursor to use during repository access.
* @return the root level parser.
* @throws MissingObjectException
* the object supplied is not available from the repository.
@@ -147,13 +141,13 @@ public class CanonicalTreeParser extends AbstractTreeIterator {
* @throws IOException
* a loose object or pack file could not be read.
*/
- public CanonicalTreeParser resetRoot(final Repository repo,
- final AnyObjectId id, final WindowCursor curs)
- throws IncorrectObjectTypeException, IOException {
+ public CanonicalTreeParser resetRoot(final ObjectReader reader,
+ final AnyObjectId id) throws IncorrectObjectTypeException,
+ IOException {
CanonicalTreeParser p = this;
while (p.parent != null)
p = (CanonicalTreeParser) p.parent;
- p.reset(repo, id, curs);
+ p.reset(reader, id);
return p;
}
@@ -181,13 +175,11 @@ public class CanonicalTreeParser extends AbstractTreeIterator {
/**
* Reset this parser to walk through the given tree.
*
- * @param repo
- * repository to load the tree data from.
+ * @param reader
+ * reader to use during repository access.
* @param id
* identity of the tree being parsed; used only in exception
* messages if data corruption is found.
- * @param curs
- * window cursor to use during repository access.
* @throws MissingObjectException
* the object supplied is not available from the repository.
* @throws IncorrectObjectTypeException
@@ -196,32 +188,21 @@ public class CanonicalTreeParser extends AbstractTreeIterator {
* @throws IOException
* a loose object or pack file could not be read.
*/
- public void reset(final Repository repo, final AnyObjectId id,
- final WindowCursor curs)
+ public void reset(final ObjectReader reader, final AnyObjectId id)
throws IncorrectObjectTypeException, IOException {
- final ObjectLoader ldr = repo.openObject(curs, id);
- if (ldr == null) {
- final ObjectId me = id.toObjectId();
- throw new MissingObjectException(me, Constants.TYPE_TREE);
- }
- final byte[] subtreeData = ldr.getCachedBytes();
- if (ldr.getType() != Constants.OBJ_TREE) {
- final ObjectId me = id.toObjectId();
- throw new IncorrectObjectTypeException(me, Constants.TYPE_TREE);
- }
- reset(subtreeData);
+ reset(reader.open(id, Constants.OBJ_TREE).getCachedBytes());
}
@Override
- public CanonicalTreeParser createSubtreeIterator(final Repository repo,
- final MutableObjectId idBuffer, final WindowCursor curs)
+ public CanonicalTreeParser createSubtreeIterator(final ObjectReader reader,
+ final MutableObjectId idBuffer)
throws IncorrectObjectTypeException, IOException {
idBuffer.fromRaw(idBuffer(), idOffset());
if (!FileMode.TREE.equals(mode)) {
final ObjectId me = idBuffer.toObjectId();
throw new IncorrectObjectTypeException(me, Constants.TYPE_TREE);
}
- return createSubtreeIterator0(repo, idBuffer, curs);
+ return createSubtreeIterator0(reader, idBuffer);
}
/**
@@ -231,32 +212,25 @@ public class CanonicalTreeParser extends AbstractTreeIterator {
* called only once the current entry has been identified as a tree and its
* identity has been converted into an ObjectId.
*
- * @param repo
- * repository to load the tree data from.
+ * @param reader
+ * reader to load the tree data from.
* @param id
* ObjectId of the tree to open.
- * @param curs
- * window cursor to use during repository access.
* @return a new parser that walks over the current subtree.
* @throws IOException
* a loose object or pack file could not be read.
*/
public final CanonicalTreeParser createSubtreeIterator0(
- final Repository repo, final AnyObjectId id, final WindowCursor curs)
+ final ObjectReader reader, final AnyObjectId id)
throws IOException {
final CanonicalTreeParser p = new CanonicalTreeParser(this);
- p.reset(repo, id, curs);
+ p.reset(reader, id);
return p;
}
- public CanonicalTreeParser createSubtreeIterator(final Repository repo)
+ public CanonicalTreeParser createSubtreeIterator(final ObjectReader reader)
throws IncorrectObjectTypeException, IOException {
- final WindowCursor curs = new WindowCursor();
- try {
- return createSubtreeIterator(repo, new MutableObjectId(), curs);
- } finally {
- curs.release();
- }
+ return createSubtreeIterator(reader, new MutableObjectId());
}
@Override
diff --git a/org.eclipse.jgit/src/org/eclipse/jgit/treewalk/EmptyTreeIterator.java b/org.eclipse.jgit/src/org/eclipse/jgit/treewalk/EmptyTreeIterator.java
index 1776b50887..7d4ee6d2bd 100644
--- a/org.eclipse.jgit/src/org/eclipse/jgit/treewalk/EmptyTreeIterator.java
+++ b/org.eclipse.jgit/src/org/eclipse/jgit/treewalk/EmptyTreeIterator.java
@@ -50,7 +50,7 @@ import java.io.IOException;
import org.eclipse.jgit.errors.CorruptObjectException;
import org.eclipse.jgit.errors.IncorrectObjectTypeException;
import org.eclipse.jgit.lib.ObjectId;
-import org.eclipse.jgit.lib.Repository;
+import org.eclipse.jgit.lib.ObjectReader;
/** Iterator over an empty tree (a directory with no files). */
public class EmptyTreeIterator extends AbstractTreeIterator {
@@ -87,7 +87,7 @@ public class EmptyTreeIterator extends AbstractTreeIterator {
}
@Override
- public AbstractTreeIterator createSubtreeIterator(final Repository repo)
+ public AbstractTreeIterator createSubtreeIterator(final ObjectReader reader)
throws IncorrectObjectTypeException, IOException {
return new EmptyTreeIterator(this);
}
diff --git a/org.eclipse.jgit/src/org/eclipse/jgit/treewalk/FileTreeIterator.java b/org.eclipse.jgit/src/org/eclipse/jgit/treewalk/FileTreeIterator.java
index 603dcf2baa..2d032ab835 100644
--- a/org.eclipse.jgit/src/org/eclipse/jgit/treewalk/FileTreeIterator.java
+++ b/org.eclipse.jgit/src/org/eclipse/jgit/treewalk/FileTreeIterator.java
@@ -54,6 +54,7 @@ import java.io.InputStream;
import org.eclipse.jgit.errors.IncorrectObjectTypeException;
import org.eclipse.jgit.lib.Constants;
import org.eclipse.jgit.lib.FileMode;
+import org.eclipse.jgit.lib.ObjectReader;
import org.eclipse.jgit.lib.Repository;
import org.eclipse.jgit.util.FS;
@@ -83,7 +84,7 @@ public class FileTreeIterator extends WorkingTreeIterator {
* the repository whose working tree will be scanned.
*/
public FileTreeIterator(Repository repo) {
- this(repo.getWorkDir(), repo.getFS());
+ this(repo.getWorkTree(), repo.getFS());
initRootIterator(repo);
}
@@ -123,7 +124,7 @@ public class FileTreeIterator extends WorkingTreeIterator {
}
@Override
- public AbstractTreeIterator createSubtreeIterator(final Repository repo)
+ public AbstractTreeIterator createSubtreeIterator(final ObjectReader reader)
throws IncorrectObjectTypeException, IOException {
return new FileTreeIterator(this, ((FileEntry) current()).file, fs);
}
diff --git a/org.eclipse.jgit/src/org/eclipse/jgit/treewalk/NameConflictTreeWalk.java b/org.eclipse.jgit/src/org/eclipse/jgit/treewalk/NameConflictTreeWalk.java
index b569174bdb..99126e8615 100644
--- a/org.eclipse.jgit/src/org/eclipse/jgit/treewalk/NameConflictTreeWalk.java
+++ b/org.eclipse.jgit/src/org/eclipse/jgit/treewalk/NameConflictTreeWalk.java
@@ -46,6 +46,7 @@ package org.eclipse.jgit.treewalk;
import org.eclipse.jgit.dircache.DirCacheBuilder;
import org.eclipse.jgit.errors.CorruptObjectException;
import org.eclipse.jgit.lib.FileMode;
+import org.eclipse.jgit.lib.ObjectReader;
import org.eclipse.jgit.lib.Repository;
/**
@@ -93,7 +94,17 @@ public class NameConflictTreeWalk extends TreeWalk {
* the repository the walker will obtain data from.
*/
public NameConflictTreeWalk(final Repository repo) {
- super(repo);
+ this(repo.newObjectReader());
+ }
+
+ /**
+ * Create a new tree walker for a given repository.
+ *
+ * @param or
+ * the reader the walker will obtain tree data from.
+ */
+ public NameConflictTreeWalk(final ObjectReader or) {
+ super(or);
}
@Override
diff --git a/org.eclipse.jgit/src/org/eclipse/jgit/treewalk/TreeWalk.java b/org.eclipse.jgit/src/org/eclipse/jgit/treewalk/TreeWalk.java
index 68965c1903..39e09a330c 100644
--- a/org.eclipse.jgit/src/org/eclipse/jgit/treewalk/TreeWalk.java
+++ b/org.eclipse.jgit/src/org/eclipse/jgit/treewalk/TreeWalk.java
@@ -57,7 +57,7 @@ import org.eclipse.jgit.lib.FileMode;
import org.eclipse.jgit.lib.MutableObjectId;
import org.eclipse.jgit.lib.ObjectId;
import org.eclipse.jgit.lib.Repository;
-import org.eclipse.jgit.lib.WindowCursor;
+import org.eclipse.jgit.lib.ObjectReader;
import org.eclipse.jgit.revwalk.RevTree;
import org.eclipse.jgit.treewalk.filter.PathFilterGroup;
import org.eclipse.jgit.treewalk.filter.TreeFilter;
@@ -113,11 +113,15 @@ public class TreeWalk {
final AnyObjectId... trees) throws MissingObjectException,
IncorrectObjectTypeException, CorruptObjectException, IOException {
final TreeWalk r = new TreeWalk(db);
- r.setFilter(PathFilterGroup.createFromStrings(Collections
- .singleton(path)));
- r.setRecursive(r.getFilter().shouldBeRecursive());
- r.reset(trees);
- return r.next() ? r : null;
+ try {
+ r.setFilter(PathFilterGroup.createFromStrings(Collections
+ .singleton(path)));
+ r.setRecursive(r.getFilter().shouldBeRecursive());
+ r.reset(trees);
+ return r.next() ? r : null;
+ } finally {
+ r.release();
+ }
}
/**
@@ -151,12 +155,10 @@ public class TreeWalk {
return forPath(db, path, new ObjectId[] { tree });
}
- private final Repository db;
+ private final ObjectReader reader;
private final MutableObjectId idBuffer = new MutableObjectId();
- private final WindowCursor curs = new WindowCursor();
-
private TreeFilter filter;
AbstractTreeIterator[] trees;
@@ -180,18 +182,34 @@ public class TreeWalk {
* the repository the walker will obtain data from.
*/
public TreeWalk(final Repository repo) {
- db = repo;
+ this(repo.newObjectReader());
+ }
+
+ /**
+ * Create a new tree walker for a given repository.
+ *
+ * @param or
+ * the reader the walker will obtain tree data from.
+ */
+ public TreeWalk(final ObjectReader or) {
+ reader = or;
filter = TreeFilter.ALL;
trees = new AbstractTreeIterator[] { new EmptyTreeIterator() };
}
+ /** @return the reader this walker is using to load objects. */
+ public ObjectReader getObjectReader() {
+ return reader;
+ }
+
/**
- * Get the repository this tree walker is reading from.
- *
- * @return the repository configured when the walker was created.
+ * Release any resources used by this walker's reader.
+ * <p>
+ * A walker that has been released can be used again, but may need to be
+ * released after the subsequent usage.
*/
- public Repository getRepository() {
- return db;
+ public void release() {
+ reader.release();
}
/**
@@ -319,7 +337,7 @@ public class TreeWalk {
if (o instanceof CanonicalTreeParser) {
o.matches = null;
o.matchShift = 0;
- ((CanonicalTreeParser) o).reset(db, id, curs);
+ ((CanonicalTreeParser) o).reset(reader, id);
trees[0] = o;
} else {
trees[0] = parserFor(id);
@@ -366,7 +384,7 @@ public class TreeWalk {
if (o instanceof CanonicalTreeParser && o.pathOffset == 0) {
o.matches = null;
o.matchShift = 0;
- ((CanonicalTreeParser) o).reset(db, ids[i], curs);
+ ((CanonicalTreeParser) o).reset(reader, ids[i]);
r[i] = o;
continue;
}
@@ -836,7 +854,7 @@ public class TreeWalk {
final AbstractTreeIterator t = trees[i];
final AbstractTreeIterator n;
if (t.matches == ch && !t.eof() && FileMode.TREE.equals(t.mode))
- n = t.createSubtreeIterator(db, idBuffer, curs);
+ n = t.createSubtreeIterator(reader, idBuffer);
else
n = t.createEmptyTreeIterator();
tmp[i] = n;
@@ -911,7 +929,7 @@ public class TreeWalk {
private CanonicalTreeParser parserFor(final AnyObjectId id)
throws IncorrectObjectTypeException, IOException {
final CanonicalTreeParser p = new CanonicalTreeParser();
- p.reset(db, id, curs);
+ p.reset(reader, id);
return p;
}
diff --git a/org.eclipse.jgit/src/org/eclipse/jgit/util/ChangeIdUtil.java b/org.eclipse.jgit/src/org/eclipse/jgit/util/ChangeIdUtil.java
index 6c146f79f0..2ffcbed9f1 100644
--- a/org.eclipse.jgit/src/org/eclipse/jgit/util/ChangeIdUtil.java
+++ b/org.eclipse.jgit/src/org/eclipse/jgit/util/ChangeIdUtil.java
@@ -42,13 +42,12 @@
*/
package org.eclipse.jgit.util;
-import java.io.ByteArrayInputStream;
import java.io.IOException;
import java.util.regex.Pattern;
import org.eclipse.jgit.lib.Constants;
+import org.eclipse.jgit.lib.ObjectInserter;
import org.eclipse.jgit.lib.ObjectId;
-import org.eclipse.jgit.lib.ObjectWriter;
import org.eclipse.jgit.lib.PersonIdent;
/**
@@ -113,12 +112,8 @@ public class ChangeIdUtil {
b.append(committer.toExternalString());
b.append("\n\n");
b.append(cleanMessage);
- ObjectWriter w = new ObjectWriter(null);
- byte[] bytes = b.toString().getBytes(Constants.CHARACTER_ENCODING);
- ByteArrayInputStream is = new ByteArrayInputStream(bytes);
- ObjectId sha1 = w.computeObjectSha1(Constants.OBJ_COMMIT, bytes.length,
- is);
- return sha1;
+ return new ObjectInserter.Formatter().idFor(Constants.OBJ_COMMIT, //
+ b.toString().getBytes(Constants.CHARACTER_ENCODING));
}
private static final Pattern issuePattern = Pattern
diff --git a/org.eclipse.jgit/src/org/eclipse/jgit/util/LongList.java b/org.eclipse.jgit/src/org/eclipse/jgit/util/LongList.java
index 26608bb2a1..96b311dfbf 100644
--- a/org.eclipse.jgit/src/org/eclipse/jgit/util/LongList.java
+++ b/org.eclipse.jgit/src/org/eclipse/jgit/util/LongList.java
@@ -83,6 +83,20 @@ public class LongList {
return entries[i];
}
+ /**
+ * Determine if an entry appears in this collection.
+ *
+ * @param value
+ * the value to search for.
+ * @return true of {@code value} appears in this list.
+ */
+ public boolean contains(final long value) {
+ for (int i = 0; i < count; i++)
+ if (entries[i] == value)
+ return true;
+ return false;
+ }
+
/** Empty this list */
public void clear() {
count = 0;
diff --git a/org.eclipse.jgit/src/org/eclipse/jgit/util/SystemReader.java b/org.eclipse.jgit/src/org/eclipse/jgit/util/SystemReader.java
index 9d7feb08f2..f4382eb186 100644
--- a/org.eclipse.jgit/src/org/eclipse/jgit/util/SystemReader.java
+++ b/org.eclipse.jgit/src/org/eclipse/jgit/util/SystemReader.java
@@ -50,7 +50,7 @@ import java.net.InetAddress;
import java.net.UnknownHostException;
import java.util.TimeZone;
-import org.eclipse.jgit.lib.FileBasedConfig;
+import org.eclipse.jgit.storage.file.FileBasedConfig;
/**
* Interface to read values from the system.
diff --git a/org.eclipse.jgit/src/org/eclipse/jgit/util/TemporaryBuffer.java b/org.eclipse.jgit/src/org/eclipse/jgit/util/TemporaryBuffer.java
index 6c421c5f50..baa45c5c61 100644
--- a/org.eclipse.jgit/src/org/eclipse/jgit/util/TemporaryBuffer.java
+++ b/org.eclipse.jgit/src/org/eclipse/jgit/util/TemporaryBuffer.java
@@ -126,7 +126,7 @@ public abstract class TemporaryBuffer extends OutputStream {
blocks.add(s);
}
- final int n = Math.min(Block.SZ - s.count, len);
+ final int n = Math.min(s.buffer.length - s.count, len);
System.arraycopy(b, off, s.buffer, s.count, n);
s.count += n;
len -= n;
@@ -139,6 +139,19 @@ public abstract class TemporaryBuffer extends OutputStream {
}
/**
+ * Dumps the entire buffer into the overflow stream, and flushes it.
+ *
+ * @throws IOException
+ * the overflow stream cannot be started, or the buffer contents
+ * cannot be written to it, or it failed to flush.
+ */
+ protected void doFlush() throws IOException {
+ if (overflow == null)
+ switchToOverflow();
+ overflow.flush();
+ }
+
+ /**
* Copy all bytes remaining on the input stream into this buffer.
*
* @param in
@@ -158,7 +171,7 @@ public abstract class TemporaryBuffer extends OutputStream {
blocks.add(s);
}
- final int n = in.read(s.buffer, s.count, Block.SZ - s.count);
+ int n = in.read(s.buffer, s.count, s.buffer.length - s.count);
if (n < 1)
return;
s.count += n;
@@ -179,8 +192,12 @@ public abstract class TemporaryBuffer extends OutputStream {
* @return total length of the buffer, in bytes.
*/
public long length() {
+ return inCoreLength();
+ }
+
+ private long inCoreLength() {
final Block last = last();
- return ((long) blocks.size()) * Block.SZ - (Block.SZ - last.count);
+ return ((long) blocks.size() - 1) * Block.SZ + last.count;
}
/**
@@ -238,8 +255,13 @@ public abstract class TemporaryBuffer extends OutputStream {
if (overflow != null) {
destroy();
}
- blocks = new ArrayList<Block>(inCoreLimit / Block.SZ);
- blocks.add(new Block());
+ if (inCoreLimit < Block.SZ) {
+ blocks = new ArrayList<Block>(1);
+ blocks.add(new Block(inCoreLimit));
+ } else {
+ blocks = new ArrayList<Block>(inCoreLimit / Block.SZ);
+ blocks.add(new Block());
+ }
}
/**
@@ -257,9 +279,14 @@ public abstract class TemporaryBuffer extends OutputStream {
}
private boolean reachedInCoreLimit() throws IOException {
- if (blocks.size() * Block.SZ < inCoreLimit)
+ if (inCoreLength() < inCoreLimit)
return false;
+ switchToOverflow();
+ return true;
+ }
+
+ private void switchToOverflow() throws IOException {
overflow = overflow();
final Block last = blocks.remove(blocks.size() - 1);
@@ -269,7 +296,6 @@ public abstract class TemporaryBuffer extends OutputStream {
overflow = new BufferedOutputStream(overflow, Block.SZ);
overflow.write(last.buffer, 0, last.count);
- return true;
}
public void close() throws IOException {
@@ -427,12 +453,20 @@ public abstract class TemporaryBuffer extends OutputStream {
static class Block {
static final int SZ = 8 * 1024;
- final byte[] buffer = new byte[SZ];
+ final byte[] buffer;
int count;
+ Block() {
+ buffer = new byte[SZ];
+ }
+
+ Block(int sz) {
+ buffer = new byte[sz];
+ }
+
boolean isFull() {
- return count == SZ;
+ return count == buffer.length;
}
}
}