From e1b312b5f7e1010c3633df2582f6a20373becdf3 Mon Sep 17 00:00:00 2001 From: "Shawn O. Pearce" Date: Wed, 23 Jun 2010 10:11:34 -0700 Subject: Use CoreConfig, UserConfig and TransferConfig directly Rather than relying on the helpers in RepositoryConfig to get these objects, obtain them directly through the Config API. Its only slightly more verbose, but permits us to work with the base Config class, which is more flexible than the highly file specific RepositoryConfig. This is what I really meant to do when I added the section parser and caching support to Config, we just failed to finish updating all of the call sites. Change-Id: I481cb365aa00bfa8c21e5ad0cd367ddd9c6c0edd Signed-off-by: Shawn O. Pearce --- org.eclipse.jgit.pgm/src/org/eclipse/jgit/pgm/IndexPack.java | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) (limited to 'org.eclipse.jgit.pgm/src/org') 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); -- cgit v1.2.3 From 2370ad9514587a354e3a8767d7b10e83587ab9cf Mon Sep 17 00:00:00 2001 From: "Shawn O. Pearce" Date: Thu, 24 Jun 2010 11:39:20 -0700 Subject: Use FileKey to resolve Git repository arguments upload-pack and receive-pack take a git repository as an argument, but its a lenient path format. Locate the repository and open it. Change-Id: I4b377e57b28ba3b1717c13d9ab51a602de1ad257 Signed-off-by: Shawn O. Pearce --- .../src/org/eclipse/jgit/pgm/ReceivePack.java | 18 +++++++++------ .../src/org/eclipse/jgit/pgm/UploadPack.java | 26 +++++++++++++--------- 2 files changed, 26 insertions(+), 18 deletions(-) (limited to 'org.eclipse.jgit.pgm/src/org') 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/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); } } -- cgit v1.2.3 From 4c14b7623dd2ff943350eb0f80d899b00450794f Mon Sep 17 00:00:00 2001 From: "Shawn O. Pearce" Date: Thu, 24 Jun 2010 15:16:32 -0700 Subject: Make lib.Repository abstract and lib.FileRepository its implementation To support other storage models other than just the local filesystem, we split the Repository class into a nearly abstract interface and then create a concrete subclass called FileRepository with the file based IO implementation. We are using an abstract class for Repository rather than the much more generic interface, as implementers will want to inherit a large array of utility functions, such as resolve(String). Having these in a base class makes it easy to inherit them. This isn't the final home for lib.FileRepository. Future changes will rename it into storage.file.FileRepository, but to do that we need to also move a number of other related class, which we aren't quite ready to do. Change-Id: I1bd54ea0500337799a8e792874c272eb14d555f7 Signed-off-by: Shawn O. Pearce --- .../jgit/junit/LocalDiskRepositoryTestCase.java | 3 +- .../src/org/eclipse/jgit/pgm/Clone.java | 4 +- .../src/org/eclipse/jgit/pgm/Init.java | 4 +- .../src/org/eclipse/jgit/pgm/Main.java | 4 +- .../tst/org/eclipse/jgit/lib/RefUpdateTest.java | 2 +- .../jgit/lib/RepositorySetupWorkDirTest.java | 30 +- .../tst/org/eclipse/jgit/lib/T0003_Basic.java | 30 +- .../src/org/eclipse/jgit/lib/FileRepository.java | 492 +++++++++++++++++++++ .../src/org/eclipse/jgit/lib/RefDirectory.java | 4 +- .../src/org/eclipse/jgit/lib/Repository.java | 364 ++------------- .../src/org/eclipse/jgit/lib/RepositoryCache.java | 2 +- .../org/eclipse/jgit/transport/TransportLocal.java | 5 +- 12 files changed, 569 insertions(+), 375 deletions(-) create mode 100644 org.eclipse.jgit/src/org/eclipse/jgit/lib/FileRepository.java (limited to 'org.eclipse.jgit.pgm/src/org') 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..100b632ae3 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 @@ -63,6 +63,7 @@ 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.FileRepository; import org.eclipse.jgit.lib.PersonIdent; import org.eclipse.jgit.lib.Repository; import org.eclipse.jgit.lib.RepositoryCache; @@ -288,7 +289,7 @@ public abstract class LocalDiskRepositoryTestCase extends TestCase { 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); + Repository db = new FileRepository(gitdir); assertFalse(gitdir.exists()); db.create(); 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..260b8b8663 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 @@ -57,11 +57,11 @@ import org.eclipse.jgit.errors.NotSupportedException; import org.eclipse.jgit.errors.TransportException; import org.eclipse.jgit.lib.Commit; import org.eclipse.jgit.lib.Constants; +import org.eclipse.jgit.lib.FileRepository; 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; @@ -103,7 +103,7 @@ class Clone extends AbstractFetchCommand { if (gitdir == null) gitdir = new File(localName, Constants.DOT_GIT); - db = new Repository(gitdir); + db = new FileRepository(gitdir); db.create(); db.getConfig().setBoolean("core", null, "bare", false); db.getConfig().save(); 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..b2a9fbde04 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.lib.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..b0da55a430 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 @@ -60,7 +60,7 @@ 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.FileRepository; import org.eclipse.jgit.pgm.opt.CmdLineParser; import org.eclipse.jgit.pgm.opt.SubcommandHandler; import org.eclipse.jgit.util.CachedAuthenticator; @@ -212,7 +212,7 @@ public class Main { writer.flush(); System.exit(1); } - cmd.init(new Repository(gitdir, gitworktree, objectdir, altobjectdirs, indexfile), gitdir); + cmd.init(new FileRepository(gitdir, gitworktree, objectdir, altobjectdirs, indexfile), gitdir); } else { cmd.init(null, gitdir); } diff --git a/org.eclipse.jgit.test/tst/org/eclipse/jgit/lib/RefUpdateTest.java b/org.eclipse.jgit.test/tst/org/eclipse/jgit/lib/RefUpdateTest.java index 8a9bb52633..161bd3eff1 100644 --- a/org.eclipse.jgit.test/tst/org/eclipse/jgit/lib/RefUpdateTest.java +++ b/org.eclipse.jgit.test/tst/org/eclipse/jgit/lib/RefUpdateTest.java @@ -664,7 +664,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")); } diff --git a/org.eclipse.jgit.test/tst/org/eclipse/jgit/lib/RepositorySetupWorkDirTest.java b/org.eclipse.jgit.test/tst/org/eclipse/jgit/lib/RepositorySetupWorkDirTest.java index 6e5e0054b8..e3617305f1 100644 --- a/org.eclipse.jgit.test/tst/org/eclipse/jgit/lib/RepositorySetupWorkDirTest.java +++ b/org.eclipse.jgit.test/tst/org/eclipse/jgit/lib/RepositorySetupWorkDirTest.java @@ -57,12 +57,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 +71,14 @@ public class RepositorySetupWorkDirTest extends LocalDiskRepositoryTestCase { public void testWorkdirIsParentDir_CreateRepositoryFromDotGitGitDir() throws Exception { File gitDir = getFile("workdir", Constants.DOT_GIT); - Repository repo = new Repository(gitDir); + Repository repo = new FileRepository(gitDir); String workdir = repo.getWorkDir().getName(); assertEquals(workdir, "workdir"); } public void testNotBare_CreateRepositoryFromWorkDirOnly() throws Exception { File workdir = getFile("workdir", "repo"); - Repository repo = new Repository(null, workdir); + Repository repo = new FileRepository(null, workdir); assertFalse(repo.isBare()); assertWorkdirPath(repo, "workdir", "repo"); assertGitdirPath(repo, "workdir", "repo", Constants.DOT_GIT); @@ -87,7 +87,7 @@ public class RepositorySetupWorkDirTest extends LocalDiskRepositoryTestCase { public void testWorkdirIsDotGit_CreateRepositoryFromWorkDirOnly() throws Exception { File workdir = getFile("workdir", "repo"); - Repository repo = new Repository(null, workdir); + Repository repo = new FileRepository(null, workdir); assertGitdirPath(repo, "workdir", "repo", Constants.DOT_GIT); } @@ -96,7 +96,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); + Repository repo = new FileRepository(gitDir, null); assertFalse(repo.isBare()); assertWorkdirPath(repo, "workdir", "treeRoot"); assertGitdirPath(repo, "workdir", "repoWithConfig"); @@ -106,7 +106,7 @@ public class RepositorySetupWorkDirTest extends LocalDiskRepositoryTestCase { throws Exception { File gitDir = getFile("workdir", "repoWithConfig"); setBare(gitDir, true); - Repository repo = new Repository(gitDir, null); + Repository repo = new FileRepository(gitDir, null); assertTrue(repo.isBare()); } @@ -114,7 +114,7 @@ public class RepositorySetupWorkDirTest extends LocalDiskRepositoryTestCase { throws Exception { File gitDir = getFile("workdir", "repoWithBareConfigTrue", "child"); setBare(gitDir, false); - Repository repo = new Repository(gitDir, null); + Repository repo = new FileRepository(gitDir, null); assertWorkdirPath(repo, "workdir", "repoWithBareConfigTrue"); } @@ -122,7 +122,7 @@ public class RepositorySetupWorkDirTest extends LocalDiskRepositoryTestCase { throws Exception { File gitDir = getFile("workdir", "repoWithBareConfigFalse", "child"); setBare(gitDir, false); - Repository repo = new Repository(gitDir, null); + Repository repo = new FileRepository(gitDir, null); assertFalse(repo.isBare()); assertWorkdirPath(repo, "workdir", "repoWithBareConfigFalse"); assertGitdirPath(repo, "workdir", "repoWithBareConfigFalse", "child"); @@ -130,7 +130,7 @@ public class RepositorySetupWorkDirTest extends LocalDiskRepositoryTestCase { public void testNotBare_MakeBareUnbareBySetWorkdir() throws Exception { File gitDir = getFile("gitDir"); - Repository repo = new Repository(gitDir); + Repository repo = new FileRepository(gitDir); repo.setWorkDir(getFile("workingDir")); assertFalse(repo.isBare()); assertWorkdirPath(repo, "workingDir"); @@ -140,7 +140,7 @@ public class RepositorySetupWorkDirTest extends LocalDiskRepositoryTestCase { public void testExceptionThrown_BareRepoGetWorkDir() throws Exception { File gitDir = getFile("workdir"); try { - new Repository(gitDir).getWorkDir(); + new FileRepository(gitDir).getWorkDir(); fail("Expected IllegalStateException missing"); } catch (IllegalStateException e) { // expected @@ -150,7 +150,7 @@ public class RepositorySetupWorkDirTest extends LocalDiskRepositoryTestCase { public void testExceptionThrown_BareRepoGetIndex() throws Exception { File gitDir = getFile("workdir"); try { - new Repository(gitDir).getIndex(); + new FileRepository(gitDir).getIndex(); fail("Expected IllegalStateException missing"); } catch (IllegalStateException e) { // expected @@ -160,7 +160,7 @@ public class RepositorySetupWorkDirTest extends LocalDiskRepositoryTestCase { public void testExceptionThrown_BareRepoGetIndexFile() throws Exception { File gitDir = getFile("workdir"); try { - new Repository(gitDir).getIndexFile(); + new FileRepository(gitDir).getIndexFile(); fail("Expected Exception missing"); } catch (IllegalStateException e) { // expected @@ -177,14 +177,14 @@ public class RepositorySetupWorkDirTest extends LocalDiskRepositoryTestCase { } private void setBare(File gitDir, boolean bare) throws IOException { - Repository repo = new Repository(gitDir, null); + Repository repo = new FileRepository(gitDir, null); repo.getConfig().setBoolean(ConfigConstants.CONFIG_CORE_SECTION, null, ConfigConstants.CONFIG_KEY_BARE, bare); repo.getConfig().save(); } private void setWorkTree(File gitDir, File workTree) throws IOException { - Repository repo = new Repository(gitDir, null); + Repository repo = new FileRepository(gitDir, null); repo.getConfig() .setString(ConfigConstants.CONFIG_CORE_SECTION, null, ConfigConstants.CONFIG_KEY_WORKTREE, diff --git a/org.eclipse.jgit.test/tst/org/eclipse/jgit/lib/T0003_Basic.java b/org.eclipse.jgit.test/tst/org/eclipse/jgit/lib/T0003_Basic.java index d96857498d..b44b9038e9 100644 --- a/org.eclipse.jgit.test/tst/org/eclipse/jgit/lib/T0003_Basic.java +++ b/org.eclipse.jgit.test/tst/org/eclipse/jgit/lib/T0003_Basic.java @@ -80,7 +80,7 @@ public class T0003_Basic extends SampleDataRepositoryTestCase { public void test000_openRepoBadArgs() throws IOException { try { - new Repository(null, null); + new FileRepository(null, null); fail("Must pass either GIT_DIR or GIT_WORK_TREE"); } catch (IllegalArgumentException e) { assertEquals( @@ -97,12 +97,12 @@ 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); + Repository r = new FileRepository(theDir, null); assertEqualsPath(theDir, r.getDirectory()); assertEqualsPath(repo1Parent, r.getWorkDir()); assertEqualsPath(new File(theDir, "index"), r.getIndexFile()); @@ -117,12 +117,12 @@ 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()); + Repository r = new FileRepository(theDir, repo1Parent.getParentFile()); assertEqualsPath(theDir, r.getDirectory()); assertEqualsPath(repo1Parent.getParentFile(), r.getWorkDir()); assertEqualsPath(new File(theDir, "index"), r.getIndexFile()); @@ -137,12 +137,12 @@ 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); + Repository r = new FileRepository(null, repo1Parent); assertEqualsPath(theDir, r.getDirectory()); assertEqualsPath(repo1Parent, r.getWorkDir()); assertEqualsPath(new File(theDir, "index"), r.getIndexFile()); @@ -159,7 +159,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)); + Repository repo1initial = new FileRepository(new File(repo1Parent, Constants.DOT_GIT)); repo1initial.create(); repo1initial.getConfig().setString("core", null, "worktree", workdir.getAbsolutePath()); @@ -167,7 +167,7 @@ public class T0003_Basic extends SampleDataRepositoryTestCase { repo1initial.close(); File theDir = new File(repo1Parent, Constants.DOT_GIT); - Repository r = new Repository(theDir, null); + Repository r = new FileRepository(theDir, null); assertEqualsPath(theDir, r.getDirectory()); assertEqualsPath(workdir, r.getWorkDir()); assertEqualsPath(new File(theDir, "index"), r.getIndexFile()); @@ -184,7 +184,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)); + Repository repo1initial = new FileRepository(new File(repo1Parent, Constants.DOT_GIT)); repo1initial.create(); repo1initial.getConfig() .setString("core", null, "worktree", "../../rw"); @@ -192,7 +192,7 @@ public class T0003_Basic extends SampleDataRepositoryTestCase { repo1initial.close(); File theDir = new File(repo1Parent, Constants.DOT_GIT); - Repository r = new Repository(theDir, null); + Repository r = new FileRepository(theDir, null); assertEqualsPath(theDir, r.getDirectory()); assertEqualsPath(workdir, r.getWorkDir()); assertEqualsPath(new File(theDir, "index"), r.getIndexFile()); @@ -211,12 +211,12 @@ public class T0003_Basic extends SampleDataRepositoryTestCase { 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)); + 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, + Repository r = new FileRepository(theDir, null, objDir, altObjDirs, indexFile); assertEqualsPath(theDir, r.getDirectory()); assertEqualsPath(theDir.getParentFile(), r.getWorkDir()); @@ -321,7 +321,7 @@ public class T0003_Basic extends SampleDataRepositoryTestCase { } public void test007_Open() throws IOException { - final Repository db2 = new Repository(db.getDirectory()); + final Repository db2 = new FileRepository(db.getDirectory()); assertEquals(db.getDirectory(), db2.getDirectory()); assertEquals(db.getObjectsDirectory(), db2.getObjectsDirectory()); assertNotSame(db.getConfig(), db2.getConfig()); @@ -337,7 +337,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); diff --git a/org.eclipse.jgit/src/org/eclipse/jgit/lib/FileRepository.java b/org.eclipse.jgit/src/org/eclipse/jgit/lib/FileRepository.java new file mode 100644 index 0000000000..f5b1bf8480 --- /dev/null +++ b/org.eclipse.jgit/src/org/eclipse/jgit/lib/FileRepository.java @@ -0,0 +1,492 @@ +/* + * Copyright (C) 2007, Dave Watson + * Copyright (C) 2008-2010, Google Inc. + * Copyright (C) 2006-2010, Robin Rosenberg + * Copyright (C) 2006-2008, Shawn O. Pearce + * 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.IOException; +import java.text.MessageFormat; +import java.util.Collection; +import java.util.HashSet; +import java.util.LinkedList; +import java.util.Set; + +import org.eclipse.jgit.JGitText; +import org.eclipse.jgit.errors.ConfigInvalidException; +import org.eclipse.jgit.util.FS; +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. + * + *
    + *
  • GIT_DIR + *
      + *
    • objects/ - objects
    • + *
    • refs/ - tags and heads
    • + *
    • config - configuration
    • + *
    • info/ - more configurations
    • + *
    + *
  • + *
+ *

+ * This class is thread-safe. + *

+ * 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. + * + * 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). + * @throws IOException + * the repository appears to already exist but cannot be + * accessed. + */ + public FileRepository(final File d) throws IOException { + this(d, null, null, null, null); // go figure it out + } + + /** + * 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 FileRepository(final File d, final File workTree) throws IOException { + this(d, workTree, null, null, null); // go figure it out + } + + /** + * 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 FileRepository(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. + * + * @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. + */ + public FileRepository(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); + repoConfig = new FileBasedConfig(userConfig, fs.resolve(gitDir, "config")); + + loadUserConfig(); + loadRepoConfig(); + + 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)); + } + } + } + + 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, 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(); + } + + /** + * @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; + } + + /** + * 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); + } + + /** + * Open object in all packs containing specified object. + * + * @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 + * @throws IOException + */ + public Collection openObjectInAllPacks( + final AnyObjectId objectId, final WindowCursor curs) + throws IOException { + Collection result = new LinkedList(); + openObjectInAllPacks(objectId, result, curs); + return result; + } + + /** + * Open object in all packs containing specified object. + * + * @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 resultLoaders, + final WindowCursor curs) throws IOException { + objectDatabase.openObjectInAllPacks(resultLoaders, curs, objectId); + } + + /** + * Objects known to exist but not expressed by {@link #getAllRefs()}. + *

+ * 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 getAdditionalHaves() { + HashSet r = new HashSet(); + for (ObjectDatabase d : getObjectDatabase().getAlternates()) { + if (d instanceof AlternateRepositoryDatabase) { + Repository repo; + + repo = ((AlternateRepositoryDatabase) d).getRepository(); + 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/RefDirectory.java b/org.eclipse.jgit/src/org/eclipse/jgit/lib/RefDirectory.java index b6fd10d018..0cbcf2b5cc 100644 --- a/org.eclipse.jgit/src/org/eclipse/jgit/lib/RefDirectory.java +++ b/org.eclipse.jgit/src/org/eclipse/jgit/lib/RefDirectory.java @@ -109,7 +109,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 +150,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(); 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 b4af15a60e..bd8fc1583f 100644 --- a/org.eclipse.jgit/src/org/eclipse/jgit/lib/Repository.java +++ b/org.eclipse.jgit/src/org/eclipse/jgit/lib/Repository.java @@ -49,7 +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; @@ -64,7 +63,6 @@ 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.IncorrectObjectTypeException; import org.eclipse.jgit.errors.RevisionSyntaxException; import org.eclipse.jgit.revwalk.RevBlob; @@ -74,257 +72,38 @@ import org.eclipse.jgit.revwalk.RevWalk; 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. - * - *

    - *
  • GIT_DIR - *
      - *
    • objects/ - objects
    • - *
    • refs/ - tags and heads
    • - *
    • config - configuration
    • - *
    • info/ - more configurations
    • - *
    - *
  • - *
+ * Represents a Git repository. *

- * 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). *

- * This implementation only handles a subtly undocumented subset of git features. - * + * This class is thread-safe. */ -public class Repository { +public abstract class Repository { private final AtomicInteger useCnt = new AtomicInteger(1); - private final File gitDir; + /** Metadata directory holding the repository's critical files. */ + protected File gitDir; - private final FS fs; - - private final FileBasedConfig userConfig; - - private final FileBasedConfig repoConfig; - - private final RefDatabase refs; - - private final ObjectDirectory objectDatabase; + /** File abstraction used to resolve paths. */ + protected FS fs; private GitIndex index; private final List listeners = new Vector(); // thread safe static private final List allListeners = new Vector(); // thread safe - private File workDir; + /** If not bare, the top level directory of the working files. */ + protected File workDir; - private File indexFile; + /** If not bare, the index file caching the working file states. */ + protected 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. - * - * @param d - * GIT_DIR (the location of the repository metadata). - * @throws IOException - * the repository appears to already exist but cannot be - * accessed. - */ - public Repository(final File d) throws IOException { - this(d, null, null, null, null); // go figure it out - } - - /** - * 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 - } - - /** - * 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. - * - * @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. - */ - 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); - repoConfig = new FileBasedConfig(userConfig, fs.resolve(gitDir, "config")); - - loadUserConfig(); - loadRepoConfig(); - - 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)); - } - } - } - - 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; - } + /** Initialize a new repository instance. */ + protected Repository() { + // Empty constructor, defined protected to require subclassing. } /** @@ -350,35 +129,7 @@ public class Repository { * @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, 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 @@ -390,42 +141,20 @@ 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 the reference database which stores the reference namespace. */ - public RefDatabase getRefDatabase() { - return refs; - } + public abstract RefDatabase getRefDatabase(); /** * @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; - } + public abstract FileBasedConfig getConfig(); /** * @return the used file system abstraction @@ -444,9 +173,7 @@ public class Repository { * @param objectId * @return suggested file name */ - public File toFile(final AnyObjectId objectId) { - return objectDatabase.fileFor(objectId); - } + public abstract File toFile(AnyObjectId objectId); /** * @param objectId @@ -501,13 +228,9 @@ public class Repository { * this object * @throws IOException */ - public Collection openObjectInAllPacks( - final AnyObjectId objectId, final WindowCursor curs) - throws IOException { - Collection result = new LinkedList(); - openObjectInAllPacks(objectId, result, curs); - return result; - } + public abstract Collection openObjectInAllPacks( + AnyObjectId objectId, WindowCursor curs) + throws IOException; /** * Open object in all packs containing specified object. @@ -521,11 +244,9 @@ public class Repository { * temporary working space associated with the calling thread. * @throws IOException */ - void openObjectInAllPacks(final AnyObjectId objectId, + abstract void openObjectInAllPacks(final AnyObjectId objectId, final Collection resultLoaders, - final WindowCursor curs) throws IOException { - objectDatabase.openObjectInAllPacks(resultLoaders, curs, objectId); - } + final WindowCursor curs) throws IOException; /** * @param id @@ -976,9 +697,7 @@ 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() + "]"; @@ -1040,18 +759,7 @@ public class Repository { * @return unmodifiable collection of other known objects. */ public Set getAdditionalHaves() { - HashSet r = new HashSet(); - for (ObjectDatabase d : objectDatabase.getAlternates()) { - if (d instanceof AlternateRepositoryDatabase) { - Repository repo; - - repo = ((AlternateRepositoryDatabase) d).getRepository(); - for (Ref ref : repo.getAllRefs().values()) - r.add(ref.getObjectId()); - r.addAll(repo.getAdditionalHaves()); - } - } - return r; + return Collections.emptySet(); } /** @@ -1411,11 +1119,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 @@ -1438,12 +1142,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 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..2bea8fecf1 100644 --- a/org.eclipse.jgit/src/org/eclipse/jgit/lib/RepositoryCache.java +++ b/org.eclipse.jgit/src/org/eclipse/jgit/lib/RepositoryCache.java @@ -313,7 +313,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/transport/TransportLocal.java b/org.eclipse.jgit/src/org/eclipse/jgit/transport/TransportLocal.java index 08fd8901d8..ae54a155a1 100644 --- a/org.eclipse.jgit/src/org/eclipse/jgit/transport/TransportLocal.java +++ b/org.eclipse.jgit/src/org/eclipse/jgit/transport/TransportLocal.java @@ -60,6 +60,7 @@ import org.eclipse.jgit.JGitText; import org.eclipse.jgit.errors.NotSupportedException; import org.eclipse.jgit.errors.TransportException; import org.eclipse.jgit.lib.Constants; +import org.eclipse.jgit.lib.FileRepository; import org.eclipse.jgit.lib.Repository; import org.eclipse.jgit.util.FS; import org.eclipse.jgit.util.io.MessageWriter; @@ -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); } -- cgit v1.2.3 From 89d4a7377fc578524dd87289afc367287fd9e653 Mon Sep 17 00:00:00 2001 From: "Shawn O. Pearce" Date: Thu, 24 Jun 2010 15:45:51 -0700 Subject: Use FileRepository where we assume other file semantics When the surrounding code is already heavily based upon the assumption that we have a FileRepository (e.g. because it created that type of repository) keep the type around and use it directly. This permits us to continue to do things like save the configuration file. Change-Id: Ib783f0f6a11acd6aa305c16d61ccc368b46beecc Signed-off-by: Shawn O. Pearce --- .../eclipse/jgit/http/test/AdvertiseErrorTest.java | 5 +- .../eclipse/jgit/http/test/HookMessageTest.java | 5 +- .../eclipse/jgit/http/test/HttpClientTests.java | 17 ++-- .../jgit/http/test/SmartClientSmartServerTest.java | 9 +- .../eclipse/jgit/http/test/util/HttpTestCase.java | 6 +- .../jgit/junit/LocalDiskRepositoryTestCase.java | 8 +- .../src/org/eclipse/jgit/junit/TestRepository.java | 102 +++++++++++---------- .../src/org/eclipse/jgit/pgm/Clone.java | 17 ++-- .../jgit/lib/RepositorySetupWorkDirTest.java | 4 +- .../org/eclipse/jgit/lib/RepositoryTestCase.java | 2 +- .../tst/org/eclipse/jgit/lib/T0003_Basic.java | 6 +- .../org/eclipse/jgit/revwalk/RevWalkTestCase.java | 5 +- .../jgit/transport/ReceivePackRefFilterTest.java | 6 +- 13 files changed, 105 insertions(+), 87 deletions(-) (limited to 'org.eclipse.jgit.pgm/src/org') 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 d8268bf93f..0f6299cd51 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 @@ -60,6 +60,7 @@ import org.eclipse.jgit.http.test.util.HttpTestCase; import org.eclipse.jgit.junit.TestRepository; import org.eclipse.jgit.lib.Constants; import org.eclipse.jgit.lib.FileBasedConfig; +import org.eclipse.jgit.lib.FileRepository; import org.eclipse.jgit.lib.NullProgressMonitor; import org.eclipse.jgit.lib.ObjectId; import org.eclipse.jgit.lib.Repository; @@ -71,14 +72,14 @@ 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 src = createTestRepository(); final String srcName = src.getRepository().getDirectory().getName(); ServletContextHandler app = server.addContext("/git"); 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 b98171ebd2..a2292e6c0e 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 @@ -62,6 +62,7 @@ import org.eclipse.jgit.http.test.util.HttpTestCase; import org.eclipse.jgit.junit.TestRepository; import org.eclipse.jgit.lib.Constants; import org.eclipse.jgit.lib.FileBasedConfig; +import org.eclipse.jgit.lib.FileRepository; import org.eclipse.jgit.lib.NullProgressMonitor; import org.eclipse.jgit.lib.ObjectId; import org.eclipse.jgit.lib.Repository; @@ -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 src = createTestRepository(); final String srcName = src.getRepository().getDirectory().getName(); ServletContextHandler app = server.addContext("/git"); 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..daf169eb1c 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 @@ -62,6 +62,7 @@ import org.eclipse.jgit.http.test.util.AccessEvent; import org.eclipse.jgit.http.test.util.HttpTestCase; import org.eclipse.jgit.junit.TestRepository; import org.eclipse.jgit.lib.Constants; +import org.eclipse.jgit.lib.FileRepository; import org.eclipse.jgit.lib.Ref; import org.eclipse.jgit.lib.RefUpdate; import org.eclipse.jgit.lib.Repository; @@ -71,7 +72,7 @@ import org.eclipse.jgit.transport.Transport; import org.eclipse.jgit.transport.URIish; public class HttpClientTests extends HttpTestCase { - private TestRepository remoteRepository; + private TestRepository 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 74dd8af8e2..867af2bf67 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 @@ -77,6 +77,7 @@ import org.eclipse.jgit.junit.TestRepository; import org.eclipse.jgit.junit.TestRng; import org.eclipse.jgit.lib.Constants; import org.eclipse.jgit.lib.FileBasedConfig; +import org.eclipse.jgit.lib.FileRepository; import org.eclipse.jgit.lib.NullProgressMonitor; import org.eclipse.jgit.lib.ObjectId; import org.eclipse.jgit.lib.Ref; @@ -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 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 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; 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..a25ca56787 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 @@ -57,6 +57,7 @@ import org.eclipse.jgit.junit.LocalDiskRepositoryTestCase; import org.eclipse.jgit.junit.TestRepository; import org.eclipse.jgit.lib.AnyObjectId; import org.eclipse.jgit.lib.Constants; +import org.eclipse.jgit.lib.FileRepository; import org.eclipse.jgit.lib.ObjectId; import org.eclipse.jgit.lib.Repository; import org.eclipse.jgit.revwalk.RevCommit; @@ -82,8 +83,9 @@ public abstract class HttpTestCase extends LocalDiskRepositoryTestCase { super.tearDown(); } - protected TestRepository createTestRepository() throws Exception { - return new TestRepository(createBareRepository()); + protected TestRepository createTestRepository() + throws IOException { + return new TestRepository(createBareRepository()); } protected URIish toURIish(String path) throws URISyntaxException { 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 100b632ae3..6920e91886 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 @@ -260,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 */); } @@ -271,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 */); } @@ -285,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 FileRepository(gitdir); + FileRepository db = new FileRepository(gitdir); assertFalse(gitdir.exists()); db.create(); 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..279762ac67 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,10 +73,10 @@ 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.FileRepository; 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; @@ -99,8 +99,13 @@ import org.eclipse.jgit.revwalk.RevWalk; 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 + * type of Repository the test data is stored on. + */ +public class TestRepository { private static final PersonIdent author; private static final PersonIdent committer; @@ -119,7 +124,7 @@ public class TestRepository { committer = new PersonIdent(cn, ce, now, tz); } - private final Repository db; + private final R db; private final RevWalk pool; @@ -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,9 +150,9 @@ 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); @@ -155,7 +160,7 @@ public class TestRepository { } /** @return the repository this helper class operates against. */ - public Repository getRepository() { + public R getRepository() { return db; } @@ -443,25 +448,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())); } } @@ -563,38 +570,40 @@ 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 all = new HashSet(); - for (Ref r : db.getAllRefs().values()) - all.add(r.getObjectId()); - pw.preparePack(all, Collections. emptySet()); - - final ObjectId name = pw.computeName(); - OutputStream out; + if (db.getObjectDatabase() instanceof ObjectDirectory) { + ObjectDirectory odb = (ObjectDirectory) db.getObjectDatabase(); + PackWriter pw = new PackWriter(db, NullProgressMonitor.INSTANCE); - final File pack = nameFor(odb, name, ".pack"); - out = new BufferedOutputStream(new FileOutputStream(pack)); - try { - pw.writePack(out); - } finally { - out.close(); - } - pack.setReadOnly(); + Set all = new HashSet(); + for (Ref r : db.getAllRefs().values()) + all.add(r.getObjectId()); + pw.preparePack(all, Collections. 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(); + + final File idx = nameFor(odb, name, ".idx"); + out = new BufferedOutputStream(new FileOutputStream(idx)); + try { + pw.writeIndex(out); + } finally { + out.close(); + } + idx.setReadOnly(); - 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 +618,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); 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 260b8b8663..60663438fc 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 @@ -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 FileRepository(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, diff --git a/org.eclipse.jgit.test/tst/org/eclipse/jgit/lib/RepositorySetupWorkDirTest.java b/org.eclipse.jgit.test/tst/org/eclipse/jgit/lib/RepositorySetupWorkDirTest.java index e3617305f1..070713d7b3 100644 --- a/org.eclipse.jgit.test/tst/org/eclipse/jgit/lib/RepositorySetupWorkDirTest.java +++ b/org.eclipse.jgit.test/tst/org/eclipse/jgit/lib/RepositorySetupWorkDirTest.java @@ -177,14 +177,14 @@ public class RepositorySetupWorkDirTest extends LocalDiskRepositoryTestCase { } private void setBare(File gitDir, boolean bare) throws IOException { - Repository repo = new FileRepository(gitDir, null); + FileRepository repo = new FileRepository(gitDir, null); repo.getConfig().setBoolean(ConfigConstants.CONFIG_CORE_SECTION, null, ConfigConstants.CONFIG_KEY_BARE, bare); repo.getConfig().save(); } private void setWorkTree(File gitDir, File workTree) throws IOException { - Repository repo = new FileRepository(gitDir, null); + FileRepository repo = new FileRepository(gitDir, null); repo.getConfig() .setString(ConfigConstants.CONFIG_CORE_SECTION, null, ConfigConstants.CONFIG_KEY_WORKTREE, 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..64df65b4c2 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 @@ -102,7 +102,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; diff --git a/org.eclipse.jgit.test/tst/org/eclipse/jgit/lib/T0003_Basic.java b/org.eclipse.jgit.test/tst/org/eclipse/jgit/lib/T0003_Basic.java index b44b9038e9..4e87fd624c 100644 --- a/org.eclipse.jgit.test/tst/org/eclipse/jgit/lib/T0003_Basic.java +++ b/org.eclipse.jgit.test/tst/org/eclipse/jgit/lib/T0003_Basic.java @@ -159,7 +159,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 FileRepository(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()); @@ -184,7 +184,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 FileRepository(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"); @@ -321,7 +321,7 @@ public class T0003_Basic extends SampleDataRepositoryTestCase { } public void test007_Open() throws IOException { - final Repository db2 = new FileRepository(db.getDirectory()); + final FileRepository db2 = new FileRepository(db.getDirectory()); assertEquals(db.getDirectory(), db2.getDirectory()); assertEquals(db.getObjectsDirectory(), db2.getObjectsDirectory()); assertNotSame(db.getConfig(), db2.getConfig()); 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 util; protected RevWalk rw; @Override public void setUp() throws Exception { super.setUp(); - util = new TestRepository(db, createRevWalk()); + util = new TestRepository(db, createRevWalk()); rw = util.getRevWalk(); } 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..b87cc7a99c 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 @@ -292,7 +292,7 @@ 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 s = new TestRepository(src); RevCommit N = s.commit().parent(B).add("q", s.blob("b")).create(); // But don't include it in the pack. @@ -333,7 +333,7 @@ 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 s = new TestRepository(src); RevBlob n = s.blob("n"); RevCommit N = s.commit().parent(B).add("q", n).create(); @@ -373,7 +373,7 @@ public class ReceivePackRefFilterTest extends LocalDiskRepositoryTestCase { } public void testUsingUnknownTreeFails() throws Exception { - TestRepository s = new TestRepository(src); + TestRepository s = new TestRepository(src); RevCommit N = s.commit().parent(B).add("q", s.blob("a")).create(); RevTree t = s.parseBody(N).getTree(); -- cgit v1.2.3 From 5cfc29b491eece0ceb71e653763b6cc771e7bdfe Mon Sep 17 00:00:00 2001 From: "Shawn O. Pearce" Date: Fri, 18 Jun 2010 23:02:57 -0700 Subject: Replace WindowCache with ObjectReader The WindowCache is an implementation detail of PackFile and how its used by ObjectDirectory. Lets start to hide it and replace the public API with a more generic concept, ObjectReader. Because PackedObjectLoader is also considered a private detail of PackFile, we have to make PackWriter temporarily dependent upon the WindowCursor and thus FileRepository and ObjectDirectory in order to just start the refactoring. In later changes we will clean up the APIs more, exposing sufficient support to PackWriter without needing the file specific implementation details. Change-Id: I676be12b57f3534f1285854ee5de1aa483895398 Signed-off-by: Shawn O. Pearce --- .../src/org/eclipse/jgit/iplog/IpLogGenerator.java | 7 +- .../jgit/pgm/opt/AbstractTreeIteratorHandler.java | 4 +- .../tst/org/eclipse/jgit/lib/T0004_PackReader.java | 2 +- .../jgit/transport/ReceivePackRefFilterTest.java | 2 +- .../org/eclipse/jgit/dircache/DirCacheBuilder.java | 4 +- .../eclipse/jgit/lib/CachedObjectDirectory.java | 9 +- .../org/eclipse/jgit/lib/FileObjectDatabase.java | 14 ++- .../src/org/eclipse/jgit/lib/FileRepository.java | 39 ------- .../src/org/eclipse/jgit/lib/ObjectDatabase.java | 66 +++++++----- .../src/org/eclipse/jgit/lib/ObjectDirectory.java | 4 +- .../src/org/eclipse/jgit/lib/ObjectReader.java | 114 +++++++++++++++++++++ .../src/org/eclipse/jgit/lib/PackWriter.java | 7 +- .../src/org/eclipse/jgit/lib/Repository.java | 63 +++++------- .../src/org/eclipse/jgit/lib/WindowCursor.java | 41 ++++++-- .../src/org/eclipse/jgit/merge/Merger.java | 4 +- .../src/org/eclipse/jgit/revwalk/RevWalk.java | 6 +- .../src/org/eclipse/jgit/transport/IndexPack.java | 22 ++-- .../jgit/treewalk/AbstractTreeIterator.java | 4 +- .../eclipse/jgit/treewalk/CanonicalTreeParser.java | 14 +-- .../src/org/eclipse/jgit/treewalk/TreeWalk.java | 5 +- 20 files changed, 274 insertions(+), 157 deletions(-) create mode 100644 org.eclipse.jgit/src/org/eclipse/jgit/lib/ObjectReader.java (limited to 'org.eclipse.jgit.pgm/src/org') 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..f40c3896a0 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 @@ -86,7 +86,7 @@ import org.eclipse.jgit.lib.MutableObjectId; import org.eclipse.jgit.lib.ObjectLoader; import org.eclipse.jgit.lib.PersonIdent; 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.RevTree; import org.eclipse.jgit.revwalk.RevWalk; @@ -144,7 +144,7 @@ public class IpLogGenerator { private NameConflictTreeWalk tw; - private final WindowCursor curs = new WindowCursor(); + private ObjectReader curs; private final MutableObjectId idbuf = new MutableObjectId(); @@ -184,6 +184,7 @@ public class IpLogGenerator { throws IOException, ConfigInvalidException { try { db = repo; + curs = db.newObjectReader(); rw = new RevWalk(db); tw = new NameConflictTreeWalk(db); @@ -194,7 +195,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; 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..d7b98c5098 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,7 +121,7 @@ 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); } catch (MissingObjectException e) { diff --git a/org.eclipse.jgit.test/tst/org/eclipse/jgit/lib/T0004_PackReader.java b/org.eclipse.jgit.test/tst/org/eclipse/jgit/lib/T0004_PackReader.java index 336bba22ce..54816f6f5e 100644 --- a/org.eclipse.jgit.test/tst/org/eclipse/jgit/lib/T0004_PackReader.java +++ b/org.eclipse.jgit.test/tst/org/eclipse/jgit/lib/T0004_PackReader.java @@ -63,7 +63,7 @@ public class T0004_PackReader extends SampleDataRepositoryTestCase { 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()); 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 b87cc7a99c..03726cba09 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 @@ -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. 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..adb8a8d77f 100644 --- a/org.eclipse.jgit/src/org/eclipse/jgit/dircache/DirCacheBuilder.java +++ b/org.eclipse.jgit/src/org/eclipse/jgit/dircache/DirCacheBuilder.java @@ -51,7 +51,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; @@ -166,7 +166,7 @@ public class DirCacheBuilder extends BaseDirCacheEditor { final Repository db, final AnyObjectId tree) throws IOException { final TreeWalk tw = new TreeWalk(db); tw.reset(); - final WindowCursor curs = new WindowCursor(); + final ObjectReader curs = db.newObjectReader(); try { tw.addTree(new CanonicalTreeParser(pathPrefix, db, tree .toObjectId(), curs)); diff --git a/org.eclipse.jgit/src/org/eclipse/jgit/lib/CachedObjectDirectory.java b/org.eclipse.jgit/src/org/eclipse/jgit/lib/CachedObjectDirectory.java index 37a96d74f2..351d6817fc 100644 --- a/org.eclipse.jgit/src/org/eclipse/jgit/lib/CachedObjectDirectory.java +++ b/org.eclipse.jgit/src/org/eclipse/jgit/lib/CachedObjectDirectory.java @@ -46,7 +46,6 @@ package org.eclipse.jgit.lib; import java.io.File; import java.io.IOException; -import java.util.Collection; /** * The cached instance of an {@link ObjectDirectory}. @@ -116,12 +115,6 @@ class CachedObjectDirectory extends FileObjectDatabase { return this; } - @Override - void openObjectInAllPacks(Collection out, - WindowCursor curs, AnyObjectId objectId) throws IOException { - wrapped.openObjectInAllPacks(out, curs, objectId); - } - @Override File getDirectory() { return wrapped.getDirectory(); @@ -157,7 +150,7 @@ class CachedObjectDirectory extends FileObjectDatabase { } @Override - public ObjectLoader openObject(final WindowCursor curs, + ObjectLoader openObject(final WindowCursor curs, final AnyObjectId objectId) throws IOException { return openObjectImpl1(curs, objectId); } diff --git a/org.eclipse.jgit/src/org/eclipse/jgit/lib/FileObjectDatabase.java b/org.eclipse.jgit/src/org/eclipse/jgit/lib/FileObjectDatabase.java index a7bf60370c..36e808c23b 100644 --- a/org.eclipse.jgit/src/org/eclipse/jgit/lib/FileObjectDatabase.java +++ b/org.eclipse.jgit/src/org/eclipse/jgit/lib/FileObjectDatabase.java @@ -45,8 +45,14 @@ package org.eclipse.jgit.lib; import java.io.File; import java.io.IOException; +import java.util.Collection; abstract class FileObjectDatabase extends ObjectDatabase { + @Override + public ObjectReader newReader() { + return new WindowCursor(this); + } + /** * Does the requested object exist in this database? *

@@ -98,8 +104,8 @@ abstract class FileObjectDatabase extends ObjectDatabase { * object, or null if the object does not exist. * @throws IOException */ - public ObjectLoader openObject(final WindowCursor curs, - final AnyObjectId objectId) throws IOException { + ObjectLoader openObject(final WindowCursor curs, final AnyObjectId objectId) + throws IOException { ObjectLoader ldr; ldr = openObjectImpl1(curs, objectId); @@ -154,6 +160,10 @@ abstract class FileObjectDatabase extends ObjectDatabase { return null; } + void openObjectInAllPacks(Collection reuseLoaders, + WindowCursor windowCursor, AnyObjectId otp) throws IOException { + } + abstract File getDirectory(); abstract AlternateHandle[] myAlternates(); diff --git a/org.eclipse.jgit/src/org/eclipse/jgit/lib/FileRepository.java b/org.eclipse.jgit/src/org/eclipse/jgit/lib/FileRepository.java index 12248fb425..32f516f7d9 100644 --- a/org.eclipse.jgit/src/org/eclipse/jgit/lib/FileRepository.java +++ b/org.eclipse.jgit/src/org/eclipse/jgit/lib/FileRepository.java @@ -49,9 +49,7 @@ package org.eclipse.jgit.lib; import java.io.File; import java.io.IOException; import java.text.MessageFormat; -import java.util.Collection; import java.util.HashSet; -import java.util.LinkedList; import java.util.Set; import org.eclipse.jgit.JGitText; @@ -396,43 +394,6 @@ public class FileRepository extends Repository { return objectDatabase.fileFor(objectId); } - /** - * Open object in all packs containing specified object. - * - * @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 - * @throws IOException - */ - public Collection openObjectInAllPacks( - final AnyObjectId objectId, final WindowCursor curs) - throws IOException { - Collection result = new LinkedList(); - openObjectInAllPacks(objectId, result, curs); - return result; - } - - /** - * Open object in all packs containing specified object. - * - * @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 resultLoaders, - final WindowCursor curs) throws IOException { - objectDatabase.openObjectInAllPacks(resultLoaders, curs, objectId); - } - /** * Objects known to exist but not expressed by {@link #getAllRefs()}. *

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 c44a7ac6f2..c2d2beda9a 100644 --- a/org.eclipse.jgit/src/org/eclipse/jgit/lib/ObjectDatabase.java +++ b/org.eclipse.jgit/src/org/eclipse/jgit/lib/ObjectDatabase.java @@ -44,7 +44,8 @@ package org.eclipse.jgit.lib; import java.io.IOException; -import java.util.Collection; + +import org.eclipse.jgit.errors.MissingObjectException; /** * Abstraction of arbitrary object storage. @@ -89,6 +90,17 @@ public abstract class ObjectDatabase { */ public abstract ObjectInserter newInserter(); + /** + * Create a new {@code ObjectReader} to read existing objects. + *

+ * The returned reader is not itself thread-safe, but multiple concurrent + * reader instances created from the same {@code ObjectDatabase} must be + * thread-safe. + * + * @return reader the caller can use to load objects from this database. + */ + public abstract ObjectReader newReader(); + /** * Close any resources held by this database. */ @@ -96,42 +108,48 @@ public abstract class ObjectDatabase { /** * Does the requested object exist in this database? + *

+ * This is a one-shot call interface which may be faster than allocating a + * {@link #newReader()} to perform the lookup. * * @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 abstract boolean hasObject(AnyObjectId objectId); + public boolean hasObject(final AnyObjectId objectId) throws IOException { + final ObjectReader or = newReader(); + try { + return or.hasObject(objectId); + } finally { + or.release(); + } + } /** * Open an object from this database. + *

+ * 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 abstract ObjectLoader openObject(WindowCursor curs, - AnyObjectId objectId) throws IOException; - - /** - * Open the object from all packs containing it. - * - * @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 - */ - abstract void openObjectInAllPacks(final Collection out, - final WindowCursor curs, final AnyObjectId objectId) - throws IOException; + public ObjectLoader openObject(final AnyObjectId objectId) + throws IOException { + final ObjectReader or = newReader(); + try { + return or.openObject(objectId); + } finally { + or.release(); + } + } /** * Create a new cached database instance over this database. This instance might diff --git a/org.eclipse.jgit/src/org/eclipse/jgit/lib/ObjectDirectory.java b/org.eclipse.jgit/src/org/eclipse/jgit/lib/ObjectDirectory.java index c605df9b8a..810e48890a 100644 --- a/org.eclipse.jgit/src/org/eclipse/jgit/lib/ObjectDirectory.java +++ b/org.eclipse.jgit/src/org/eclipse/jgit/lib/ObjectDirectory.java @@ -283,7 +283,6 @@ public class ObjectDirectory extends FileObjectDatabase { } } - @Override void openObjectInAllPacks(final Collection out, final WindowCursor curs, final AnyObjectId objectId) throws IOException { @@ -308,6 +307,9 @@ public class ObjectDirectory extends FileObjectDatabase { } break SEARCH; } + + for (AlternateHandle h : myAlternates()) + h.db.openObjectInAllPacks(out, curs, objectId); } boolean hasObject2(final String objectName) { 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..c95130ce29 --- /dev/null +++ b/org.eclipse.jgit/src/org/eclipse/jgit/lib/ObjectReader.java @@ -0,0 +1,114 @@ +/* + * 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.MissingObjectException; + +/** Reads an {@link ObjectDatabase} for a single thread. */ +public abstract class ObjectReader { + /** Type hint indicating the caller doesn't know the type. */ + protected static final int OBJ_ANY = -1; + + /** + * 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 hasObject(AnyObjectId objectId) throws IOException { + try { + openObject(objectId); + 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 + */ + public ObjectLoader openObject(AnyObjectId objectId) + throws MissingObjectException, IOException { + return openObject(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 IOException + */ + public abstract ObjectLoader openObject(AnyObjectId objectId, int typeHint) + throws MissingObjectException, IOException; + + /** + * Release any resources used by this reader. + *

+ * 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/PackWriter.java b/org.eclipse.jgit/src/org/eclipse/jgit/lib/PackWriter.java index e8e5fc21e6..15ac17425e 100644 --- a/org.eclipse.jgit/src/org/eclipse/jgit/lib/PackWriter.java +++ b/org.eclipse.jgit/src/org/eclipse/jgit/lib/PackWriter.java @@ -179,7 +179,7 @@ public class PackWriter { private final byte[] buf = new byte[16384]; // 16 KB - private final WindowCursor windowCursor = new WindowCursor(); + private final WindowCursor windowCursor; private List sortedByName; @@ -236,6 +236,9 @@ public class PackWriter { public PackWriter(final Repository repo, final ProgressMonitor imonitor, final ProgressMonitor wmonitor) { this.db = repo; + windowCursor = new WindowCursor((ObjectDirectory) repo + .getObjectDatabase()); + initMonitor = imonitor == null ? NullProgressMonitor.INSTANCE : imonitor; writeMonitor = wmonitor == null ? NullProgressMonitor.INSTANCE : wmonitor; @@ -621,7 +624,7 @@ public class PackWriter { private void searchForReuse( final Collection reuseLoaders, final ObjectToPack otp) throws IOException { - db.openObjectInAllPacks(otp, reuseLoaders, windowCursor); + windowCursor.openObjectInAllPacks(otp, reuseLoaders); if (reuseDeltas) { selectDeltaReuseForObject(otp, reuseLoaders); } 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 334581415a..400cae43d4 100644 --- a/org.eclipse.jgit/src/org/eclipse/jgit/lib/Repository.java +++ b/org.eclipse.jgit/src/org/eclipse/jgit/lib/Repository.java @@ -50,7 +50,6 @@ import java.io.File; import java.io.FileNotFoundException; import java.io.IOException; import java.util.ArrayList; -import java.util.Collection; import java.util.Collections; import java.util.HashMap; import java.util.HashSet; @@ -64,6 +63,7 @@ import java.util.concurrent.atomic.AtomicInteger; import org.eclipse.jgit.JGitText; import org.eclipse.jgit.dircache.DirCache; import org.eclipse.jgit.errors.IncorrectObjectTypeException; +import org.eclipse.jgit.errors.MissingObjectException; import org.eclipse.jgit.errors.RevisionSyntaxException; import org.eclipse.jgit.revwalk.RevBlob; import org.eclipse.jgit.revwalk.RevCommit; @@ -153,6 +153,11 @@ public abstract class Repository { return getObjectDatabase().newInserter(); } + /** @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(); @@ -186,7 +191,12 @@ public abstract class Repository { * known shared repositories. */ public boolean hasObject(AnyObjectId objectId) { - return getObjectDatabase().hasObject(objectId); + try { + return getObjectDatabase().hasObject(objectId); + } catch (IOException e) { + // Legacy API, assume error means "no" + return false; + } } /** @@ -199,11 +209,11 @@ public abstract class Repository { */ public ObjectLoader openObject(final AnyObjectId id) throws IOException { - final WindowCursor wc = new WindowCursor(); try { - return openObject(wc, id); - } finally { - wc.release(); + return getObjectDatabase().openObject(id); + } catch (MissingObjectException notFound) { + // Legacy API, return null + return null; } } @@ -216,43 +226,18 @@ public abstract class Repository { * @return a {@link ObjectLoader} for accessing the data of the named * object, or null if the object does not exist. * @throws IOException + * @deprecated Use {code newObjectReader().open(id)}. */ - public ObjectLoader openObject(WindowCursor curs, AnyObjectId id) + @Deprecated + public ObjectLoader openObject(ObjectReader curs, AnyObjectId id) throws IOException { - return getObjectDatabase().openObject(curs, id); + try { + return curs.openObject(id); + } catch (MissingObjectException notFound) { + return null; + } } - /** - * Open object in all packs containing specified object. - * - * @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 - * @throws IOException - */ - public abstract Collection openObjectInAllPacks( - AnyObjectId objectId, WindowCursor curs) - throws IOException; - - /** - * Open object in all packs containing specified object. - * - * @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 - */ - abstract void openObjectInAllPacks(final AnyObjectId objectId, - final Collection resultLoaders, - final WindowCursor curs) throws IOException; - /** * @param id * SHA'1 of a blob diff --git a/org.eclipse.jgit/src/org/eclipse/jgit/lib/WindowCursor.java b/org.eclipse.jgit/src/org/eclipse/jgit/lib/WindowCursor.java index 968c92e5ce..afc7f7186a 100644 --- a/org.eclipse.jgit/src/org/eclipse/jgit/lib/WindowCursor.java +++ b/org.eclipse.jgit/src/org/eclipse/jgit/lib/WindowCursor.java @@ -45,11 +45,14 @@ package org.eclipse.jgit.lib; import java.io.IOException; +import java.util.Collection; import java.util.zip.DataFormatException; import java.util.zip.Inflater; +import org.eclipse.jgit.errors.MissingObjectException; + /** Active handle to a ByteWindow. */ -public final class WindowCursor { +final class WindowCursor extends ObjectReader { /** Temporary buffer large enough for at least one raw object id. */ final byte[] tempId = new byte[Constants.OBJECT_ID_LENGTH]; @@ -57,6 +60,32 @@ public final class WindowCursor { private ByteWindow window; + private final FileObjectDatabase db; + + WindowCursor(FileObjectDatabase db) { + this.db = db; + } + + public boolean hasObject(AnyObjectId objectId) throws IOException { + return db.hasObject(objectId); + } + + public ObjectLoader openObject(AnyObjectId objectId, int typeHint) + throws MissingObjectException, 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); + } + return ldr; + } + + void openObjectInAllPacks(AnyObjectId otp, + Collection reuseLoaders) throws IOException { + db.openObjectInAllPacks(reuseLoaders, this, otp); + } + /** * Copy bytes from the window to a caller supplied buffer. * @@ -168,14 +197,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/merge/Merger.java b/org.eclipse.jgit/src/org/eclipse/jgit/merge/Merger.java index 59b826438e..38a8b8eae5 100644 --- a/org.eclipse.jgit/src/org/eclipse/jgit/merge/Merger.java +++ b/org.eclipse.jgit/src/org/eclipse/jgit/merge/Merger.java @@ -53,7 +53,7 @@ import org.eclipse.jgit.lib.Constants; import org.eclipse.jgit.lib.ObjectId; 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; @@ -202,7 +202,7 @@ public abstract class Merger { */ protected AbstractTreeIterator openTree(final AnyObjectId treeId) throws IncorrectObjectTypeException, IOException { - final WindowCursor curs = new WindowCursor(); + final ObjectReader curs = db.newObjectReader(); try { return new CanonicalTreeParser(null, db, treeId, curs); } finally { 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 e42554811b..1224182960 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; @@ -159,7 +159,7 @@ public class RevWalk implements Iterable { final Repository db; - final WindowCursor curs; + final ObjectReader curs; final MutableObjectId idBuffer; @@ -193,7 +193,7 @@ public class RevWalk implements Iterable { */ public RevWalk(final Repository repo) { db = repo; - curs = new WindowCursor(); + curs = db.newObjectReader(); idBuffer = new MutableObjectId(); objects = new ObjectIdSubclassMap(); roots = new ArrayList(); 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 f6e04107f6..0cd6733695 100644 --- a/org.eclipse.jgit/src/org/eclipse/jgit/transport/IndexPack.java +++ b/org.eclipse.jgit/src/org/eclipse/jgit/transport/IndexPack.java @@ -79,7 +79,7 @@ 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.util.NB; /** Indexes Git pack files for local use. */ @@ -213,7 +213,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. @@ -232,7 +232,7 @@ public class IndexPack { objectDatabase = db.getObjectDatabase().newCachedDatabase(); in = src; inflater = InflaterCache.get(); - readCurs = new WindowCursor(); + readCurs = objectDatabase.newReader(); buf = new byte[BUFFER_SIZE]; objectData = new byte[BUFFER_SIZE]; objectDigest = Constants.newMessageDigest(); @@ -430,7 +430,13 @@ public class IndexPack { inflater = null; objectDatabase.close(); } - readCurs = WindowCursor.release(readCurs); + + try { + if (readCurs != null) + readCurs.release(); + } finally { + readCurs = null; + } progress.endTask(); if (packOut != null) @@ -847,12 +853,16 @@ public class IndexPack { } } - final ObjectLoader ldr = objectDatabase.openObject(readCurs, id); - if (ldr != null) { + try { + final ObjectLoader ldr = readCurs.openObject(id, type); final byte[] existingData = ldr.getCachedBytes(); if (ldr.getType() != type || !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/treewalk/AbstractTreeIterator.java b/org.eclipse.jgit/src/org/eclipse/jgit/treewalk/AbstractTreeIterator.java index 90cea0f1be..73357a4dfe 100644 --- a/org.eclipse.jgit/src/org/eclipse/jgit/treewalk/AbstractTreeIterator.java +++ b/org.eclipse.jgit/src/org/eclipse/jgit/treewalk/AbstractTreeIterator.java @@ -56,7 +56,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.treewalk.filter.TreeFilter; /** @@ -474,7 +474,7 @@ public abstract class AbstractTreeIterator { * a loose object or pack file could not be read. */ public AbstractTreeIterator createSubtreeIterator(final Repository repo, - final MutableObjectId idBuffer, final WindowCursor curs) + final MutableObjectId idBuffer, final ObjectReader curs) throws IncorrectObjectTypeException, IOException { return createSubtreeIterator(repo); } 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..b7f980ccaa 100644 --- a/org.eclipse.jgit/src/org/eclipse/jgit/treewalk/CanonicalTreeParser.java +++ b/org.eclipse.jgit/src/org/eclipse/jgit/treewalk/CanonicalTreeParser.java @@ -56,7 +56,7 @@ 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 { @@ -102,7 +102,7 @@ public class CanonicalTreeParser extends AbstractTreeIterator { * a loose object or pack file could not be read. */ public CanonicalTreeParser(final byte[] prefix, final Repository repo, - final AnyObjectId treeId, final WindowCursor curs) + final AnyObjectId treeId, final ObjectReader curs) throws IncorrectObjectTypeException, IOException { super(prefix); reset(repo, treeId, curs); @@ -148,7 +148,7 @@ public class CanonicalTreeParser extends AbstractTreeIterator { * a loose object or pack file could not be read. */ public CanonicalTreeParser resetRoot(final Repository repo, - final AnyObjectId id, final WindowCursor curs) + final AnyObjectId id, final ObjectReader curs) throws IncorrectObjectTypeException, IOException { CanonicalTreeParser p = this; while (p.parent != null) @@ -197,7 +197,7 @@ public class CanonicalTreeParser extends AbstractTreeIterator { * a loose object or pack file could not be read. */ public void reset(final Repository repo, final AnyObjectId id, - final WindowCursor curs) + final ObjectReader curs) throws IncorrectObjectTypeException, IOException { final ObjectLoader ldr = repo.openObject(curs, id); if (ldr == null) { @@ -214,7 +214,7 @@ public class CanonicalTreeParser extends AbstractTreeIterator { @Override public CanonicalTreeParser createSubtreeIterator(final Repository repo, - final MutableObjectId idBuffer, final WindowCursor curs) + final MutableObjectId idBuffer, final ObjectReader curs) throws IncorrectObjectTypeException, IOException { idBuffer.fromRaw(idBuffer(), idOffset()); if (!FileMode.TREE.equals(mode)) { @@ -242,7 +242,7 @@ public class CanonicalTreeParser extends AbstractTreeIterator { * a loose object or pack file could not be read. */ public final CanonicalTreeParser createSubtreeIterator0( - final Repository repo, final AnyObjectId id, final WindowCursor curs) + final Repository repo, final AnyObjectId id, final ObjectReader curs) throws IOException { final CanonicalTreeParser p = new CanonicalTreeParser(this); p.reset(repo, id, curs); @@ -251,7 +251,7 @@ public class CanonicalTreeParser extends AbstractTreeIterator { public CanonicalTreeParser createSubtreeIterator(final Repository repo) throws IncorrectObjectTypeException, IOException { - final WindowCursor curs = new WindowCursor(); + final ObjectReader curs = repo.newObjectReader(); try { return createSubtreeIterator(repo, new MutableObjectId(), curs); } finally { 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 25ab78ef04..aefa79c3a8 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; @@ -155,7 +155,7 @@ public class TreeWalk { private final MutableObjectId idBuffer = new MutableObjectId(); - private final WindowCursor curs = new WindowCursor(); + private final ObjectReader curs; private TreeFilter filter; @@ -181,6 +181,7 @@ public class TreeWalk { */ public TreeWalk(final Repository repo) { db = repo; + curs = repo.newObjectReader(); filter = TreeFilter.ALL; trees = new AbstractTreeIterator[] { new EmptyTreeIterator() }; } -- cgit v1.2.3 From 532421d98925f23ddaa63c8d5f22be24879a6385 Mon Sep 17 00:00:00 2001 From: "Shawn O. Pearce" Date: Thu, 24 Jun 2010 09:07:53 -0700 Subject: Refactor repository construction to builder class The new FileRepositoryBuilder class helps applications to construct a properly configured FileRepository, with properties assumed based upon the standard Git rules for the local filesystem. To better support simple command line applications, environment variable handling and repository searching was moved into this builder class. The change gets rid of the ever-growing FileRepository constructor variants, and the multitude of java.io.File typed parameters, by using simple named setter methods. Change-Id: I17e8e0392ad1dbf6a90a7eb49a6d809388d27e4c Signed-off-by: Shawn O. Pearce --- .../src/org/eclipse/jgit/pgm/Main.java | 76 +-- .../jgit/lib/RepositorySetupWorkDirTest.java | 52 +- .../tst/org/eclipse/jgit/lib/T0003_Basic.java | 22 +- .../resources/org/eclipse/jgit/JGitText.properties | 1 + .../src/org/eclipse/jgit/JGitText.java | 1 + .../eclipse/jgit/lib/BaseRepositoryBuilder.java | 613 +++++++++++++++++++++ .../src/org/eclipse/jgit/lib/FileRepository.java | 180 +----- .../eclipse/jgit/lib/FileRepositoryBuilder.java | 88 +++ .../src/org/eclipse/jgit/lib/Repository.java | 32 +- .../org/eclipse/jgit/lib/RepositoryBuilder.java | 72 +++ 10 files changed, 859 insertions(+), 278 deletions(-) create mode 100644 org.eclipse.jgit/src/org/eclipse/jgit/lib/BaseRepositoryBuilder.java create mode 100644 org.eclipse.jgit/src/org/eclipse/jgit/lib/FileRepositoryBuilder.java create mode 100644 org.eclipse.jgit/src/org/eclipse/jgit/lib/RepositoryBuilder.java (limited to 'org.eclipse.jgit.pgm/src/org') 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 b0da55a430..2da210d7e8 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.FileRepository; +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 frb = new RepositoryBuilder() // + .setGitDir(gitdir) // + .readEnvironment() // + .findGitDir(); + if (frb.getGitDir() == null) { writer.println(CLIText.get().cantFindGitDirectory); writer.flush(); System.exit(1); } - cmd.init(new FileRepository(gitdir, gitworktree, objectdir, altobjectdirs, indexfile), gitdir); + + cmd.init(frb.build(), null); } else { cmd.init(null, gitdir); } @@ -224,27 +185,6 @@ public class Main { } } - private static File findGitDir() { - Set ceilingDirectories = new HashSet(); - 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.test/tst/org/eclipse/jgit/lib/RepositorySetupWorkDirTest.java b/org.eclipse.jgit.test/tst/org/eclipse/jgit/lib/RepositorySetupWorkDirTest.java index 070713d7b3..187d3ae9da 100644 --- a/org.eclipse.jgit.test/tst/org/eclipse/jgit/lib/RepositorySetupWorkDirTest.java +++ b/org.eclipse.jgit.test/tst/org/eclipse/jgit/lib/RepositorySetupWorkDirTest.java @@ -47,6 +47,7 @@ package org.eclipse.jgit.lib; import java.io.File; import java.io.IOException; +import org.eclipse.jgit.errors.ConfigInvalidException; import org.eclipse.jgit.junit.LocalDiskRepositoryTestCase; /** @@ -78,7 +79,7 @@ public class RepositorySetupWorkDirTest extends LocalDiskRepositoryTestCase { public void testNotBare_CreateRepositoryFromWorkDirOnly() throws Exception { File workdir = getFile("workdir", "repo"); - Repository repo = new FileRepository(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 +88,7 @@ public class RepositorySetupWorkDirTest extends LocalDiskRepositoryTestCase { public void testWorkdirIsDotGit_CreateRepositoryFromWorkDirOnly() throws Exception { File workdir = getFile("workdir", "repo"); - Repository repo = new FileRepository(null, workdir); + FileRepository repo = new FileRepositoryBuilder().setWorkTree(workdir).build(); assertGitdirPath(repo, "workdir", "repo", Constants.DOT_GIT); } @@ -96,7 +97,7 @@ public class RepositorySetupWorkDirTest extends LocalDiskRepositoryTestCase { File gitDir = getFile("workdir", "repoWithConfig"); File workTree = getFile("workdir", "treeRoot"); setWorkTree(gitDir, workTree); - Repository repo = new FileRepository(gitDir, null); + FileRepository repo = new FileRepositoryBuilder().setGitDir(gitDir).build(); assertFalse(repo.isBare()); assertWorkdirPath(repo, "workdir", "treeRoot"); assertGitdirPath(repo, "workdir", "repoWithConfig"); @@ -106,7 +107,7 @@ public class RepositorySetupWorkDirTest extends LocalDiskRepositoryTestCase { throws Exception { File gitDir = getFile("workdir", "repoWithConfig"); setBare(gitDir, true); - Repository repo = new FileRepository(gitDir, null); + FileRepository repo = new FileRepositoryBuilder().setGitDir(gitDir).build(); assertTrue(repo.isBare()); } @@ -114,7 +115,7 @@ public class RepositorySetupWorkDirTest extends LocalDiskRepositoryTestCase { throws Exception { File gitDir = getFile("workdir", "repoWithBareConfigTrue", "child"); setBare(gitDir, false); - Repository repo = new FileRepository(gitDir, null); + FileRepository repo = new FileRepositoryBuilder().setGitDir(gitDir).build(); assertWorkdirPath(repo, "workdir", "repoWithBareConfigTrue"); } @@ -122,21 +123,12 @@ public class RepositorySetupWorkDirTest extends LocalDiskRepositoryTestCase { throws Exception { File gitDir = getFile("workdir", "repoWithBareConfigFalse", "child"); setBare(gitDir, false); - Repository repo = new FileRepository(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 FileRepository(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 { @@ -176,20 +168,28 @@ public class RepositorySetupWorkDirTest extends LocalDiskRepositoryTestCase { return result; } - private void setBare(File gitDir, boolean bare) throws IOException { - FileRepository repo = new FileRepository(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 { - FileRepository repo = new FileRepository(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) diff --git a/org.eclipse.jgit.test/tst/org/eclipse/jgit/lib/T0003_Basic.java b/org.eclipse.jgit.test/tst/org/eclipse/jgit/lib/T0003_Basic.java index c770d605d0..8dd5ae2bd0 100644 --- a/org.eclipse.jgit.test/tst/org/eclipse/jgit/lib/T0003_Basic.java +++ b/org.eclipse.jgit.test/tst/org/eclipse/jgit/lib/T0003_Basic.java @@ -80,7 +80,7 @@ public class T0003_Basic extends SampleDataRepositoryTestCase { public void test000_openRepoBadArgs() throws IOException { try { - new FileRepository(null, null); + new FileRepositoryBuilder().build(); fail("Must pass either GIT_DIR or GIT_WORK_TREE"); } catch (IllegalArgumentException e) { assertEquals( @@ -102,7 +102,7 @@ public class T0003_Basic extends SampleDataRepositoryTestCase { repo1initial.close(); File theDir = new File(repo1Parent, Constants.DOT_GIT); - Repository r = new FileRepository(theDir, null); + FileRepository r = new FileRepositoryBuilder().setGitDir(theDir).build(); assertEqualsPath(theDir, r.getDirectory()); assertEqualsPath(repo1Parent, r.getWorkDir()); assertEqualsPath(new File(theDir, "index"), r.getIndexFile()); @@ -122,7 +122,8 @@ public class T0003_Basic extends SampleDataRepositoryTestCase { repo1initial.close(); File theDir = new File(repo1Parent, Constants.DOT_GIT); - Repository r = new FileRepository(theDir, repo1Parent.getParentFile()); + FileRepository r = new FileRepositoryBuilder().setGitDir(theDir) + .setWorkTree(repo1Parent.getParentFile()).build(); assertEqualsPath(theDir, r.getDirectory()); assertEqualsPath(repo1Parent.getParentFile(), r.getWorkDir()); assertEqualsPath(new File(theDir, "index"), r.getIndexFile()); @@ -142,7 +143,7 @@ public class T0003_Basic extends SampleDataRepositoryTestCase { repo1initial.close(); File theDir = new File(repo1Parent, Constants.DOT_GIT); - Repository r = new FileRepository(null, repo1Parent); + FileRepository r = new FileRepositoryBuilder().setWorkTree(repo1Parent).build(); assertEqualsPath(theDir, r.getDirectory()); assertEqualsPath(repo1Parent, r.getWorkDir()); assertEqualsPath(new File(theDir, "index"), r.getIndexFile()); @@ -167,7 +168,7 @@ public class T0003_Basic extends SampleDataRepositoryTestCase { repo1initial.close(); File theDir = new File(repo1Parent, Constants.DOT_GIT); - Repository r = new FileRepository(theDir, null); + FileRepository r = new FileRepositoryBuilder().setGitDir(theDir).build(); assertEqualsPath(theDir, r.getDirectory()); assertEqualsPath(workdir, r.getWorkDir()); assertEqualsPath(new File(theDir, "index"), r.getIndexFile()); @@ -192,7 +193,7 @@ public class T0003_Basic extends SampleDataRepositoryTestCase { repo1initial.close(); File theDir = new File(repo1Parent, Constants.DOT_GIT); - Repository r = new FileRepository(theDir, null); + FileRepository r = new FileRepositoryBuilder().setGitDir(theDir).build(); assertEqualsPath(theDir, r.getDirectory()); assertEqualsPath(workdir, r.getWorkDir()); assertEqualsPath(new File(theDir, "index"), r.getIndexFile()); @@ -210,14 +211,17 @@ 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() }; + File altObjDir = db.getObjectsDirectory(); 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 FileRepository(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(indexFile, r.getIndexFile()); diff --git a/org.eclipse.jgit/resources/org/eclipse/jgit/JGitText.properties b/org.eclipse.jgit/resources/org/eclipse/jgit/JGitText.properties index 91b67daf83..f6e4ea5b4b 100644 --- a/org.eclipse.jgit/resources/org/eclipse/jgit/JGitText.properties +++ b/org.eclipse.jgit/resources/org/eclipse/jgit/JGitText.properties @@ -298,6 +298,7 @@ remoteDoesNotSupportSmartHTTPPush=remote does not support smart HTTP push remoteHungUpUnexpectedly=remote hung up unexpectedly remoteNameCantBeNull=Remote name can't be null. repositoryAlreadyExists=Repository already exists: {0} +repositoryConfigFileInvalid=Repository config file {0} invalid {1} repositoryNotFound=repository not found: {0} requiredHashFunctionNotAvailable=Required hash function {0} not available. resolvingDeltas=Resolving deltas diff --git a/org.eclipse.jgit/src/org/eclipse/jgit/JGitText.java b/org.eclipse.jgit/src/org/eclipse/jgit/JGitText.java index 0c64b9eddf..377e9c119a 100644 --- a/org.eclipse.jgit/src/org/eclipse/jgit/JGitText.java +++ b/org.eclipse.jgit/src/org/eclipse/jgit/JGitText.java @@ -357,6 +357,7 @@ public class JGitText extends TranslationBundle { /***/ public String remoteHungUpUnexpectedly; /***/ public String remoteNameCantBeNull; /***/ public String repositoryAlreadyExists; + /***/ public String repositoryConfigFileInvalid; /***/ public String repositoryNotFound; /***/ public String requiredHashFunctionNotAvailable; /***/ public String resolvingDeltas; 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..f5dd7eec72 --- /dev/null +++ b/org.eclipse.jgit/src/org/eclipse/jgit/lib/BaseRepositoryBuilder.java @@ -0,0 +1,613 @@ +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.util.FS; +import org.eclipse.jgit.util.SystemReader; + +/** + * Base builder to customize repository construction. + *

+ * Repository implementations may subclass this builder in order to add custom + * repository detection methods. + * + * @param + * type of the repository builder. + * @param + * type of the repository that is constructed. + * @see RepositoryBuilder + * @see FileRepositoryBuilder + */ +public class BaseRepositoryBuilder { + private FS fs; + + private File gitDir; + + private File objectDirectory; + + private List alternateObjectDirectories; + + private File indexFile; + + private File workTree; + + /** Directories limiting the search for a Git repository. */ + private List 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. + *

+ * 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. + *

+ * 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(); + alternateObjectDirectories.add(other); + } + return self(); + } + + /** + * Add alternate object directories to the search list. + *

+ * 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 inList) { + if (inList != null) { + for (File path : inList) + addAlternateObjectDirectory(path); + } + return self(); + } + + /** + * Add alternate object directories to the search list. + *

+ * 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 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). + *

+ * 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. + *

+ * 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. + *

+ * 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. + *

+ * 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. + *

+ * 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(); + ceilingDirectories.add(root); + } + return self(); + } + + /** + * Add ceiling directories to the search list. + *

+ * 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 inList) { + if (inList != null) { + for (File path : inList) + addCeilingDirectory(path); + } + return self(); + } + + /** + * Add ceiling directories to the search list. + *

+ * 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. + *

+ * 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}. + *

+ * 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. + *

+ * 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}. + *

+ * 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. + *

+ * 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. + *

+ * 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().eitherGIT_DIRorGIT_WORK_TREEmustBePassed); + } + + /** + * 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. + *

+ * 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. + *

+ * 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/FileRepository.java b/org.eclipse.jgit/src/org/eclipse/jgit/lib/FileRepository.java index 86ae5fadae..f45ffdf149 100644 --- a/org.eclipse.jgit/src/org/eclipse/jgit/lib/FileRepository.java +++ b/org.eclipse.jgit/src/org/eclipse/jgit/lib/FileRepository.java @@ -56,7 +56,6 @@ import org.eclipse.jgit.JGitText; import org.eclipse.jgit.errors.ConfigInvalidException; import org.eclipse.jgit.lib.FileObjectDatabase.AlternateHandle; import org.eclipse.jgit.lib.FileObjectDatabase.AlternateRepository; -import org.eclipse.jgit.util.FS; import org.eclipse.jgit.util.SystemReader; /** @@ -95,176 +94,41 @@ public class FileRepository extends Repository { /** * 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). - * @throws IOException - * the repository appears to already exist but cannot be - * accessed. - */ - public FileRepository(final File d) throws IOException { - this(d, null, null, null, null); // go figure it out - } - - /** - * Construct a representation of a Git repository. + * rules by running {@link FileRepositoryBuilder}. This constructor is the + * same as saying: * - * The work tree, object directory, alternate object directories and index - * file locations are deduced from the given git directory and the default - * rules. + *

+	 * new FileRepositoryBuilder().setGitDir(gitDir).build()
+	 * 
* - * @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 FileRepository(final File d, final File workTree) throws IOException { - this(d, workTree, null, null, null); // go figure it out - } - - /** - * 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. + * @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 d, final File workTree, final File objectDir, - final File[] alternateObjectDir, final File indexFile) throws IOException { - this(d, workTree, objectDir, alternateObjectDir, indexFile, FS.DETECTED); + public FileRepository(final File gitDir) throws IOException { + this(new FileRepositoryBuilder().setGitDir(gitDir).setup()); } - /** - * 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. - * @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. - */ - public FileRepository(final File d, final File workTree, final File objectDir, - final File[] alternateObjectDir, final File indexFile, FS fs) - throws IOException { + FileRepository(final BaseRepositoryBuilder options) throws IOException { + super(options); - 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); - repoConfig = new FileBasedConfig(userConfig, fs.resolve(gitDir, "config")); + userConfig = SystemReader.getInstance().openUserConfig(getFS()); + repoConfig = new FileBasedConfig(userConfig, getFS().resolve(getDirectory(), "config")); loadUserConfig(); loadRepoConfig(); - 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(repoConfig, // - fs.resolve(objectDir, ""), // - alternateObjectDir, // - fs); - } else { - objectDatabase = new ObjectDirectory(repoConfig, // - fs.resolve(gitDir, "objects"), // - alternateObjectDir, // - fs); - } - - if (indexFile != null) - this.indexFile = indexFile; - else - this.indexFile = new File(gitDir, "index"); + objectDatabase = new ObjectDirectory(repoConfig, // + options.getObjectDirectory(), // + options.getAlternateObjectDirectories(), // + getFS()); if (objectDatabase.exists()) { final String repositoryFormatVersion = getConfig().getString( @@ -314,13 +178,13 @@ public class FileRepository extends Repository { final FileBasedConfig cfg = getConfig(); if (cfg.getFile().exists()) { throw new IllegalStateException(MessageFormat.format( - JGitText.get().repositoryAlreadyExists, gitDir)); + JGitText.get().repositoryAlreadyExists, getDirectory())); } - gitDir.mkdirs(); + getDirectory().mkdirs(); refs.create(); objectDatabase.create(); - new File(gitDir, "branches").mkdir(); + new File(getDirectory(), "branches").mkdir(); RefUpdate head = updateRef(Constants.HEAD); head.disableRefLog(); diff --git a/org.eclipse.jgit/src/org/eclipse/jgit/lib/FileRepositoryBuilder.java b/org.eclipse.jgit/src/org/eclipse/jgit/lib/FileRepositoryBuilder.java new file mode 100644 index 0000000000..c0220d979e --- /dev/null +++ b/org.eclipse.jgit/src/org/eclipse/jgit/lib/FileRepositoryBuilder.java @@ -0,0 +1,88 @@ +/* + * 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 java.io.IOException; + +/** + * Constructs a {@link FileRepository}. + *

+ * 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. + *

+ * Single repository applications trying to be compatible with other Git + * implementations are encouraged to use a model such as: + * + *

+ * 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()
+ * 
+ */ +public class FileRepositoryBuilder extends + BaseRepositoryBuilder { + /** + * Create a repository matching the configuration in this builder. + *

+ * 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 FileRepository build() throws IOException { + return new FileRepository(setup()); + } +} 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 09411a4531..939d923a74 100644 --- a/org.eclipse.jgit/src/org/eclipse/jgit/lib/Repository.java +++ b/org.eclipse.jgit/src/org/eclipse/jgit/lib/Repository.java @@ -85,10 +85,10 @@ public abstract class Repository { private final AtomicInteger useCnt = new AtomicInteger(1); /** Metadata directory holding the repository's critical files. */ - protected File gitDir; + private final File gitDir; /** File abstraction used to resolve paths. */ - protected FS fs; + private final FS fs; private GitIndex index; @@ -96,14 +96,22 @@ public abstract class Repository { static private final List allListeners = new Vector(); // thread safe /** If not bare, the top level directory of the working files. */ - protected File workDir; + private final File workDir; /** If not bare, the index file caching the working file states. */ - protected File indexFile; + private final File indexFile; - /** Initialize a new repository instance. */ - protected Repository() { - // Empty constructor, defined protected to require subclassing. + /** + * Initialize a new repository instance. + * + * @param options + * options to configure the repository. + */ + protected Repository(final BaseRepositoryBuilder options) { + gitDir = options.getGitDir(); + fs = options.getFS(); + workDir = options.getWorkTree(); + indexFile = options.getIndexFile(); } /** @@ -1018,16 +1026,6 @@ public abstract class Repository { 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. 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..4b0e1f6901 --- /dev/null +++ b/org.eclipse.jgit/src/org/eclipse/jgit/lib/RepositoryBuilder.java @@ -0,0 +1,72 @@ +/* + * 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; + +/** + * Base class to support constructing a {@link Repository}. + *

+ * 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. + *

+ * Single repository applications trying to be compatible with other Git + * implementations are encouraged to use a model such as: + * + *

+ * 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()
+ * 
+ * + * @see FileRepositoryBuilder + */ +public class RepositoryBuilder extends + BaseRepositoryBuilder { + // Empty implementation, everything is inherited. +} -- cgit v1.2.3 From 203bd6626767015dfb04d421c572b26a34e9cecf Mon Sep 17 00:00:00 2001 From: "Shawn O. Pearce" Date: Fri, 25 Jun 2010 17:53:03 -0700 Subject: Rename Repository getWorkDir to getWorkTree This better matches with the name used in the environment (GIT_WORK_TREE), in the configuration file (core.worktree), and in our builder object. Since we are already breaking a good chunk of other code related to repository access, and this fairly easy to fix in an application's code base, I'm not going to offer the wrapper getWorkDir() method. Change-Id: Ib698ba4bbc213c48114f342378cecfe377e37bb7 Signed-off-by: Shawn O. Pearce --- .../jgit/junit/LocalDiskRepositoryTestCase.java | 2 +- .../src/org/eclipse/jgit/pgm/Clone.java | 2 +- org.eclipse.jgit.pgm/src/org/eclipse/jgit/pgm/Rm.java | 2 +- .../src/org/eclipse/jgit/pgm/eclipse/Ipzilla.java | 2 +- .../org/eclipse/jgit/lib/T0007_GitIndexTest.java | 2 +- .../tst/org/eclipse/jgit/api/MergeCommandTest.java | 18 +++++++++--------- .../eclipse/jgit/lib/RepositorySetupWorkDirTest.java | 6 +++--- .../tst/org/eclipse/jgit/lib/RepositoryTestCase.java | 4 ++-- .../tst/org/eclipse/jgit/lib/T0003_Basic.java | 18 +++++++++--------- .../src/org/eclipse/jgit/api/MergeCommand.java | 2 +- .../src/org/eclipse/jgit/lib/IndexDiff.java | 2 +- .../src/org/eclipse/jgit/lib/Repository.java | 17 +++++++++-------- 12 files changed, 39 insertions(+), 38 deletions(-) (limited to 'org.eclipse.jgit.pgm/src/org') 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 6920e91886..f45e5f67a7 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 @@ -324,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.pgm/src/org/eclipse/jgit/pgm/Clone.java b/org.eclipse.jgit.pgm/src/org/eclipse/jgit/pgm/Clone.java index 60663438fc..cc59a6eb5a 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 @@ -183,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/Rm.java b/org.eclipse.jgit.pgm/src/org/eclipse/jgit/pgm/Rm.java index 1b8711dc9d..b0cc5248c9 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,7 +67,7 @@ class Rm extends TextBuiltin { @Override protected void run() throws Exception { - root = db.getWorkDir(); + root = db.getWorkTree(); final DirCache dirc = DirCache.lock(db); final DirCacheBuilder edit = dirc.builder(); 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.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/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/lib/RepositorySetupWorkDirTest.java b/org.eclipse.jgit.test/tst/org/eclipse/jgit/lib/RepositorySetupWorkDirTest.java index 187d3ae9da..52cb46bae1 100644 --- a/org.eclipse.jgit.test/tst/org/eclipse/jgit/lib/RepositorySetupWorkDirTest.java +++ b/org.eclipse.jgit.test/tst/org/eclipse/jgit/lib/RepositorySetupWorkDirTest.java @@ -73,7 +73,7 @@ public class RepositorySetupWorkDirTest extends LocalDiskRepositoryTestCase { throws Exception { File gitDir = getFile("workdir", Constants.DOT_GIT); Repository repo = new FileRepository(gitDir); - String workdir = repo.getWorkDir().getName(); + String workdir = repo.getWorkTree().getName(); assertEquals(workdir, "workdir"); } @@ -132,7 +132,7 @@ public class RepositorySetupWorkDirTest extends LocalDiskRepositoryTestCase { public void testExceptionThrown_BareRepoGetWorkDir() throws Exception { File gitDir = getFile("workdir"); try { - new FileRepository(gitDir).getWorkDir(); + new FileRepository(gitDir).getWorkTree(); fail("Expected IllegalStateException missing"); } catch (IllegalStateException e) { // expected @@ -202,7 +202,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/RepositoryTestCase.java b/org.eclipse.jgit.test/tst/org/eclipse/jgit/lib/RepositoryTestCase.java index 64df65b4c2..cc45722447 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 @@ -83,7 +83,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; } @@ -111,6 +111,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/T0003_Basic.java b/org.eclipse.jgit.test/tst/org/eclipse/jgit/lib/T0003_Basic.java index 8dd5ae2bd0..aab57aa594 100644 --- a/org.eclipse.jgit.test/tst/org/eclipse/jgit/lib/T0003_Basic.java +++ b/org.eclipse.jgit.test/tst/org/eclipse/jgit/lib/T0003_Basic.java @@ -104,7 +104,7 @@ public class T0003_Basic extends SampleDataRepositoryTestCase { File theDir = new File(repo1Parent, Constants.DOT_GIT); 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()); } @@ -125,7 +125,7 @@ public class T0003_Basic extends SampleDataRepositoryTestCase { 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()); } @@ -145,7 +145,7 @@ public class T0003_Basic extends SampleDataRepositoryTestCase { File theDir = new File(repo1Parent, Constants.DOT_GIT); 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()); } @@ -170,7 +170,7 @@ public class T0003_Basic extends SampleDataRepositoryTestCase { File theDir = new File(repo1Parent, Constants.DOT_GIT); 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()); } @@ -195,7 +195,7 @@ public class T0003_Basic extends SampleDataRepositoryTestCase { File theDir = new File(repo1Parent, Constants.DOT_GIT); 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()); } @@ -223,7 +223,7 @@ public class T0003_Basic extends SampleDataRepositoryTestCase { .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()); assertNotNull(r.mapCommit("6db9c2ebf75590eef973081736730a9ea169a0c4")); @@ -726,10 +726,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/src/org/eclipse/jgit/api/MergeCommand.java b/org.eclipse.jgit/src/org/eclipse/jgit/api/MergeCommand.java index 00a0309152..76a3bc4794 100644 --- a/org.eclipse.jgit/src/org/eclipse/jgit/api/MergeCommand.java +++ b/org.eclipse.jgit/src/org/eclipse/jgit/api/MergeCommand.java @@ -163,7 +163,7 @@ public class MergeCommand extends GitCommand { 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/lib/IndexDiff.java b/org.eclipse.jgit/src/org/eclipse/jgit/lib/IndexDiff.java index d655a97eb9..2e491af5a7 100644 --- a/org.eclipse.jgit/src/org/eclipse/jgit/lib/IndexDiff.java +++ b/org.eclipse.jgit/src/org/eclipse/jgit/lib/IndexDiff.java @@ -89,7 +89,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/Repository.java b/org.eclipse.jgit/src/org/eclipse/jgit/lib/Repository.java index 939d923a74..7b32d69a0e 100644 --- a/org.eclipse.jgit/src/org/eclipse/jgit/lib/Repository.java +++ b/org.eclipse.jgit/src/org/eclipse/jgit/lib/Repository.java @@ -96,7 +96,7 @@ public abstract class Repository { static private final List allListeners = new Vector(); // thread safe /** If not bare, the top level directory of the working files. */ - private final File workDir; + private final File workTree; /** If not bare, the index file caching the working file states. */ private final File indexFile; @@ -110,7 +110,7 @@ public abstract class Repository { protected Repository(final BaseRepositoryBuilder options) { gitDir = options.getGitDir(); fs = options.getFS(); - workDir = options.getWorkTree(); + workTree = options.getWorkTree(); indexFile = options.getIndexFile(); } @@ -886,7 +886,7 @@ public abstract class Repository { 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(getDirectory(), ".dotest-merge").exists()) return RepositoryState.REBASING_INTERACTIVE; @@ -1011,19 +1011,20 @@ public abstract class Repository { * @return the "bare"-ness of this Repository */ public boolean isBare() { - return workDir == null; + return workTree == null; } /** - * @return the workdir file, i.e. where the files are checked out + * @return the root directory of the working tree, where files are checked + * out for viewing and editing. * @throws IllegalStateException - * if the repository is "bare" + * if the repository is bare and has no working directory. */ - public File getWorkDir() throws IllegalStateException { + public File getWorkTree() throws IllegalStateException { if (isBare()) throw new IllegalStateException( JGitText.get().bareRepositoryNoWorkdirAndIndex); - return workDir; + return workTree; } /** -- cgit v1.2.3 From ffe0614d4db653cbcd48c19e9f599fd87cdcfaba Mon Sep 17 00:00:00 2001 From: "Shawn O. Pearce" Date: Fri, 25 Jun 2010 17:46:07 -0700 Subject: Allow Repository.getDirectory() to be null Some types of repositories might not be stored on local disk. For these, they will most likely return null for getDirectory() as the java.io.File type cannot describe where their storage is, its not in the host's filesystem. Document that getDirectory() can return null now, and update all current non-test callers in JGit that might run into problems on such repositories. For the most part, just act like its bare. Change-Id: I061236a691372a267fd7d41f0550650e165d2066 Signed-off-by: Shawn O. Pearce --- .../org/eclipse/jgit/http/server/TextFileServlet.java | 2 ++ .../jgit/http/server/resolver/FileResolver.java | 4 +++- .../src/org/eclipse/jgit/pgm/Glog.java | 8 +++++--- .../src/org/eclipse/jgit/api/CommitCommand.java | 13 +++++++------ .../src/org/eclipse/jgit/lib/Repository.java | 18 +++++++++++------- .../src/org/eclipse/jgit/lib/RepositoryCache.java | 10 ++++++++-- .../src/org/eclipse/jgit/transport/FetchProcess.java | 6 ++++-- .../org/eclipse/jgit/transport/RemoteRefUpdate.java | 2 +- 8 files changed, 41 insertions(+), 22 deletions(-) (limited to 'org.eclipse.jgit.pgm/src/org') 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/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.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/src/org/eclipse/jgit/api/CommitCommand.java b/org.eclipse.jgit/src/org/eclipse/jgit/api/CommitCommand.java index f12c94b33e..1cf2fe669c 100644 --- a/org.eclipse.jgit/src/org/eclipse/jgit/api/CommitCommand.java +++ b/org.eclipse.jgit/src/org/eclipse/jgit/api/CommitCommand.java @@ -171,17 +171,18 @@ public class CommitCommand extends GitCommand { Result rc = ru.update(); switch (rc) { case NEW: - case FAST_FORWARD: + case FAST_FORWARD: { setCallable(false); - if (state == RepositoryState.MERGING_RESOLVED) { + File meta = repo.getDirectory(); + if (state == RepositoryState.MERGING_RESOLVED + && meta != null) { // 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(); + new File(meta, Constants.MERGE_HEAD).delete(); + new File(meta, Constants.MERGE_MSG).delete(); } return revCommit; + } case REJECTED: case LOCK_FAILURE: throw new ConcurrentRefUpdateException( 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 2d99f65879..6b91481d3e 100644 --- a/org.eclipse.jgit/src/org/eclipse/jgit/lib/Repository.java +++ b/org.eclipse.jgit/src/org/eclipse/jgit/lib/Repository.java @@ -165,9 +165,7 @@ public abstract class Repository { */ 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; } @@ -712,7 +710,13 @@ public abstract class Repository { 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 + "]"; } /** @@ -908,7 +912,7 @@ public abstract class Repository { * @return an important state */ public RepositoryState getRepositoryState() { - if (isBare()) + if (isBare() || getDirectory() == null) return RepositoryState.BARE; // Pre Git-1.6 logic @@ -1096,7 +1100,7 @@ public abstract class Repository { * if the repository is "bare" */ public String readMergeCommitMsg() throws IOException { - if (isBare()) + if (isBare() || getDirectory() == null) throw new IllegalStateException( JGitText.get().bareRepositoryNoWorkdirAndIndex); @@ -1123,7 +1127,7 @@ public abstract class Repository { * if the repository is "bare" */ public List readMergeHeads() throws IOException { - if (isBare()) + if (isBare() || getDirectory() == null) throw new IllegalStateException( JGitText.get().bareRepositoryNoWorkdirAndIndex); 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 2bea8fecf1..39d734a32b 100644 --- a/org.eclipse.jgit/src/org/eclipse/jgit/lib/RepositoryCache.java +++ b/org.eclipse.jgit/src/org/eclipse/jgit/lib/RepositoryCache.java @@ -120,7 +120,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 +136,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. */ 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..27505bef65 100644 --- a/org.eclipse.jgit/src/org/eclipse/jgit/transport/FetchProcess.java +++ b/org.eclipse.jgit/src/org/eclipse/jgit/transport/FetchProcess.java @@ -271,8 +271,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()); 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") + "]"; } } -- cgit v1.2.3 From ad5238dc67aa5922c425e6bc829e1152c2e20439 Mon Sep 17 00:00:00 2001 From: "Shawn O. Pearce" Date: Sat, 26 Jun 2010 16:56:55 -0700 Subject: Move FileRepository to storage.file.FileRepository This move isolates all of the local file specific implementation code into a single package, where their package-private methods and support classes are properly hidden away from the rest of the core library. Because of the sheer number of files impacted, I have limited this change to only the renames and the updated imports. Change-Id: Icca4884e1a418f83f8b617d0c4c78b73d8a4bd17 Signed-off-by: Shawn O. Pearce --- org.eclipse.jgit.http.server/META-INF/MANIFEST.MF | 1 + .../eclipse/jgit/http/server/InfoPacksServlet.java | 4 +- .../eclipse/jgit/http/server/IsLocalFilter.java | 2 +- .../jgit/http/server/ObjectFileServlet.java | 2 +- org.eclipse.jgit.http.test/META-INF/MANIFEST.MF | 1 + .../eclipse/jgit/http/test/AdvertiseErrorTest.java | 4 +- .../eclipse/jgit/http/test/HookMessageTest.java | 4 +- .../eclipse/jgit/http/test/HttpClientTests.java | 2 +- .../jgit/http/test/SmartClientSmartServerTest.java | 6 +- .../eclipse/jgit/http/test/util/HttpTestCase.java | 2 +- org.eclipse.jgit.iplog/META-INF/MANIFEST.MF | 1 + .../src/org/eclipse/jgit/iplog/IpLogMeta.java | 4 +- org.eclipse.jgit.junit/META-INF/MANIFEST.MF | 1 + .../jgit/junit/LocalDiskRepositoryTestCase.java | 8 +- .../org/eclipse/jgit/junit/MockSystemReader.java | 2 +- .../src/org/eclipse/jgit/junit/TestRepository.java | 10 +- org.eclipse.jgit.pgm/META-INF/MANIFEST.MF | 1 + .../src/org/eclipse/jgit/pgm/Clone.java | 2 +- .../src/org/eclipse/jgit/pgm/Init.java | 2 +- .../eclipse/jgit/pgm/debug/RebuildCommitGraph.java | 2 +- .../src/org/eclipse/jgit/pgm/eclipse/Iplog.java | 2 +- org.eclipse.jgit.test/META-INF/MANIFEST.MF | 1 + .../org/eclipse/jgit/lib/ConcurrentRepackTest.java | 266 ----- .../org/eclipse/jgit/lib/PackIndexTestCase.java | 168 ---- .../tst/org/eclipse/jgit/lib/PackIndexV1Test.java | 82 -- .../tst/org/eclipse/jgit/lib/PackIndexV2Test.java | 93 -- .../org/eclipse/jgit/lib/PackReverseIndexTest.java | 121 --- .../tst/org/eclipse/jgit/lib/PackWriterTest.java | 530 ---------- .../tst/org/eclipse/jgit/lib/RefDirectoryTest.java | 1024 ------------------- .../tst/org/eclipse/jgit/lib/RefUpdateTest.java | 840 ---------------- .../tst/org/eclipse/jgit/lib/ReflogReaderTest.java | 198 ---- .../jgit/lib/RepositorySetupWorkDirTest.java | 208 ---- .../org/eclipse/jgit/lib/RepositoryTestCase.java | 1 + .../tst/org/eclipse/jgit/lib/T0003_Basic.java | 754 -------------- .../tst/org/eclipse/jgit/lib/T0004_PackReader.java | 86 -- .../org/eclipse/jgit/lib/WindowCacheGetTest.java | 149 --- .../jgit/lib/WindowCacheReconfigureTest.java | 125 --- .../tst/org/eclipse/jgit/lib/XInputStream.java | 88 -- .../jgit/storage/file/ConcurrentRepackTest.java | 275 ++++++ .../jgit/storage/file/PackIndexTestCase.java | 169 ++++ .../eclipse/jgit/storage/file/PackIndexV1Test.java | 83 ++ .../eclipse/jgit/storage/file/PackIndexV2Test.java | 94 ++ .../jgit/storage/file/PackReverseIndexTest.java | 122 +++ .../eclipse/jgit/storage/file/PackWriterTest.java | 534 ++++++++++ .../jgit/storage/file/RefDirectoryTest.java | 1028 ++++++++++++++++++++ .../eclipse/jgit/storage/file/RefUpdateTest.java | 853 ++++++++++++++++ .../jgit/storage/file/ReflogReaderTest.java | 201 ++++ .../storage/file/RepositorySetupWorkDirTest.java | 211 ++++ .../org/eclipse/jgit/storage/file/T0003_Basic.java | 769 +++++++++++++++ .../jgit/storage/file/T0004_PackReader.java | 90 ++ .../jgit/storage/file/WindowCacheGetTest.java | 153 +++ .../storage/file/WindowCacheReconfigureTest.java | 127 +++ .../eclipse/jgit/storage/file/XInputStream.java | 88 ++ .../org/eclipse/jgit/transport/IndexPackTest.java | 2 +- .../jgit/transport/ReceivePackRefFilterTest.java | 2 +- org.eclipse.jgit/META-INF/MANIFEST.MF | 1 + .../src/org/eclipse/jgit/dircache/DirCache.java | 2 +- .../eclipse/jgit/lib/BaseRepositoryBuilder.java | 3 + .../src/org/eclipse/jgit/lib/ByteArrayWindow.java | 101 -- .../src/org/eclipse/jgit/lib/ByteBufferWindow.java | 92 -- .../src/org/eclipse/jgit/lib/ByteWindow.java | 175 ---- .../eclipse/jgit/lib/CachedObjectDirectory.java | 184 ---- .../jgit/lib/DeltaOfsPackedObjectLoader.java | 82 -- .../eclipse/jgit/lib/DeltaPackedObjectLoader.java | 118 --- .../jgit/lib/DeltaRefPackedObjectLoader.java | 80 -- .../src/org/eclipse/jgit/lib/FileBasedConfig.java | 163 ---- .../org/eclipse/jgit/lib/FileObjectDatabase.java | 207 ---- .../src/org/eclipse/jgit/lib/FileRepository.java | 310 ------ .../eclipse/jgit/lib/FileRepositoryBuilder.java | 88 -- .../jgit/lib/LocalObjectRepresentation.java | 80 -- .../org/eclipse/jgit/lib/LocalObjectToPack.java | 66 -- .../src/org/eclipse/jgit/lib/LockFile.java | 448 --------- .../src/org/eclipse/jgit/lib/ObjectDirectory.java | 621 ------------ .../eclipse/jgit/lib/ObjectDirectoryInserter.java | 177 ---- .../src/org/eclipse/jgit/lib/PackFile.java | 695 ------------- .../src/org/eclipse/jgit/lib/PackIndex.java | 315 ------ .../src/org/eclipse/jgit/lib/PackIndexV1.java | 202 ---- .../src/org/eclipse/jgit/lib/PackIndexV2.java | 279 ------ .../src/org/eclipse/jgit/lib/PackIndexWriter.java | 275 ------ .../org/eclipse/jgit/lib/PackIndexWriterV1.java | 85 -- .../org/eclipse/jgit/lib/PackIndexWriterV2.java | 107 -- .../src/org/eclipse/jgit/lib/PackLock.java | 90 -- .../src/org/eclipse/jgit/lib/PackReverseIndex.java | 193 ---- .../src/org/eclipse/jgit/lib/PackWriter.java | 1 + .../org/eclipse/jgit/lib/PackedObjectLoader.java | 124 --- .../src/org/eclipse/jgit/lib/RefDirectory.java | 1006 ------------------- .../org/eclipse/jgit/lib/RefDirectoryRename.java | 225 ----- .../org/eclipse/jgit/lib/RefDirectoryUpdate.java | 156 --- .../src/org/eclipse/jgit/lib/RefWriter.java | 1 + .../src/org/eclipse/jgit/lib/ReflogReader.java | 184 ---- .../src/org/eclipse/jgit/lib/Repository.java | 1 + .../org/eclipse/jgit/lib/RepositoryBuilder.java | 2 + .../src/org/eclipse/jgit/lib/RepositoryCache.java | 1 + .../org/eclipse/jgit/lib/UnpackedObjectCache.java | 195 ---- .../org/eclipse/jgit/lib/UnpackedObjectLoader.java | 223 ----- .../eclipse/jgit/lib/WholePackedObjectLoader.java | 110 --- .../src/org/eclipse/jgit/lib/WindowCache.java | 624 ------------ .../org/eclipse/jgit/lib/WindowCacheConfig.java | 176 ---- .../src/org/eclipse/jgit/lib/WindowCursor.java | 213 ---- .../eclipse/jgit/storage/file/ByteArrayWindow.java | 101 ++ .../jgit/storage/file/ByteBufferWindow.java | 92 ++ .../org/eclipse/jgit/storage/file/ByteWindow.java | 175 ++++ .../jgit/storage/file/CachedObjectDirectory.java | 194 ++++ .../storage/file/DeltaOfsPackedObjectLoader.java | 84 ++ .../jgit/storage/file/DeltaPackedObjectLoader.java | 120 +++ .../storage/file/DeltaRefPackedObjectLoader.java | 82 ++ .../eclipse/jgit/storage/file/FileBasedConfig.java | 165 ++++ .../jgit/storage/file/FileObjectDatabase.java | 214 ++++ .../eclipse/jgit/storage/file/FileRepository.java | 327 +++++++ .../jgit/storage/file/FileRepositoryBuilder.java | 90 ++ .../storage/file/LocalObjectRepresentation.java | 83 ++ .../jgit/storage/file/LocalObjectToPack.java | 68 ++ .../org/eclipse/jgit/storage/file/LockFile.java | 450 +++++++++ .../eclipse/jgit/storage/file/ObjectDirectory.java | 630 ++++++++++++ .../jgit/storage/file/ObjectDirectoryInserter.java | 182 ++++ .../org/eclipse/jgit/storage/file/PackFile.java | 700 +++++++++++++ .../org/eclipse/jgit/storage/file/PackIndex.java | 318 ++++++ .../org/eclipse/jgit/storage/file/PackIndexV1.java | 205 ++++ .../org/eclipse/jgit/storage/file/PackIndexV2.java | 282 ++++++ .../eclipse/jgit/storage/file/PackIndexWriter.java | 277 ++++++ .../jgit/storage/file/PackIndexWriterV1.java | 85 ++ .../jgit/storage/file/PackIndexWriterV2.java | 107 ++ .../org/eclipse/jgit/storage/file/PackLock.java | 92 ++ .../jgit/storage/file/PackReverseIndex.java | 194 ++++ .../jgit/storage/file/PackedObjectLoader.java | 127 +++ .../eclipse/jgit/storage/file/RefDirectory.java | 1018 +++++++++++++++++++ .../jgit/storage/file/RefDirectoryRename.java | 229 +++++ .../jgit/storage/file/RefDirectoryUpdate.java | 160 +++ .../eclipse/jgit/storage/file/ReflogReader.java | 188 ++++ .../jgit/storage/file/UnpackedObjectCache.java | 195 ++++ .../jgit/storage/file/UnpackedObjectLoader.java | 227 +++++ .../jgit/storage/file/WholePackedObjectLoader.java | 112 +++ .../org/eclipse/jgit/storage/file/WindowCache.java | 624 ++++++++++++ .../jgit/storage/file/WindowCacheConfig.java | 178 ++++ .../eclipse/jgit/storage/file/WindowCursor.java | 222 +++++ .../jgit/transport/BasePackFetchConnection.java | 2 +- .../jgit/transport/BundleFetchConnection.java | 2 +- .../eclipse/jgit/transport/FetchConnection.java | 2 +- .../org/eclipse/jgit/transport/FetchProcess.java | 4 +- .../src/org/eclipse/jgit/transport/IndexPack.java | 4 +- .../org/eclipse/jgit/transport/ReceivePack.java | 2 +- .../org/eclipse/jgit/transport/TransportHttp.java | 2 +- .../org/eclipse/jgit/transport/TransportLocal.java | 2 +- .../jgit/transport/WalkFetchConnection.java | 8 +- .../jgit/transport/WalkRemoteObjectDatabase.java | 2 +- .../src/org/eclipse/jgit/util/SystemReader.java | 2 +- 146 files changed, 13459 insertions(+), 13249 deletions(-) delete mode 100644 org.eclipse.jgit.test/tst/org/eclipse/jgit/lib/ConcurrentRepackTest.java delete mode 100644 org.eclipse.jgit.test/tst/org/eclipse/jgit/lib/PackIndexTestCase.java delete mode 100644 org.eclipse.jgit.test/tst/org/eclipse/jgit/lib/PackIndexV1Test.java delete mode 100644 org.eclipse.jgit.test/tst/org/eclipse/jgit/lib/PackIndexV2Test.java delete mode 100644 org.eclipse.jgit.test/tst/org/eclipse/jgit/lib/PackReverseIndexTest.java delete mode 100644 org.eclipse.jgit.test/tst/org/eclipse/jgit/lib/PackWriterTest.java delete mode 100644 org.eclipse.jgit.test/tst/org/eclipse/jgit/lib/RefDirectoryTest.java delete mode 100644 org.eclipse.jgit.test/tst/org/eclipse/jgit/lib/RefUpdateTest.java delete mode 100644 org.eclipse.jgit.test/tst/org/eclipse/jgit/lib/ReflogReaderTest.java delete mode 100644 org.eclipse.jgit.test/tst/org/eclipse/jgit/lib/RepositorySetupWorkDirTest.java delete mode 100644 org.eclipse.jgit.test/tst/org/eclipse/jgit/lib/T0003_Basic.java delete mode 100644 org.eclipse.jgit.test/tst/org/eclipse/jgit/lib/T0004_PackReader.java delete mode 100644 org.eclipse.jgit.test/tst/org/eclipse/jgit/lib/WindowCacheGetTest.java delete mode 100644 org.eclipse.jgit.test/tst/org/eclipse/jgit/lib/WindowCacheReconfigureTest.java delete mode 100644 org.eclipse.jgit.test/tst/org/eclipse/jgit/lib/XInputStream.java create mode 100644 org.eclipse.jgit.test/tst/org/eclipse/jgit/storage/file/ConcurrentRepackTest.java create mode 100644 org.eclipse.jgit.test/tst/org/eclipse/jgit/storage/file/PackIndexTestCase.java create mode 100644 org.eclipse.jgit.test/tst/org/eclipse/jgit/storage/file/PackIndexV1Test.java create mode 100644 org.eclipse.jgit.test/tst/org/eclipse/jgit/storage/file/PackIndexV2Test.java create mode 100644 org.eclipse.jgit.test/tst/org/eclipse/jgit/storage/file/PackReverseIndexTest.java create mode 100644 org.eclipse.jgit.test/tst/org/eclipse/jgit/storage/file/PackWriterTest.java create mode 100644 org.eclipse.jgit.test/tst/org/eclipse/jgit/storage/file/RefDirectoryTest.java create mode 100644 org.eclipse.jgit.test/tst/org/eclipse/jgit/storage/file/RefUpdateTest.java create mode 100644 org.eclipse.jgit.test/tst/org/eclipse/jgit/storage/file/ReflogReaderTest.java create mode 100644 org.eclipse.jgit.test/tst/org/eclipse/jgit/storage/file/RepositorySetupWorkDirTest.java create mode 100644 org.eclipse.jgit.test/tst/org/eclipse/jgit/storage/file/T0003_Basic.java create mode 100644 org.eclipse.jgit.test/tst/org/eclipse/jgit/storage/file/T0004_PackReader.java create mode 100644 org.eclipse.jgit.test/tst/org/eclipse/jgit/storage/file/WindowCacheGetTest.java create mode 100644 org.eclipse.jgit.test/tst/org/eclipse/jgit/storage/file/WindowCacheReconfigureTest.java create mode 100644 org.eclipse.jgit.test/tst/org/eclipse/jgit/storage/file/XInputStream.java delete mode 100644 org.eclipse.jgit/src/org/eclipse/jgit/lib/ByteArrayWindow.java delete mode 100644 org.eclipse.jgit/src/org/eclipse/jgit/lib/ByteBufferWindow.java delete mode 100644 org.eclipse.jgit/src/org/eclipse/jgit/lib/ByteWindow.java delete mode 100644 org.eclipse.jgit/src/org/eclipse/jgit/lib/CachedObjectDirectory.java delete mode 100644 org.eclipse.jgit/src/org/eclipse/jgit/lib/DeltaOfsPackedObjectLoader.java delete mode 100644 org.eclipse.jgit/src/org/eclipse/jgit/lib/DeltaPackedObjectLoader.java delete mode 100644 org.eclipse.jgit/src/org/eclipse/jgit/lib/DeltaRefPackedObjectLoader.java delete mode 100644 org.eclipse.jgit/src/org/eclipse/jgit/lib/FileBasedConfig.java delete mode 100644 org.eclipse.jgit/src/org/eclipse/jgit/lib/FileObjectDatabase.java delete mode 100644 org.eclipse.jgit/src/org/eclipse/jgit/lib/FileRepository.java delete mode 100644 org.eclipse.jgit/src/org/eclipse/jgit/lib/FileRepositoryBuilder.java delete mode 100644 org.eclipse.jgit/src/org/eclipse/jgit/lib/LocalObjectRepresentation.java delete mode 100644 org.eclipse.jgit/src/org/eclipse/jgit/lib/LocalObjectToPack.java delete mode 100644 org.eclipse.jgit/src/org/eclipse/jgit/lib/LockFile.java delete mode 100644 org.eclipse.jgit/src/org/eclipse/jgit/lib/ObjectDirectory.java delete mode 100644 org.eclipse.jgit/src/org/eclipse/jgit/lib/ObjectDirectoryInserter.java delete mode 100644 org.eclipse.jgit/src/org/eclipse/jgit/lib/PackFile.java delete mode 100644 org.eclipse.jgit/src/org/eclipse/jgit/lib/PackIndex.java delete mode 100644 org.eclipse.jgit/src/org/eclipse/jgit/lib/PackIndexV1.java delete mode 100644 org.eclipse.jgit/src/org/eclipse/jgit/lib/PackIndexV2.java delete mode 100644 org.eclipse.jgit/src/org/eclipse/jgit/lib/PackIndexWriter.java delete mode 100644 org.eclipse.jgit/src/org/eclipse/jgit/lib/PackIndexWriterV1.java delete mode 100644 org.eclipse.jgit/src/org/eclipse/jgit/lib/PackIndexWriterV2.java delete mode 100644 org.eclipse.jgit/src/org/eclipse/jgit/lib/PackLock.java delete mode 100644 org.eclipse.jgit/src/org/eclipse/jgit/lib/PackReverseIndex.java delete mode 100644 org.eclipse.jgit/src/org/eclipse/jgit/lib/PackedObjectLoader.java delete mode 100644 org.eclipse.jgit/src/org/eclipse/jgit/lib/RefDirectory.java delete mode 100644 org.eclipse.jgit/src/org/eclipse/jgit/lib/RefDirectoryRename.java delete mode 100644 org.eclipse.jgit/src/org/eclipse/jgit/lib/RefDirectoryUpdate.java delete mode 100644 org.eclipse.jgit/src/org/eclipse/jgit/lib/ReflogReader.java delete mode 100644 org.eclipse.jgit/src/org/eclipse/jgit/lib/UnpackedObjectCache.java delete mode 100644 org.eclipse.jgit/src/org/eclipse/jgit/lib/UnpackedObjectLoader.java delete mode 100644 org.eclipse.jgit/src/org/eclipse/jgit/lib/WholePackedObjectLoader.java delete mode 100644 org.eclipse.jgit/src/org/eclipse/jgit/lib/WindowCache.java delete mode 100644 org.eclipse.jgit/src/org/eclipse/jgit/lib/WindowCacheConfig.java delete mode 100644 org.eclipse.jgit/src/org/eclipse/jgit/lib/WindowCursor.java create mode 100644 org.eclipse.jgit/src/org/eclipse/jgit/storage/file/ByteArrayWindow.java create mode 100644 org.eclipse.jgit/src/org/eclipse/jgit/storage/file/ByteBufferWindow.java create mode 100644 org.eclipse.jgit/src/org/eclipse/jgit/storage/file/ByteWindow.java create mode 100644 org.eclipse.jgit/src/org/eclipse/jgit/storage/file/CachedObjectDirectory.java create mode 100644 org.eclipse.jgit/src/org/eclipse/jgit/storage/file/DeltaOfsPackedObjectLoader.java create mode 100644 org.eclipse.jgit/src/org/eclipse/jgit/storage/file/DeltaPackedObjectLoader.java create mode 100644 org.eclipse.jgit/src/org/eclipse/jgit/storage/file/DeltaRefPackedObjectLoader.java create mode 100644 org.eclipse.jgit/src/org/eclipse/jgit/storage/file/FileBasedConfig.java create mode 100644 org.eclipse.jgit/src/org/eclipse/jgit/storage/file/FileObjectDatabase.java create mode 100644 org.eclipse.jgit/src/org/eclipse/jgit/storage/file/FileRepository.java create mode 100644 org.eclipse.jgit/src/org/eclipse/jgit/storage/file/FileRepositoryBuilder.java create mode 100644 org.eclipse.jgit/src/org/eclipse/jgit/storage/file/LocalObjectRepresentation.java create mode 100644 org.eclipse.jgit/src/org/eclipse/jgit/storage/file/LocalObjectToPack.java create mode 100644 org.eclipse.jgit/src/org/eclipse/jgit/storage/file/LockFile.java create mode 100644 org.eclipse.jgit/src/org/eclipse/jgit/storage/file/ObjectDirectory.java create mode 100644 org.eclipse.jgit/src/org/eclipse/jgit/storage/file/ObjectDirectoryInserter.java create mode 100644 org.eclipse.jgit/src/org/eclipse/jgit/storage/file/PackFile.java create mode 100644 org.eclipse.jgit/src/org/eclipse/jgit/storage/file/PackIndex.java create mode 100644 org.eclipse.jgit/src/org/eclipse/jgit/storage/file/PackIndexV1.java create mode 100644 org.eclipse.jgit/src/org/eclipse/jgit/storage/file/PackIndexV2.java create mode 100644 org.eclipse.jgit/src/org/eclipse/jgit/storage/file/PackIndexWriter.java create mode 100644 org.eclipse.jgit/src/org/eclipse/jgit/storage/file/PackIndexWriterV1.java create mode 100644 org.eclipse.jgit/src/org/eclipse/jgit/storage/file/PackIndexWriterV2.java create mode 100644 org.eclipse.jgit/src/org/eclipse/jgit/storage/file/PackLock.java create mode 100644 org.eclipse.jgit/src/org/eclipse/jgit/storage/file/PackReverseIndex.java create mode 100644 org.eclipse.jgit/src/org/eclipse/jgit/storage/file/PackedObjectLoader.java create mode 100644 org.eclipse.jgit/src/org/eclipse/jgit/storage/file/RefDirectory.java create mode 100644 org.eclipse.jgit/src/org/eclipse/jgit/storage/file/RefDirectoryRename.java create mode 100644 org.eclipse.jgit/src/org/eclipse/jgit/storage/file/RefDirectoryUpdate.java create mode 100644 org.eclipse.jgit/src/org/eclipse/jgit/storage/file/ReflogReader.java create mode 100644 org.eclipse.jgit/src/org/eclipse/jgit/storage/file/UnpackedObjectCache.java create mode 100644 org.eclipse.jgit/src/org/eclipse/jgit/storage/file/UnpackedObjectLoader.java create mode 100644 org.eclipse.jgit/src/org/eclipse/jgit/storage/file/WholePackedObjectLoader.java create mode 100644 org.eclipse.jgit/src/org/eclipse/jgit/storage/file/WindowCache.java create mode 100644 org.eclipse.jgit/src/org/eclipse/jgit/storage/file/WindowCacheConfig.java create mode 100644 org.eclipse.jgit/src/org/eclipse/jgit/storage/file/WindowCursor.java (limited to 'org.eclipse.jgit.pgm/src/org') 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/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.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 0f6299cd51..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 @@ -59,13 +59,13 @@ import org.eclipse.jgit.http.server.resolver.ServiceNotEnabledException; import org.eclipse.jgit.http.test.util.HttpTestCase; import org.eclipse.jgit.junit.TestRepository; import org.eclipse.jgit.lib.Constants; -import org.eclipse.jgit.lib.FileBasedConfig; -import org.eclipse.jgit.lib.FileRepository; import org.eclipse.jgit.lib.NullProgressMonitor; import org.eclipse.jgit.lib.ObjectId; import org.eclipse.jgit.lib.Repository; 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; 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 a2292e6c0e..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 @@ -61,13 +61,13 @@ import org.eclipse.jgit.http.test.util.AccessEvent; import org.eclipse.jgit.http.test.util.HttpTestCase; import org.eclipse.jgit.junit.TestRepository; import org.eclipse.jgit.lib.Constants; -import org.eclipse.jgit.lib.FileBasedConfig; -import org.eclipse.jgit.lib.FileRepository; import org.eclipse.jgit.lib.NullProgressMonitor; import org.eclipse.jgit.lib.ObjectId; import org.eclipse.jgit.lib.Repository; 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; 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 daf169eb1c..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 @@ -62,11 +62,11 @@ import org.eclipse.jgit.http.test.util.AccessEvent; import org.eclipse.jgit.http.test.util.HttpTestCase; import org.eclipse.jgit.junit.TestRepository; import org.eclipse.jgit.lib.Constants; -import org.eclipse.jgit.lib.FileRepository; 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; 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 867af2bf67..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 @@ -76,15 +76,15 @@ import org.eclipse.jgit.http.test.util.HttpTestCase; import org.eclipse.jgit.junit.TestRepository; import org.eclipse.jgit.junit.TestRng; import org.eclipse.jgit.lib.Constants; -import org.eclipse.jgit.lib.FileBasedConfig; -import org.eclipse.jgit.lib.FileRepository; 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.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; 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 a25ca56787..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 @@ -57,11 +57,11 @@ import org.eclipse.jgit.junit.LocalDiskRepositoryTestCase; import org.eclipse.jgit.junit.TestRepository; import org.eclipse.jgit.lib.AnyObjectId; import org.eclipse.jgit.lib.Constants; -import org.eclipse.jgit.lib.FileRepository; 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; 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/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..ab24b5aaf9 100644 --- a/org.eclipse.jgit.junit/META-INF/MANIFEST.MF +++ b/org.eclipse.jgit.junit/META-INF/MANIFEST.MF @@ -18,6 +18,7 @@ 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.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 f45e5f67a7..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,13 +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.FileRepository; 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; 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 dbf4eaf4ce..ec34e3c3a8 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,14 +73,10 @@ 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.FileRepository; -import org.eclipse.jgit.lib.LockFile; import org.eclipse.jgit.lib.NullProgressMonitor; import org.eclipse.jgit.lib.ObjectChecker; -import org.eclipse.jgit.lib.ObjectDirectory; import org.eclipse.jgit.lib.ObjectId; import org.eclipse.jgit.lib.ObjectInserter; -import org.eclipse.jgit.lib.PackFile; import org.eclipse.jgit.lib.PackWriter; import org.eclipse.jgit.lib.PersonIdent; import org.eclipse.jgit.lib.Ref; @@ -88,7 +84,6 @@ 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,6 +91,11 @@ 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.treewalk.TreeWalk; import org.eclipse.jgit.treewalk.filter.PathFilterGroup; diff --git a/org.eclipse.jgit.pgm/META-INF/MANIFEST.MF b/org.eclipse.jgit.pgm/META-INF/MANIFEST.MF index 8628896780..0a4376921a 100644 --- a/org.eclipse.jgit.pgm/META-INF/MANIFEST.MF +++ b/org.eclipse.jgit.pgm/META-INF/MANIFEST.MF @@ -17,6 +17,7 @@ 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.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/src/org/eclipse/jgit/pgm/Clone.java b/org.eclipse.jgit.pgm/src/org/eclipse/jgit/pgm/Clone.java index cc59a6eb5a..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 @@ -57,7 +57,6 @@ import org.eclipse.jgit.errors.NotSupportedException; import org.eclipse.jgit.errors.TransportException; import org.eclipse.jgit.lib.Commit; import org.eclipse.jgit.lib.Constants; -import org.eclipse.jgit.lib.FileRepository; import org.eclipse.jgit.lib.GitIndex; import org.eclipse.jgit.lib.Ref; import org.eclipse.jgit.lib.RefComparator; @@ -65,6 +64,7 @@ import org.eclipse.jgit.lib.RefUpdate; 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; 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 b2a9fbde04..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.FileRepository; +import org.eclipse.jgit.storage.file.FileRepository; @Command(common = true, usage = "usage_CreateAnEmptyGitRepository") class Init extends TextBuiltin { 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..8ba3e4b90a 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. 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.test/META-INF/MANIFEST.MF b/org.eclipse.jgit.test/META-INF/MANIFEST.MF index 3b82cf7d1d..b88ccf3467 100644 --- a/org.eclipse.jgit.test/META-INF/MANIFEST.MF +++ b/org.eclipse.jgit.test/META-INF/MANIFEST.MF @@ -28,6 +28,7 @@ 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.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/tst/org/eclipse/jgit/lib/ConcurrentRepackTest.java b/org.eclipse.jgit.test/tst/org/eclipse/jgit/lib/ConcurrentRepackTest.java deleted file mode 100644 index e3a62faf23..0000000000 --- a/org.eclipse.jgit.test/tst/org/eclipse/jgit/lib/ConcurrentRepackTest.java +++ /dev/null @@ -1,266 +0,0 @@ -/* - * Copyright (C) 2009-2010, Google Inc. - * Copyright (C) 2009, Robin Rosenberg - * 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.BufferedOutputStream; -import java.io.File; -import java.io.FileOutputStream; -import java.io.IOException; -import java.io.OutputStream; -import java.util.Arrays; - -import org.eclipse.jgit.errors.IncorrectObjectTypeException; -import org.eclipse.jgit.errors.MissingObjectException; -import org.eclipse.jgit.revwalk.RevObject; -import org.eclipse.jgit.revwalk.RevWalk; - -public class ConcurrentRepackTest extends RepositoryTestCase { - public void setUp() throws Exception { - WindowCacheConfig windowCacheConfig = new WindowCacheConfig(); - windowCacheConfig.setPackedGitOpenFiles(1); - WindowCache.reconfigure(windowCacheConfig); - super.setUp(); - } - - protected void tearDown() throws Exception { - super.tearDown(); - WindowCacheConfig windowCacheConfig = new WindowCacheConfig(); - WindowCache.reconfigure(windowCacheConfig); - } - - public void testObjectInNewPack() throws IncorrectObjectTypeException, - IOException { - // Create a new object in a new pack, and test that it is present. - // - final Repository eden = createBareRepository(); - final RevObject o1 = writeBlob(eden, "o1"); - pack(eden, o1); - assertEquals(o1.name(), parse(o1).name()); - } - - public void testObjectMovedToNewPack1() - throws IncorrectObjectTypeException, IOException { - // Create an object and pack it. Then remove that pack and put the - // object into a different pack file, with some other object. We - // still should be able to access the objects. - // - final Repository eden = createBareRepository(); - final RevObject o1 = writeBlob(eden, "o1"); - final File[] out1 = pack(eden, o1); - assertEquals(o1.name(), parse(o1).name()); - - final RevObject o2 = writeBlob(eden, "o2"); - pack(eden, o2, o1); - - // Force close, and then delete, the old pack. - // - whackCache(); - delete(out1); - - // Now here is the interesting thing. Will git figure the new - // object exists in the new pack, and not the old one. - // - assertEquals(o2.name(), parse(o2).name()); - assertEquals(o1.name(), parse(o1).name()); - } - - public void testObjectMovedWithinPack() - throws IncorrectObjectTypeException, IOException { - // Create an object and pack it. - // - final Repository eden = createBareRepository(); - final RevObject o1 = writeBlob(eden, "o1"); - final File[] out1 = pack(eden, o1); - assertEquals(o1.name(), parse(o1).name()); - - // Force close the old pack. - // - whackCache(); - - // Now overwrite the old pack in place. This method of creating a - // different pack under the same file name is partially broken. We - // should also have a different file name because the list of objects - // within the pack has been modified. - // - final RevObject o2 = writeBlob(eden, "o2"); - final PackWriter pw = new PackWriter(eden, NullProgressMonitor.INSTANCE); - pw.addObject(o2); - pw.addObject(o1); - write(out1, pw); - - // 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. - // - assertEquals(o1.name(), parse(o1).name()); - assertEquals(o2.name(), parse(o2).name()); - } - - public void testObjectMovedToNewPack2() - throws IncorrectObjectTypeException, IOException { - // Create an object and pack it. Then remove that pack and put the - // object into a different pack file, with some other object. We - // still should be able to access the objects. - // - final Repository eden = createBareRepository(); - final RevObject o1 = writeBlob(eden, "o1"); - final File[] out1 = pack(eden, o1); - assertEquals(o1.name(), parse(o1).name()); - - final ObjectLoader load1 = db.openBlob(o1); - assertNotNull(load1); - - final RevObject o2 = writeBlob(eden, "o2"); - pack(eden, o2, o1); - - // Force close, and then delete, the old pack. - // - whackCache(); - delete(out1); - - // Now here is the interesting thing... can the loader we made - // earlier still resolve the object, even though its underlying - // pack is gone, but the object still exists. - // - final ObjectLoader load2 = db.openBlob(o1); - assertNotNull(load2); - assertNotSame(load1, load2); - - final byte[] data2 = load2.getCachedBytes(); - final byte[] data1 = load1.getCachedBytes(); - assertNotNull(data2); - assertNotNull(data1); - assertNotSame(data1, data2); // cache should be per-pack, not per object - assertTrue(Arrays.equals(data1, data2)); - assertEquals(load2.getType(), load1.getType()); - } - - private static void whackCache() { - final WindowCacheConfig config = new WindowCacheConfig(); - config.setPackedGitOpenFiles(1); - WindowCache.reconfigure(config); - } - - private RevObject parse(final AnyObjectId id) - throws MissingObjectException, IOException { - return new RevWalk(db).parseAny(id); - } - - private File[] pack(final Repository src, final RevObject... list) - throws IOException { - final PackWriter pw = new PackWriter(src, NullProgressMonitor.INSTANCE); - for (final RevObject o : list) { - pw.addObject(o); - } - - final ObjectId name = pw.computeName(); - final File packFile = fullPackFileName(name, ".pack"); - final File idxFile = fullPackFileName(name, ".idx"); - final File[] files = new File[] { packFile, idxFile }; - write(files, pw); - return files; - } - - private static void write(final File[] files, final PackWriter pw) - throws IOException { - final long begin = files[0].getParentFile().lastModified(); - OutputStream out; - - out = new BufferedOutputStream(new FileOutputStream(files[0])); - try { - pw.writePack(out); - } finally { - out.close(); - } - - out = new BufferedOutputStream(new FileOutputStream(files[1])); - try { - pw.writeIndex(out); - } finally { - out.close(); - } - - touch(begin, files[0].getParentFile()); - } - - private static void delete(final File[] list) { - final long begin = list[0].getParentFile().lastModified(); - for (final File f : list) { - f.delete(); - assertFalse(f + " was removed", f.exists()); - } - touch(begin, list[0].getParentFile()); - } - - private static void touch(final long begin, final File dir) { - while (begin >= dir.lastModified()) { - try { - Thread.sleep(25); - } catch (InterruptedException ie) { - // - } - dir.setLastModified(System.currentTimeMillis()); - } - } - - private File fullPackFileName(final ObjectId name, final String suffix) { - final File packdir = new File(db.getObjectDatabase().getDirectory(), "pack"); - return new File(packdir, "pack-" + name.name() + suffix); - } - - private RevObject writeBlob(final Repository repo, final String data) - throws IOException { - final RevWalk revWalk = new RevWalk(repo); - final byte[] bytes = Constants.encode(data); - final ObjectWriter ow = new ObjectWriter(repo); - final ObjectId id = ow.writeBlob(bytes); - try { - parse(id); - fail("Object " + id.name() + " should not exist in test repository"); - } catch (MissingObjectException e) { - // Ok - } - return revWalk.lookupBlob(id); - } -} diff --git a/org.eclipse.jgit.test/tst/org/eclipse/jgit/lib/PackIndexTestCase.java b/org.eclipse.jgit.test/tst/org/eclipse/jgit/lib/PackIndexTestCase.java deleted file mode 100644 index d1c5c5eccd..0000000000 --- a/org.eclipse.jgit.test/tst/org/eclipse/jgit/lib/PackIndexTestCase.java +++ /dev/null @@ -1,168 +0,0 @@ -/* - * Copyright (C) 2008, Marek Zawirski - * 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.util.Iterator; -import java.util.NoSuchElementException; - -import org.eclipse.jgit.errors.MissingObjectException; -import org.eclipse.jgit.lib.PackIndex.MutableEntry; - -public abstract class PackIndexTestCase extends RepositoryTestCase { - - PackIndex smallIdx; - - PackIndex denseIdx; - - public void setUp() throws Exception { - super.setUp(); - smallIdx = PackIndex.open(getFileForPack34be9032()); - denseIdx = PackIndex.open(getFileForPackdf2982f28()); - } - - /** - * Return file with appropriate index version for prepared pack. - * - * @return file with index - */ - public abstract File getFileForPack34be9032(); - - /** - * Return file with appropriate index version for prepared pack. - * - * @return file with index - */ - public abstract File getFileForPackdf2982f28(); - - /** - * Verify CRC32 support. - * - * @throws MissingObjectException - * @throws UnsupportedOperationException - */ - public abstract void testCRC32() throws MissingObjectException, - UnsupportedOperationException; - - /** - * Test contracts of Iterator methods and this implementation remove() - * limitations. - */ - public void testIteratorMethodsContract() { - Iterator iter = smallIdx.iterator(); - while (iter.hasNext()) { - iter.next(); - } - - try { - iter.next(); - fail("next() unexpectedly returned element"); - } catch (NoSuchElementException x) { - // expected - } - - try { - iter.remove(); - fail("remove() shouldn't be implemented"); - } catch (UnsupportedOperationException x) { - // expected - } - } - - /** - * Test results of iterator comparing to content of well-known (prepared) - * small index. - */ - public void testIteratorReturnedValues1() { - Iterator iter = smallIdx.iterator(); - assertEquals("4b825dc642cb6eb9a060e54bf8d69288fbee4904", iter.next() - .name()); - assertEquals("540a36d136cf413e4b064c2b0e0a4db60f77feab", iter.next() - .name()); - assertEquals("5b6e7c66c276e7610d4a73c70ec1a1f7c1003259", iter.next() - .name()); - assertEquals("6ff87c4664981e4397625791c8ea3bbb5f2279a3", iter.next() - .name()); - assertEquals("82c6b885ff600be425b4ea96dee75dca255b69e7", iter.next() - .name()); - assertEquals("902d5476fa249b7abc9d84c611577a81381f0327", iter.next() - .name()); - assertEquals("aabf2ffaec9b497f0950352b3e582d73035c2035", iter.next() - .name()); - assertEquals("c59759f143fb1fe21c197981df75a7ee00290799", iter.next() - .name()); - assertFalse(iter.hasNext()); - } - - /** - * Compare offset from iterator entries with output of findOffset() method. - */ - public void testCompareEntriesOffsetsWithFindOffsets() { - for (MutableEntry me : smallIdx) { - assertEquals(smallIdx.findOffset(me.toObjectId()), me.getOffset()); - } - for (MutableEntry me : denseIdx) { - assertEquals(denseIdx.findOffset(me.toObjectId()), me.getOffset()); - } - } - - /** - * Test partial results of iterator comparing to content of well-known - * (prepared) dense index, that may need multi-level indexing. - */ - public void testIteratorReturnedValues2() { - Iterator iter = denseIdx.iterator(); - while (!iter.next().name().equals( - "0a3d7772488b6b106fb62813c4d6d627918d9181")) { - // just iterating - } - assertEquals("1004d0d7ac26fbf63050a234c9b88a46075719d3", iter.next() - .name()); // same level-1 - assertEquals("10da5895682013006950e7da534b705252b03be6", iter.next() - .name()); // same level-1 - assertEquals("1203b03dc816ccbb67773f28b3c19318654b0bc8", iter.next() - .name()); - } - -} diff --git a/org.eclipse.jgit.test/tst/org/eclipse/jgit/lib/PackIndexV1Test.java b/org.eclipse.jgit.test/tst/org/eclipse/jgit/lib/PackIndexV1Test.java deleted file mode 100644 index f3082fb294..0000000000 --- a/org.eclipse.jgit.test/tst/org/eclipse/jgit/lib/PackIndexV1Test.java +++ /dev/null @@ -1,82 +0,0 @@ -/* - * Copyright (C) 2008, Imran M Yousuf - * Copyright (C) 2008, Marek Zawirski - * Copyright (C) 2009, Matthias Sohn - * 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.errors.MissingObjectException; -import org.eclipse.jgit.util.JGitTestUtil; - -public class PackIndexV1Test extends PackIndexTestCase { - @Override - public File getFileForPack34be9032() { - return JGitTestUtil.getTestResourceFile( - "pack-34be9032ac282b11fa9babdc2b2a93ca996c9c2f.idx"); - } - - @Override - public File getFileForPackdf2982f28() { - return JGitTestUtil.getTestResourceFile( - "pack-df2982f284bbabb6bdb59ee3fcc6eb0983e20371.idx"); - } - - /** - * Verify CRC32 - V1 should not index anything. - * - * @throws MissingObjectException - */ - @Override - public void testCRC32() throws MissingObjectException { - assertFalse(smallIdx.hasCRC32Support()); - try { - smallIdx.findCRC32(ObjectId - .fromString("4b825dc642cb6eb9a060e54bf8d69288fbee4904")); - fail("index V1 shouldn't support CRC"); - } catch (UnsupportedOperationException x) { - // expected - } - } -} diff --git a/org.eclipse.jgit.test/tst/org/eclipse/jgit/lib/PackIndexV2Test.java b/org.eclipse.jgit.test/tst/org/eclipse/jgit/lib/PackIndexV2Test.java deleted file mode 100644 index c5669f9d24..0000000000 --- a/org.eclipse.jgit.test/tst/org/eclipse/jgit/lib/PackIndexV2Test.java +++ /dev/null @@ -1,93 +0,0 @@ -/* - * Copyright (C) 2008, Imran M Yousuf - * Copyright (C) 2008, Marek Zawirski - * Copyright (C) 2009, Matthias Sohn - * 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.errors.MissingObjectException; -import org.eclipse.jgit.util.JGitTestUtil; - -public class PackIndexV2Test extends PackIndexTestCase { - @Override - public File getFileForPack34be9032() { - return JGitTestUtil.getTestResourceFile( - "pack-34be9032ac282b11fa9babdc2b2a93ca996c9c2f.idxV2"); - } - - @Override - public File getFileForPackdf2982f28() { - return JGitTestUtil.getTestResourceFile( - "pack-df2982f284bbabb6bdb59ee3fcc6eb0983e20371.idxV2"); - } - - /** - * Verify CRC32 indexing. - * - * @throws UnsupportedOperationException - * @throws MissingObjectException - */ - @Override - public void testCRC32() throws MissingObjectException, - UnsupportedOperationException { - assertTrue(smallIdx.hasCRC32Support()); - assertEquals(0x00000000C2B64258l, smallIdx.findCRC32(ObjectId - .fromString("4b825dc642cb6eb9a060e54bf8d69288fbee4904"))); - assertEquals(0x0000000072AD57C2l, smallIdx.findCRC32(ObjectId - .fromString("540a36d136cf413e4b064c2b0e0a4db60f77feab"))); - assertEquals(0x00000000FF10A479l, smallIdx.findCRC32(ObjectId - .fromString("5b6e7c66c276e7610d4a73c70ec1a1f7c1003259"))); - assertEquals(0x0000000034B27DDCl, smallIdx.findCRC32(ObjectId - .fromString("6ff87c4664981e4397625791c8ea3bbb5f2279a3"))); - assertEquals(0x000000004743F1E4l, smallIdx.findCRC32(ObjectId - .fromString("82c6b885ff600be425b4ea96dee75dca255b69e7"))); - assertEquals(0x00000000640B358Bl, smallIdx.findCRC32(ObjectId - .fromString("902d5476fa249b7abc9d84c611577a81381f0327"))); - assertEquals(0x000000002A17CB5El, smallIdx.findCRC32(ObjectId - .fromString("aabf2ffaec9b497f0950352b3e582d73035c2035"))); - assertEquals(0x000000000B3B5BA6l, smallIdx.findCRC32(ObjectId - .fromString("c59759f143fb1fe21c197981df75a7ee00290799"))); - } -} diff --git a/org.eclipse.jgit.test/tst/org/eclipse/jgit/lib/PackReverseIndexTest.java b/org.eclipse.jgit.test/tst/org/eclipse/jgit/lib/PackReverseIndexTest.java deleted file mode 100644 index 19b705813f..0000000000 --- a/org.eclipse.jgit.test/tst/org/eclipse/jgit/lib/PackReverseIndexTest.java +++ /dev/null @@ -1,121 +0,0 @@ -/* - * Copyright (C) 2008, Imran M Yousuf - * Copyright (C) 2008, Marek Zawirski - * 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 org.eclipse.jgit.errors.CorruptObjectException; -import org.eclipse.jgit.lib.PackIndex.MutableEntry; -import org.eclipse.jgit.util.JGitTestUtil; - -public class PackReverseIndexTest extends RepositoryTestCase { - - private PackIndex idx; - - private PackReverseIndex reverseIdx; - - /** - * Set up tested class instance, test constructor by the way. - */ - public void setUp() throws Exception { - super.setUp(); - // index with both small (< 2^31) and big offsets - idx = PackIndex.open(JGitTestUtil.getTestResourceFile( - "pack-huge.idx")); - reverseIdx = new PackReverseIndex(idx); - } - - /** - * Test findObject() for all index entries. - */ - public void testFindObject() { - for (MutableEntry me : idx) - assertEquals(me.toObjectId(), reverseIdx.findObject(me.getOffset())); - } - - /** - * Test findObject() with illegal argument. - */ - public void testFindObjectWrongOffset() { - assertNull(reverseIdx.findObject(0)); - } - - /** - * Test findNextOffset() for all index entries. - * - * @throws CorruptObjectException - */ - public void testFindNextOffset() throws CorruptObjectException { - long offset = findFirstOffset(); - assertTrue(offset > 0); - for (int i = 0; i < idx.getObjectCount(); i++) { - long newOffset = reverseIdx.findNextOffset(offset, Long.MAX_VALUE); - assertTrue(newOffset > offset); - if (i == idx.getObjectCount() - 1) - assertEquals(newOffset, Long.MAX_VALUE); - else - assertEquals(newOffset, idx.findOffset(reverseIdx - .findObject(newOffset))); - offset = newOffset; - } - } - - /** - * Test findNextOffset() with wrong illegal argument as offset. - */ - public void testFindNextOffsetWrongOffset() { - try { - reverseIdx.findNextOffset(0, Long.MAX_VALUE); - fail("findNextOffset() should throw exception"); - } catch (CorruptObjectException x) { - // expected - } - } - - private long findFirstOffset() { - long min = Long.MAX_VALUE; - for (MutableEntry me : idx) - min = Math.min(min, me.getOffset()); - return min; - } -} diff --git a/org.eclipse.jgit.test/tst/org/eclipse/jgit/lib/PackWriterTest.java b/org.eclipse.jgit.test/tst/org/eclipse/jgit/lib/PackWriterTest.java deleted file mode 100644 index c291968b74..0000000000 --- a/org.eclipse.jgit.test/tst/org/eclipse/jgit/lib/PackWriterTest.java +++ /dev/null @@ -1,530 +0,0 @@ -/* - * Copyright (C) 2008, Marek Zawirski - * 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.File; -import java.io.FileOutputStream; -import java.io.IOException; -import java.io.InputStream; -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.LinkedList; -import java.util.List; - -import org.eclipse.jgit.errors.MissingObjectException; -import org.eclipse.jgit.lib.PackIndex.MutableEntry; -import org.eclipse.jgit.revwalk.RevObject; -import org.eclipse.jgit.revwalk.RevWalk; -import org.eclipse.jgit.transport.IndexPack; -import org.eclipse.jgit.util.JGitTestUtil; - -public class PackWriterTest extends SampleDataRepositoryTestCase { - - private static final List EMPTY_LIST_OBJECT = Collections - . emptyList(); - - private static final List EMPTY_LIST_REVS = Collections - . emptyList(); - - private PackWriter writer; - - private ByteArrayOutputStream os; - - private File packBase; - - private File packFile; - - private File indexFile; - - private PackFile pack; - - public void setUp() throws Exception { - super.setUp(); - os = new ByteArrayOutputStream(); - 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()); - } - - /** - * Test constructor for exceptions, default settings, initialization. - */ - public void testContructor() { - assertEquals(false, writer.isDeltaBaseAsOffset()); - assertEquals(true, writer.isReuseDeltas()); - assertEquals(true, writer.isReuseObjects()); - assertEquals(0, writer.getObjectsNumber()); - } - - /** - * Change default settings and verify them. - */ - public void testModifySettings() { - writer.setDeltaBaseAsOffset(true); - writer.setReuseDeltas(false); - writer.setReuseObjects(false); - - assertEquals(true, writer.isDeltaBaseAsOffset()); - assertEquals(false, writer.isReuseDeltas()); - assertEquals(false, writer.isReuseObjects()); - } - - /** - * Write empty pack by providing empty sets of interesting/uninteresting - * objects and check for correct format. - * - * @throws IOException - */ - public void testWriteEmptyPack1() throws IOException { - createVerifyOpenPack(EMPTY_LIST_OBJECT, EMPTY_LIST_OBJECT, false, false); - - assertEquals(0, writer.getObjectsNumber()); - assertEquals(0, pack.getObjectCount()); - assertEquals("da39a3ee5e6b4b0d3255bfef95601890afd80709", writer - .computeName().name()); - } - - /** - * Write empty pack by providing empty iterator of objects to write and - * check for correct format. - * - * @throws IOException - */ - public void testWriteEmptyPack2() throws IOException { - createVerifyOpenPack(EMPTY_LIST_REVS.iterator()); - - assertEquals(0, writer.getObjectsNumber()); - assertEquals(0, pack.getObjectCount()); - } - - /** - * Try to pass non-existing object as uninteresting, with non-ignoring - * setting. - * - * @throws IOException - */ - public void testNotIgnoreNonExistingObjects() throws IOException { - final ObjectId nonExisting = ObjectId - .fromString("0000000000000000000000000000000000000001"); - try { - createVerifyOpenPack(EMPTY_LIST_OBJECT, Collections.nCopies(1, - nonExisting), false, false); - fail("Should have thrown MissingObjectException"); - } catch (MissingObjectException x) { - // expected - } - } - - /** - * Try to pass non-existing object as uninteresting, with ignoring setting. - * - * @throws IOException - */ - public void testIgnoreNonExistingObjects() throws IOException { - final ObjectId nonExisting = ObjectId - .fromString("0000000000000000000000000000000000000001"); - createVerifyOpenPack(EMPTY_LIST_OBJECT, Collections.nCopies(1, - nonExisting), false, true); - // shouldn't throw anything - } - - /** - * Create pack basing on only interesting objects, then precisely verify - * content. No delta reuse here. - * - * @throws IOException - */ - public void testWritePack1() throws IOException { - writer.setReuseDeltas(false); - writeVerifyPack1(); - } - - /** - * Test writing pack without object reuse. Pack content/preparation as in - * {@link #testWritePack1()}. - * - * @throws IOException - */ - public void testWritePack1NoObjectReuse() throws IOException { - writer.setReuseDeltas(false); - writer.setReuseObjects(false); - writeVerifyPack1(); - } - - /** - * Create pack basing on both interesting and uninteresting objects, then - * precisely verify content. No delta reuse here. - * - * @throws IOException - */ - public void testWritePack2() throws IOException { - writeVerifyPack2(false); - } - - /** - * Test pack writing with deltas reuse, delta-base first rule. Pack - * content/preparation as in {@link #testWritePack2()}. - * - * @throws IOException - */ - public void testWritePack2DeltasReuseRefs() throws IOException { - writeVerifyPack2(true); - } - - /** - * Test pack writing with delta reuse. Delta bases referred as offsets. Pack - * configuration as in {@link #testWritePack2DeltasReuseRefs()}. - * - * @throws IOException - */ - public void testWritePack2DeltasReuseOffsets() throws IOException { - writer.setDeltaBaseAsOffset(true); - writeVerifyPack2(true); - } - - /** - * Test pack writing with delta reuse. Raw-data copy (reuse) is made on a - * pack with CRC32 index. Pack configuration as in - * {@link #testWritePack2DeltasReuseRefs()}. - * - * @throws IOException - */ - public void testWritePack2DeltasCRC32Copy() throws IOException { - final File packDir = new File(db.getObjectDatabase().getDirectory(), "pack"); - final File crc32Pack = new File(packDir, - "pack-34be9032ac282b11fa9babdc2b2a93ca996c9c2f.pack"); - final File crc32Idx = new File(packDir, - "pack-34be9032ac282b11fa9babdc2b2a93ca996c9c2f.idx"); - copyFile(JGitTestUtil.getTestResourceFile( - "pack-34be9032ac282b11fa9babdc2b2a93ca996c9c2f.idxV2"), - crc32Idx); - db.openPack(crc32Pack, crc32Idx); - - writeVerifyPack2(true); - } - - /** - * Create pack basing on fixed objects list, then precisely verify content. - * No delta reuse here. - * - * @throws IOException - * @throws MissingObjectException - * - */ - public void testWritePack3() throws MissingObjectException, IOException { - writer.setReuseDeltas(false); - final ObjectId forcedOrder[] = new ObjectId[] { - ObjectId.fromString("82c6b885ff600be425b4ea96dee75dca255b69e7"), - ObjectId.fromString("c59759f143fb1fe21c197981df75a7ee00290799"), - ObjectId.fromString("aabf2ffaec9b497f0950352b3e582d73035c2035"), - ObjectId.fromString("902d5476fa249b7abc9d84c611577a81381f0327"), - ObjectId.fromString("5b6e7c66c276e7610d4a73c70ec1a1f7c1003259"), - ObjectId.fromString("6ff87c4664981e4397625791c8ea3bbb5f2279a3") }; - final RevWalk parser = new RevWalk(db); - final RevObject forcedOrderRevs[] = new RevObject[forcedOrder.length]; - for (int i = 0; i < forcedOrder.length; i++) - forcedOrderRevs[i] = parser.parseAny(forcedOrder[i]); - - createVerifyOpenPack(Arrays.asList(forcedOrderRevs).iterator()); - - assertEquals(forcedOrder.length, writer.getObjectsNumber()); - verifyObjectsOrder(forcedOrder); - assertEquals("ed3f96b8327c7c66b0f8f70056129f0769323d86", writer - .computeName().name()); - } - - /** - * Another pack creation: basing on both interesting and uninteresting - * objects. No delta reuse possible here, as this is a specific case when we - * write only 1 commit, associated with 1 tree, 1 blob. - * - * @throws IOException - */ - public void testWritePack4() throws IOException { - writeVerifyPack4(false); - } - - /** - * Test thin pack writing: 1 blob delta base is on objects edge. Pack - * configuration as in {@link #testWritePack4()}. - * - * @throws IOException - */ - public void testWritePack4ThinPack() throws IOException { - writeVerifyPack4(true); - } - - /** - * Compare sizes of packs created using {@link #testWritePack2()} and - * {@link #testWritePack2DeltasReuseRefs()}. The pack using deltas should - * be smaller. - * - * @throws Exception - */ - public void testWritePack2SizeDeltasVsNoDeltas() throws Exception { - testWritePack2(); - final long sizePack2NoDeltas = os.size(); - tearDown(); - setUp(); - testWritePack2DeltasReuseRefs(); - final long sizePack2DeltasRefs = os.size(); - - assertTrue(sizePack2NoDeltas > sizePack2DeltasRefs); - } - - /** - * Compare sizes of packs created using - * {@link #testWritePack2DeltasReuseRefs()} and - * {@link #testWritePack2DeltasReuseOffsets()}. The pack with delta bases - * written as offsets should be smaller. - * - * @throws Exception - */ - public void testWritePack2SizeOffsetsVsRefs() throws Exception { - testWritePack2DeltasReuseRefs(); - final long sizePack2DeltasRefs = os.size(); - tearDown(); - setUp(); - testWritePack2DeltasReuseOffsets(); - final long sizePack2DeltasOffsets = os.size(); - - assertTrue(sizePack2DeltasRefs > sizePack2DeltasOffsets); - } - - /** - * Compare sizes of packs created using {@link #testWritePack4()} and - * {@link #testWritePack4ThinPack()}. Obviously, the thin pack should be - * smaller. - * - * @throws Exception - */ - public void testWritePack4SizeThinVsNoThin() throws Exception { - testWritePack4(); - final long sizePack4 = os.size(); - tearDown(); - setUp(); - testWritePack4ThinPack(); - final long sizePack4Thin = os.size(); - - assertTrue(sizePack4 > sizePack4Thin); - } - - public void testWriteIndex() throws Exception { - writer.setIndexVersion(2); - writeVerifyPack4(false); - - // Validate that IndexPack came up with the right CRC32 value. - final PackIndex idx1 = PackIndex.open(indexFile); - assertTrue(idx1 instanceof PackIndexV2); - assertEquals(0x4743F1E4L, idx1.findCRC32(ObjectId - .fromString("82c6b885ff600be425b4ea96dee75dca255b69e7"))); - - // Validate that an index written by PackWriter is the same. - final File idx2File = new File(indexFile.getAbsolutePath() + ".2"); - final FileOutputStream is = new FileOutputStream(idx2File); - try { - writer.writeIndex(is); - } finally { - is.close(); - } - final PackIndex idx2 = PackIndex.open(idx2File); - assertTrue(idx2 instanceof PackIndexV2); - assertEquals(idx1.getObjectCount(), idx2.getObjectCount()); - assertEquals(idx1.getOffset64Count(), idx2.getOffset64Count()); - - for (int i = 0; i < idx1.getObjectCount(); i++) { - final ObjectId id = idx1.getObjectId(i); - assertEquals(id, idx2.getObjectId(i)); - assertEquals(idx1.findOffset(id), idx2.findOffset(id)); - assertEquals(idx1.findCRC32(id), idx2.findCRC32(id)); - } - } - - // TODO: testWritePackDeltasCycle() - // TODO: testWritePackDeltasDepth() - - private void writeVerifyPack1() throws IOException { - final LinkedList interestings = new LinkedList(); - interestings.add(ObjectId - .fromString("82c6b885ff600be425b4ea96dee75dca255b69e7")); - createVerifyOpenPack(interestings, EMPTY_LIST_OBJECT, false, false); - - final ObjectId expectedOrder[] = new ObjectId[] { - ObjectId.fromString("82c6b885ff600be425b4ea96dee75dca255b69e7"), - ObjectId.fromString("c59759f143fb1fe21c197981df75a7ee00290799"), - ObjectId.fromString("540a36d136cf413e4b064c2b0e0a4db60f77feab"), - ObjectId.fromString("aabf2ffaec9b497f0950352b3e582d73035c2035"), - ObjectId.fromString("902d5476fa249b7abc9d84c611577a81381f0327"), - ObjectId.fromString("4b825dc642cb6eb9a060e54bf8d69288fbee4904"), - ObjectId.fromString("5b6e7c66c276e7610d4a73c70ec1a1f7c1003259"), - ObjectId.fromString("6ff87c4664981e4397625791c8ea3bbb5f2279a3") }; - - assertEquals(expectedOrder.length, writer.getObjectsNumber()); - verifyObjectsOrder(expectedOrder); - assertEquals("34be9032ac282b11fa9babdc2b2a93ca996c9c2f", writer - .computeName().name()); - } - - private void writeVerifyPack2(boolean deltaReuse) throws IOException { - writer.setReuseDeltas(deltaReuse); - final LinkedList interestings = new LinkedList(); - interestings.add(ObjectId - .fromString("82c6b885ff600be425b4ea96dee75dca255b69e7")); - final LinkedList uninterestings = new LinkedList(); - uninterestings.add(ObjectId - .fromString("540a36d136cf413e4b064c2b0e0a4db60f77feab")); - createVerifyOpenPack(interestings, uninterestings, false, false); - - final ObjectId expectedOrder[] = new ObjectId[] { - ObjectId.fromString("82c6b885ff600be425b4ea96dee75dca255b69e7"), - ObjectId.fromString("c59759f143fb1fe21c197981df75a7ee00290799"), - ObjectId.fromString("aabf2ffaec9b497f0950352b3e582d73035c2035"), - ObjectId.fromString("902d5476fa249b7abc9d84c611577a81381f0327"), - ObjectId.fromString("5b6e7c66c276e7610d4a73c70ec1a1f7c1003259"), - ObjectId.fromString("6ff87c4664981e4397625791c8ea3bbb5f2279a3") }; - if (deltaReuse) { - // objects order influenced (swapped) by delta-base first rule - ObjectId temp = expectedOrder[4]; - expectedOrder[4] = expectedOrder[5]; - expectedOrder[5] = temp; - } - assertEquals(expectedOrder.length, writer.getObjectsNumber()); - verifyObjectsOrder(expectedOrder); - assertEquals("ed3f96b8327c7c66b0f8f70056129f0769323d86", writer - .computeName().name()); - } - - private void writeVerifyPack4(final boolean thin) throws IOException { - final LinkedList interestings = new LinkedList(); - interestings.add(ObjectId - .fromString("82c6b885ff600be425b4ea96dee75dca255b69e7")); - final LinkedList uninterestings = new LinkedList(); - uninterestings.add(ObjectId - .fromString("c59759f143fb1fe21c197981df75a7ee00290799")); - createVerifyOpenPack(interestings, uninterestings, thin, false); - - final ObjectId writtenObjects[] = new ObjectId[] { - ObjectId.fromString("82c6b885ff600be425b4ea96dee75dca255b69e7"), - ObjectId.fromString("aabf2ffaec9b497f0950352b3e582d73035c2035"), - ObjectId.fromString("5b6e7c66c276e7610d4a73c70ec1a1f7c1003259") }; - assertEquals(writtenObjects.length, writer.getObjectsNumber()); - ObjectId expectedObjects[]; - if (thin) { - expectedObjects = new ObjectId[4]; - System.arraycopy(writtenObjects, 0, expectedObjects, 0, - writtenObjects.length); - expectedObjects[3] = ObjectId - .fromString("6ff87c4664981e4397625791c8ea3bbb5f2279a3"); - - } else { - expectedObjects = writtenObjects; - } - verifyObjectsOrder(expectedObjects); - assertEquals("cded4b74176b4456afa456768b2b5aafb41c44fc", writer - .computeName().name()); - } - - private void createVerifyOpenPack(final Collection interestings, - final Collection uninterestings, final boolean thin, - final boolean ignoreMissingUninteresting) - throws MissingObjectException, IOException { - writer.setThin(thin); - writer.setIgnoreMissingUninteresting(ignoreMissingUninteresting); - writer.preparePack(interestings, uninterestings); - writer.writePack(os); - verifyOpenPack(thin); - } - - private void createVerifyOpenPack(final Iterator objectSource) - throws MissingObjectException, IOException { - writer.preparePack(objectSource); - writer.writePack(os); - verifyOpenPack(false); - } - - private void verifyOpenPack(final boolean thin) throws IOException { - if (thin) { - final InputStream is = new ByteArrayInputStream(os.toByteArray()); - final IndexPack indexer = new IndexPack(db, is, packBase); - try { - indexer.index(new TextProgressMonitor()); - fail("indexer should grumble about missing object"); - } catch (IOException x) { - // expected - } - } - final InputStream is = new ByteArrayInputStream(os.toByteArray()); - final IndexPack indexer = new IndexPack(db, is, packBase); - indexer.setKeepEmpty(true); - indexer.setFixThin(thin); - indexer.setIndexVersion(2); - indexer.index(new TextProgressMonitor()); - pack = new PackFile(indexFile, packFile); - } - - private void verifyObjectsOrder(final ObjectId objectsOrder[]) { - final List entries = new ArrayList(); - - for (MutableEntry me : pack) { - entries.add(me.cloneEntry()); - } - Collections.sort(entries, new Comparator() { - public int compare(MutableEntry o1, MutableEntry o2) { - return Long.signum(o1.getOffset() - o2.getOffset()); - } - }); - - int i = 0; - for (MutableEntry me : entries) { - assertEquals(objectsOrder[i++].toObjectId(), me.toObjectId()); - } - } -} diff --git a/org.eclipse.jgit.test/tst/org/eclipse/jgit/lib/RefDirectoryTest.java b/org.eclipse.jgit.test/tst/org/eclipse/jgit/lib/RefDirectoryTest.java deleted file mode 100644 index a2812901bc..0000000000 --- a/org.eclipse.jgit.test/tst/org/eclipse/jgit/lib/RefDirectoryTest.java +++ /dev/null @@ -1,1024 +0,0 @@ -/* - * 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 static org.eclipse.jgit.lib.Constants.HEAD; -import static org.eclipse.jgit.lib.Constants.R_HEADS; -import static org.eclipse.jgit.lib.Constants.R_TAGS; -import static org.eclipse.jgit.lib.Ref.Storage.LOOSE; -import static org.eclipse.jgit.lib.Ref.Storage.NEW; - -import java.io.File; -import java.io.IOException; -import java.util.Map; - -import org.eclipse.jgit.junit.LocalDiskRepositoryTestCase; -import org.eclipse.jgit.junit.TestRepository; -import org.eclipse.jgit.revwalk.RevCommit; -import org.eclipse.jgit.revwalk.RevTag; - -public class RefDirectoryTest extends LocalDiskRepositoryTestCase { - private Repository diskRepo; - - private TestRepository repo; - - private RefDirectory refdir; - - private RevCommit A; - - private RevCommit B; - - private RevTag v1_0; - - protected void setUp() throws Exception { - super.setUp(); - - diskRepo = createBareRepository(); - refdir = (RefDirectory) diskRepo.getRefDatabase(); - - repo = new TestRepository(diskRepo); - A = repo.commit().create(); - B = repo.commit(repo.getRevWalk().parseCommit(A)); - v1_0 = repo.tag("v1_0", B); - repo.getRevWalk().parseBody(v1_0); - } - - public void testCreate() throws IOException { - // setUp above created the directory. We just have to test it. - File d = diskRepo.getDirectory(); - assertSame(diskRepo, refdir.getRepository()); - - assertTrue(new File(d, "refs").isDirectory()); - assertTrue(new File(d, "logs").isDirectory()); - assertTrue(new File(d, "logs/refs").isDirectory()); - assertFalse(new File(d, "packed-refs").exists()); - - assertTrue(new File(d, "refs/heads").isDirectory()); - assertTrue(new File(d, "refs/tags").isDirectory()); - assertEquals(2, new File(d, "refs").list().length); - assertEquals(0, new File(d, "refs/heads").list().length); - assertEquals(0, new File(d, "refs/tags").list().length); - - assertTrue(new File(d, "logs/refs/heads").isDirectory()); - assertFalse(new File(d, "logs/HEAD").exists()); - assertEquals(0, new File(d, "logs/refs/heads").list().length); - - assertEquals("ref: refs/heads/master\n", read(new File(d, HEAD))); - } - - public void testGetRefs_EmptyDatabase() throws IOException { - Map all; - - all = refdir.getRefs(RefDatabase.ALL); - assertTrue("no references", all.isEmpty()); - - all = refdir.getRefs(R_HEADS); - assertTrue("no references", all.isEmpty()); - - all = refdir.getRefs(R_TAGS); - assertTrue("no references", all.isEmpty()); - } - - public void testGetRefs_HeadOnOneBranch() throws IOException { - Map all; - Ref head, master; - - writeLooseRef("refs/heads/master", A); - - all = refdir.getRefs(RefDatabase.ALL); - assertEquals(2, all.size()); - assertTrue("has HEAD", all.containsKey(HEAD)); - assertTrue("has master", all.containsKey("refs/heads/master")); - - head = all.get(HEAD); - master = all.get("refs/heads/master"); - - assertEquals(HEAD, head.getName()); - assertTrue(head.isSymbolic()); - assertSame(LOOSE, head.getStorage()); - assertSame("uses same ref as target", master, head.getTarget()); - - assertEquals("refs/heads/master", master.getName()); - assertFalse(master.isSymbolic()); - assertSame(LOOSE, master.getStorage()); - assertEquals(A, master.getObjectId()); - } - - public void testGetRefs_DeatchedHead1() throws IOException { - Map all; - Ref head; - - writeLooseRef(HEAD, A); - BUG_WorkAroundRacyGitIssues(HEAD); - - all = refdir.getRefs(RefDatabase.ALL); - assertEquals(1, all.size()); - assertTrue("has HEAD", all.containsKey(HEAD)); - - head = all.get(HEAD); - - assertEquals(HEAD, head.getName()); - assertFalse(head.isSymbolic()); - assertSame(LOOSE, head.getStorage()); - assertEquals(A, head.getObjectId()); - } - - public void testGetRefs_DeatchedHead2() throws IOException { - Map all; - Ref head, master; - - writeLooseRef(HEAD, A); - writeLooseRef("refs/heads/master", B); - BUG_WorkAroundRacyGitIssues(HEAD); - - all = refdir.getRefs(RefDatabase.ALL); - assertEquals(2, all.size()); - - head = all.get(HEAD); - master = all.get("refs/heads/master"); - - assertEquals(HEAD, head.getName()); - assertFalse(head.isSymbolic()); - assertSame(LOOSE, head.getStorage()); - assertEquals(A, head.getObjectId()); - - assertEquals("refs/heads/master", master.getName()); - assertFalse(master.isSymbolic()); - assertSame(LOOSE, master.getStorage()); - assertEquals(B, master.getObjectId()); - } - - public void testGetRefs_DeeplyNestedBranch() throws IOException { - String name = "refs/heads/a/b/c/d/e/f/g/h/i/j/k"; - Map all; - Ref r; - - writeLooseRef(name, A); - - all = refdir.getRefs(RefDatabase.ALL); - assertEquals(1, all.size()); - - r = all.get(name); - assertEquals(name, r.getName()); - assertFalse(r.isSymbolic()); - assertSame(LOOSE, r.getStorage()); - assertEquals(A, r.getObjectId()); - } - - public void testGetRefs_HeadBranchNotBorn() throws IOException { - Map all; - Ref a, b; - - writeLooseRef("refs/heads/A", A); - writeLooseRef("refs/heads/B", B); - - all = refdir.getRefs(RefDatabase.ALL); - assertEquals(2, all.size()); - assertFalse("no HEAD", all.containsKey(HEAD)); - - a = all.get("refs/heads/A"); - b = all.get("refs/heads/B"); - - assertEquals(A, a.getObjectId()); - assertEquals(B, b.getObjectId()); - - assertEquals("refs/heads/A", a.getName()); - assertEquals("refs/heads/B", b.getName()); - } - - public void testGetRefs_LooseOverridesPacked() throws IOException { - Map heads; - Ref a; - - writeLooseRef("refs/heads/master", B); - writePackedRef("refs/heads/master", A); - - heads = refdir.getRefs(R_HEADS); - assertEquals(1, heads.size()); - - a = heads.get("master"); - assertEquals("refs/heads/master", a.getName()); - assertEquals(B, a.getObjectId()); - } - - public void testGetRefs_IgnoresGarbageRef1() throws IOException { - Map heads; - Ref a; - - writeLooseRef("refs/heads/A", A); - write(new File(diskRepo.getDirectory(), "refs/heads/bad"), "FAIL\n"); - - heads = refdir.getRefs(RefDatabase.ALL); - assertEquals(1, heads.size()); - - a = heads.get("refs/heads/A"); - assertEquals("refs/heads/A", a.getName()); - assertEquals(A, a.getObjectId()); - } - - public void testGetRefs_IgnoresGarbageRef2() throws IOException { - Map heads; - Ref a; - - writeLooseRef("refs/heads/A", A); - write(new File(diskRepo.getDirectory(), "refs/heads/bad"), ""); - - heads = refdir.getRefs(RefDatabase.ALL); - assertEquals(1, heads.size()); - - a = heads.get("refs/heads/A"); - assertEquals("refs/heads/A", a.getName()); - assertEquals(A, a.getObjectId()); - } - - public void testGetRefs_IgnoresGarbageRef3() throws IOException { - Map heads; - Ref a; - - writeLooseRef("refs/heads/A", A); - write(new File(diskRepo.getDirectory(), "refs/heads/bad"), "\n"); - - heads = refdir.getRefs(RefDatabase.ALL); - assertEquals(1, heads.size()); - - a = heads.get("refs/heads/A"); - assertEquals("refs/heads/A", a.getName()); - assertEquals(A, a.getObjectId()); - } - - public void testGetRefs_IgnoresGarbageRef4() throws IOException { - Map heads; - Ref a, b, c; - - writeLooseRef("refs/heads/A", A); - writeLooseRef("refs/heads/B", B); - writeLooseRef("refs/heads/C", A); - heads = refdir.getRefs(RefDatabase.ALL); - assertEquals(3, heads.size()); - assertTrue(heads.containsKey("refs/heads/A")); - assertTrue(heads.containsKey("refs/heads/B")); - assertTrue(heads.containsKey("refs/heads/C")); - - writeLooseRef("refs/heads/B", "FAIL\n"); - BUG_WorkAroundRacyGitIssues("refs/heads/B"); - - heads = refdir.getRefs(RefDatabase.ALL); - assertEquals(2, heads.size()); - - a = heads.get("refs/heads/A"); - b = heads.get("refs/heads/B"); - c = heads.get("refs/heads/C"); - - assertEquals("refs/heads/A", a.getName()); - assertEquals(A, a.getObjectId()); - - assertNull("no refs/heads/B", b); - - assertEquals("refs/heads/C", c.getName()); - assertEquals(A, c.getObjectId()); - } - - public void testGetRefs_InvalidName() throws IOException { - writeLooseRef("refs/heads/A", A); - - assertTrue("empty refs/heads", refdir.getRefs("refs/heads").isEmpty()); - assertTrue("empty objects", refdir.getRefs("objects").isEmpty()); - assertTrue("empty objects/", refdir.getRefs("objects/").isEmpty()); - } - - public void testGetRefs_HeadsOnly_AllLoose() throws IOException { - Map heads; - Ref a, b; - - writeLooseRef("refs/heads/A", A); - writeLooseRef("refs/heads/B", B); - writeLooseRef("refs/tags/v1.0", v1_0); - - heads = refdir.getRefs(R_HEADS); - assertEquals(2, heads.size()); - - a = heads.get("A"); - b = heads.get("B"); - - assertEquals("refs/heads/A", a.getName()); - assertEquals("refs/heads/B", b.getName()); - - assertEquals(A, a.getObjectId()); - assertEquals(B, b.getObjectId()); - } - - public void testGetRefs_HeadsOnly_AllPacked1() throws IOException { - Map heads; - Ref a; - - deleteLooseRef(HEAD); - writePackedRef("refs/heads/A", A); - - heads = refdir.getRefs(R_HEADS); - assertEquals(1, heads.size()); - - a = heads.get("A"); - - assertEquals("refs/heads/A", a.getName()); - assertEquals(A, a.getObjectId()); - } - - public void testGetRefs_HeadsOnly_SymrefToPacked() throws IOException { - Map heads; - Ref master, other; - - writeLooseRef("refs/heads/other", "ref: refs/heads/master\n"); - writePackedRef("refs/heads/master", A); - - heads = refdir.getRefs(R_HEADS); - assertEquals(2, heads.size()); - - master = heads.get("master"); - other = heads.get("other"); - - assertEquals("refs/heads/master", master.getName()); - assertEquals(A, master.getObjectId()); - - assertEquals("refs/heads/other", other.getName()); - assertEquals(A, other.getObjectId()); - assertSame(master, other.getTarget()); - } - - public void testGetRefs_HeadsOnly_Mixed() throws IOException { - Map heads; - Ref a, b; - - writeLooseRef("refs/heads/A", A); - writeLooseRef("refs/heads/B", B); - writePackedRef("refs/tags/v1.0", v1_0); - - heads = refdir.getRefs(R_HEADS); - assertEquals(2, heads.size()); - - a = heads.get("A"); - b = heads.get("B"); - - assertEquals("refs/heads/A", a.getName()); - assertEquals("refs/heads/B", b.getName()); - - assertEquals(A, a.getObjectId()); - assertEquals(B, b.getObjectId()); - } - - public void testGetRefs_TagsOnly_AllLoose() throws IOException { - Map tags; - Ref a; - - writeLooseRef("refs/heads/A", A); - writeLooseRef("refs/tags/v1.0", v1_0); - - tags = refdir.getRefs(R_TAGS); - assertEquals(1, tags.size()); - - a = tags.get("v1.0"); - - assertEquals("refs/tags/v1.0", a.getName()); - assertEquals(v1_0, a.getObjectId()); - } - - public void testGetRefs_TagsOnly_AllPacked() throws IOException { - Map tags; - Ref a; - - deleteLooseRef(HEAD); - writePackedRef("refs/tags/v1.0", v1_0); - - tags = refdir.getRefs(R_TAGS); - assertEquals(1, tags.size()); - - a = tags.get("v1.0"); - - assertEquals("refs/tags/v1.0", a.getName()); - assertEquals(v1_0, a.getObjectId()); - } - - public void testGetRefs_DiscoversNewLoose1() throws IOException { - Map orig, next; - Ref orig_r, next_r; - - writeLooseRef("refs/heads/master", A); - orig = refdir.getRefs(RefDatabase.ALL); - - writeLooseRef("refs/heads/next", B); - next = refdir.getRefs(RefDatabase.ALL); - - assertEquals(2, orig.size()); - assertEquals(3, next.size()); - - assertFalse(orig.containsKey("refs/heads/next")); - assertTrue(next.containsKey("refs/heads/next")); - - orig_r = orig.get("refs/heads/master"); - next_r = next.get("refs/heads/master"); - assertEquals(A, orig_r.getObjectId()); - assertSame("uses cached instance", orig_r, next_r); - assertSame("same HEAD", orig_r, orig.get(HEAD).getTarget()); - assertSame("same HEAD", orig_r, next.get(HEAD).getTarget()); - - next_r = next.get("refs/heads/next"); - assertSame(LOOSE, next_r.getStorage()); - assertEquals(B, next_r.getObjectId()); - } - - public void testGetRefs_DiscoversNewLoose2() throws IOException { - Map orig, next, news; - - writeLooseRef("refs/heads/pu", A); - orig = refdir.getRefs(RefDatabase.ALL); - - writeLooseRef("refs/heads/new/B", B); - news = refdir.getRefs("refs/heads/new/"); - next = refdir.getRefs(RefDatabase.ALL); - - assertEquals(1, orig.size()); - assertEquals(2, next.size()); - assertEquals(1, news.size()); - - assertTrue(orig.containsKey("refs/heads/pu")); - assertTrue(next.containsKey("refs/heads/pu")); - assertFalse(news.containsKey("refs/heads/pu")); - - assertFalse(orig.containsKey("refs/heads/new/B")); - assertTrue(next.containsKey("refs/heads/new/B")); - assertTrue(news.containsKey("B")); - } - - public void testGetRefs_DiscoversModifiedLoose() throws IOException { - Map all; - - writeLooseRef("refs/heads/master", A); - all = refdir.getRefs(RefDatabase.ALL); - assertEquals(A, all.get(HEAD).getObjectId()); - - writeLooseRef("refs/heads/master", B); - BUG_WorkAroundRacyGitIssues("refs/heads/master"); - all = refdir.getRefs(RefDatabase.ALL); - assertEquals(B, all.get(HEAD).getObjectId()); - } - - public void testGetRef_DiscoversModifiedLoose() throws IOException { - Map all; - - writeLooseRef("refs/heads/master", A); - all = refdir.getRefs(RefDatabase.ALL); - assertEquals(A, all.get(HEAD).getObjectId()); - - writeLooseRef("refs/heads/master", B); - BUG_WorkAroundRacyGitIssues("refs/heads/master"); - - Ref master = refdir.getRef("refs/heads/master"); - assertEquals(B, master.getObjectId()); - } - - public void testGetRefs_DiscoversDeletedLoose1() throws IOException { - Map orig, next; - Ref orig_r, next_r; - - writeLooseRef("refs/heads/B", B); - writeLooseRef("refs/heads/master", A); - orig = refdir.getRefs(RefDatabase.ALL); - - deleteLooseRef("refs/heads/B"); - next = refdir.getRefs(RefDatabase.ALL); - - assertEquals(3, orig.size()); - assertEquals(2, next.size()); - - assertTrue(orig.containsKey("refs/heads/B")); - assertFalse(next.containsKey("refs/heads/B")); - - orig_r = orig.get("refs/heads/master"); - next_r = next.get("refs/heads/master"); - assertEquals(A, orig_r.getObjectId()); - assertSame("uses cached instance", orig_r, next_r); - assertSame("same HEAD", orig_r, orig.get(HEAD).getTarget()); - assertSame("same HEAD", orig_r, next.get(HEAD).getTarget()); - - orig_r = orig.get("refs/heads/B"); - assertSame(LOOSE, orig_r.getStorage()); - assertEquals(B, orig_r.getObjectId()); - } - - public void testGetRef_DiscoversDeletedLoose() throws IOException { - Map all; - - writeLooseRef("refs/heads/master", A); - all = refdir.getRefs(RefDatabase.ALL); - assertEquals(A, all.get(HEAD).getObjectId()); - - deleteLooseRef("refs/heads/master"); - assertNull(refdir.getRef("refs/heads/master")); - assertTrue(refdir.getRefs(RefDatabase.ALL).isEmpty()); - } - - public void testGetRefs_DiscoversDeletedLoose2() throws IOException { - Map orig, next; - - writeLooseRef("refs/heads/master", A); - writeLooseRef("refs/heads/pu", B); - orig = refdir.getRefs(RefDatabase.ALL); - - deleteLooseRef("refs/heads/pu"); - next = refdir.getRefs(RefDatabase.ALL); - - assertEquals(3, orig.size()); - assertEquals(2, next.size()); - - assertTrue(orig.containsKey("refs/heads/pu")); - assertFalse(next.containsKey("refs/heads/pu")); - } - - public void testGetRefs_DiscoversDeletedLoose3() throws IOException { - Map orig, next; - - writeLooseRef("refs/heads/master", A); - writeLooseRef("refs/heads/next", B); - writeLooseRef("refs/heads/pu", B); - writeLooseRef("refs/tags/v1.0", v1_0); - orig = refdir.getRefs(RefDatabase.ALL); - - deleteLooseRef("refs/heads/pu"); - deleteLooseRef("refs/heads/next"); - next = refdir.getRefs(RefDatabase.ALL); - - assertEquals(5, orig.size()); - assertEquals(3, next.size()); - - assertTrue(orig.containsKey("refs/heads/pu")); - assertTrue(orig.containsKey("refs/heads/next")); - assertFalse(next.containsKey("refs/heads/pu")); - assertFalse(next.containsKey("refs/heads/next")); - } - - public void testGetRefs_DiscoversDeletedLoose4() throws IOException { - Map orig, next; - Ref orig_r, next_r; - - writeLooseRef("refs/heads/B", B); - writeLooseRef("refs/heads/master", A); - orig = refdir.getRefs(RefDatabase.ALL); - - deleteLooseRef("refs/heads/master"); - next = refdir.getRefs("refs/heads/"); - - assertEquals(3, orig.size()); - assertEquals(1, next.size()); - - assertTrue(orig.containsKey("refs/heads/B")); - assertTrue(orig.containsKey("refs/heads/master")); - assertTrue(next.containsKey("B")); - assertFalse(next.containsKey("master")); - - orig_r = orig.get("refs/heads/B"); - next_r = next.get("B"); - assertEquals(B, orig_r.getObjectId()); - assertSame("uses cached instance", orig_r, next_r); - } - - public void testGetRefs_DiscoversDeletedLoose5() throws IOException { - Map orig, next; - - writeLooseRef("refs/heads/master", A); - writeLooseRef("refs/heads/pu", B); - orig = refdir.getRefs(RefDatabase.ALL); - - deleteLooseRef("refs/heads/pu"); - writeLooseRef("refs/tags/v1.0", v1_0); - next = refdir.getRefs(RefDatabase.ALL); - - assertEquals(3, orig.size()); - assertEquals(3, next.size()); - - assertTrue(orig.containsKey("refs/heads/pu")); - assertFalse(orig.containsKey("refs/tags/v1.0")); - assertFalse(next.containsKey("refs/heads/pu")); - assertTrue(next.containsKey("refs/tags/v1.0")); - } - - public void testGetRefs_SkipsLockFiles() throws IOException { - Map all; - - writeLooseRef("refs/heads/master", A); - writeLooseRef("refs/heads/pu.lock", B); - all = refdir.getRefs(RefDatabase.ALL); - - assertEquals(2, all.size()); - - assertTrue(all.containsKey(HEAD)); - assertTrue(all.containsKey("refs/heads/master")); - assertFalse(all.containsKey("refs/heads/pu.lock")); - } - - public void testGetRefs_CycleInSymbolicRef() throws IOException { - Map all; - Ref r; - - writeLooseRef("refs/1", "ref: refs/2\n"); - writeLooseRef("refs/2", "ref: refs/3\n"); - writeLooseRef("refs/3", "ref: refs/4\n"); - writeLooseRef("refs/4", "ref: refs/5\n"); - writeLooseRef("refs/5", "ref: refs/end\n"); - writeLooseRef("refs/end", A); - - all = refdir.getRefs(RefDatabase.ALL); - r = all.get("refs/1"); - assertNotNull("has 1", r); - - assertEquals("refs/1", r.getName()); - assertEquals(A, r.getObjectId()); - assertTrue(r.isSymbolic()); - - r = r.getTarget(); - assertEquals("refs/2", r.getName()); - assertEquals(A, r.getObjectId()); - assertTrue(r.isSymbolic()); - - r = r.getTarget(); - assertEquals("refs/3", r.getName()); - assertEquals(A, r.getObjectId()); - assertTrue(r.isSymbolic()); - - r = r.getTarget(); - assertEquals("refs/4", r.getName()); - assertEquals(A, r.getObjectId()); - assertTrue(r.isSymbolic()); - - r = r.getTarget(); - assertEquals("refs/5", r.getName()); - assertEquals(A, r.getObjectId()); - assertTrue(r.isSymbolic()); - - r = r.getTarget(); - assertEquals("refs/end", r.getName()); - assertEquals(A, r.getObjectId()); - assertFalse(r.isSymbolic()); - - writeLooseRef("refs/5", "ref: refs/6\n"); - writeLooseRef("refs/6", "ref: refs/end\n"); - BUG_WorkAroundRacyGitIssues("refs/5"); - all = refdir.getRefs(RefDatabase.ALL); - r = all.get("refs/1"); - assertNull("mising 1 due to cycle", r); - } - - public void testGetRefs_PackedNotPeeled_Sorted() throws IOException { - Map all; - - writePackedRefs("" + // - A.name() + " refs/heads/master\n" + // - B.name() + " refs/heads/other\n" + // - v1_0.name() + " refs/tags/v1.0\n"); - all = refdir.getRefs(RefDatabase.ALL); - - assertEquals(4, all.size()); - final Ref head = all.get(HEAD); - final Ref master = all.get("refs/heads/master"); - final Ref other = all.get("refs/heads/other"); - final Ref tag = all.get("refs/tags/v1.0"); - - assertEquals(A, master.getObjectId()); - assertFalse(master.isPeeled()); - assertNull(master.getPeeledObjectId()); - - assertEquals(B, other.getObjectId()); - assertFalse(other.isPeeled()); - assertNull(other.getPeeledObjectId()); - - assertSame(master, head.getTarget()); - assertEquals(A, head.getObjectId()); - assertFalse(head.isPeeled()); - assertNull(head.getPeeledObjectId()); - - assertEquals(v1_0, tag.getObjectId()); - assertFalse(tag.isPeeled()); - assertNull(tag.getPeeledObjectId()); - } - - public void testGetRef_PackedNotPeeled_WrongSort() throws IOException { - writePackedRefs("" + // - v1_0.name() + " refs/tags/v1.0\n" + // - B.name() + " refs/heads/other\n" + // - A.name() + " refs/heads/master\n"); - - final Ref head = refdir.getRef(HEAD); - final Ref master = refdir.getRef("refs/heads/master"); - final Ref other = refdir.getRef("refs/heads/other"); - final Ref tag = refdir.getRef("refs/tags/v1.0"); - - assertEquals(A, master.getObjectId()); - assertFalse(master.isPeeled()); - assertNull(master.getPeeledObjectId()); - - assertEquals(B, other.getObjectId()); - assertFalse(other.isPeeled()); - assertNull(other.getPeeledObjectId()); - - assertSame(master, head.getTarget()); - assertEquals(A, head.getObjectId()); - assertFalse(head.isPeeled()); - assertNull(head.getPeeledObjectId()); - - assertEquals(v1_0, tag.getObjectId()); - assertFalse(tag.isPeeled()); - assertNull(tag.getPeeledObjectId()); - } - - public void testGetRefs_PackedWithPeeled() throws IOException { - Map all; - - writePackedRefs("# pack-refs with: peeled \n" + // - A.name() + " refs/heads/master\n" + // - B.name() + " refs/heads/other\n" + // - v1_0.name() + " refs/tags/v1.0\n" + // - "^" + v1_0.getObject().name() + "\n"); - all = refdir.getRefs(RefDatabase.ALL); - - assertEquals(4, all.size()); - final Ref head = all.get(HEAD); - final Ref master = all.get("refs/heads/master"); - final Ref other = all.get("refs/heads/other"); - final Ref tag = all.get("refs/tags/v1.0"); - - assertEquals(A, master.getObjectId()); - assertTrue(master.isPeeled()); - assertNull(master.getPeeledObjectId()); - - assertEquals(B, other.getObjectId()); - assertTrue(other.isPeeled()); - assertNull(other.getPeeledObjectId()); - - assertSame(master, head.getTarget()); - assertEquals(A, head.getObjectId()); - assertTrue(head.isPeeled()); - assertNull(head.getPeeledObjectId()); - - assertEquals(v1_0, tag.getObjectId()); - assertTrue(tag.isPeeled()); - assertEquals(v1_0.getObject(), tag.getPeeledObjectId()); - } - - public void testGetRef_EmptyDatabase() throws IOException { - Ref r; - - r = refdir.getRef(HEAD); - assertTrue(r.isSymbolic()); - assertSame(LOOSE, r.getStorage()); - assertEquals("refs/heads/master", r.getTarget().getName()); - assertSame(NEW, r.getTarget().getStorage()); - assertNull(r.getTarget().getObjectId()); - - assertNull(refdir.getRef("refs/heads/master")); - assertNull(refdir.getRef("refs/tags/v1.0")); - assertNull(refdir.getRef("FETCH_HEAD")); - assertNull(refdir.getRef("NOT.A.REF.NAME")); - assertNull(refdir.getRef("master")); - assertNull(refdir.getRef("v1.0")); - } - - public void testGetRef_FetchHead() throws IOException { - // This is an odd special case where we need to make sure we read - // exactly the first 40 bytes of the file and nothing further on - // that line, or the remainder of the file. - write(new File(diskRepo.getDirectory(), "FETCH_HEAD"), A.name() - + "\tnot-for-merge" - + "\tbranch 'master' of git://egit.eclipse.org/jgit\n"); - - Ref r = refdir.getRef("FETCH_HEAD"); - assertFalse(r.isSymbolic()); - assertEquals(A, r.getObjectId()); - assertEquals("FETCH_HEAD", r.getName()); - assertFalse(r.isPeeled()); - assertNull(r.getPeeledObjectId()); - } - - public void testGetRef_AnyHeadWithGarbage() throws IOException { - write(new File(diskRepo.getDirectory(), "refs/heads/A"), A.name() - + "012345 . this is not a standard reference\n" - + "#and even more junk\n"); - - Ref r = refdir.getRef("refs/heads/A"); - assertFalse(r.isSymbolic()); - assertEquals(A, r.getObjectId()); - assertEquals("refs/heads/A", r.getName()); - assertFalse(r.isPeeled()); - assertNull(r.getPeeledObjectId()); - } - - public void testGetRefs_CorruptSymbolicReference() throws IOException { - String name = "refs/heads/A"; - writeLooseRef(name, "ref: \n"); - assertTrue(refdir.getRefs(RefDatabase.ALL).isEmpty()); - } - - public void testGetRef_CorruptSymbolicReference() throws IOException { - String name = "refs/heads/A"; - writeLooseRef(name, "ref: \n"); - try { - refdir.getRef(name); - fail("read an invalid reference"); - } catch (IOException err) { - String msg = err.getMessage(); - assertEquals("Not a ref: " + name + ": ref:", msg); - } - } - - public void testGetRefs_CorruptObjectIdReference() throws IOException { - String name = "refs/heads/A"; - String content = "zoo" + A.name(); - writeLooseRef(name, content + "\n"); - assertTrue(refdir.getRefs(RefDatabase.ALL).isEmpty()); - } - - public void testGetRef_CorruptObjectIdReference() throws IOException { - String name = "refs/heads/A"; - String content = "zoo" + A.name(); - writeLooseRef(name, content + "\n"); - try { - refdir.getRef(name); - fail("read an invalid reference"); - } catch (IOException err) { - String msg = err.getMessage(); - assertEquals("Not a ref: " + name + ": " + content, msg); - } - } - - public void testIsNameConflicting() throws IOException { - writeLooseRef("refs/heads/a/b", A); - writePackedRef("refs/heads/q", B); - - // new references cannot replace an existing container - assertTrue(refdir.isNameConflicting("refs")); - assertTrue(refdir.isNameConflicting("refs/heads")); - assertTrue(refdir.isNameConflicting("refs/heads/a")); - - // existing reference is not conflicting - assertFalse(refdir.isNameConflicting("refs/heads/a/b")); - - // new references are not conflicting - assertFalse(refdir.isNameConflicting("refs/heads/a/d")); - assertFalse(refdir.isNameConflicting("refs/heads/master")); - - // existing reference must not be used as a container - assertTrue(refdir.isNameConflicting("refs/heads/a/b/c")); - assertTrue(refdir.isNameConflicting("refs/heads/q/master")); - } - - public void testPeelLooseTag() throws IOException { - writeLooseRef("refs/tags/v1_0", v1_0); - writeLooseRef("refs/tags/current", "ref: refs/tags/v1_0\n"); - - final Ref tag = refdir.getRef("refs/tags/v1_0"); - final Ref cur = refdir.getRef("refs/tags/current"); - - assertEquals(v1_0, tag.getObjectId()); - assertFalse(tag.isSymbolic()); - assertFalse(tag.isPeeled()); - assertNull(tag.getPeeledObjectId()); - - assertEquals(v1_0, cur.getObjectId()); - assertTrue(cur.isSymbolic()); - assertFalse(cur.isPeeled()); - assertNull(cur.getPeeledObjectId()); - - final Ref tag_p = refdir.peel(tag); - final Ref cur_p = refdir.peel(cur); - - assertNotSame(tag, tag_p); - assertFalse(tag_p.isSymbolic()); - assertTrue(tag_p.isPeeled()); - assertEquals(v1_0, tag_p.getObjectId()); - assertEquals(v1_0.getObject(), tag_p.getPeeledObjectId()); - assertSame(tag_p, refdir.peel(tag_p)); - - assertNotSame(cur, cur_p); - assertEquals("refs/tags/current", cur_p.getName()); - assertTrue(cur_p.isSymbolic()); - assertEquals("refs/tags/v1_0", cur_p.getTarget().getName()); - assertTrue(cur_p.isPeeled()); - assertEquals(v1_0, cur_p.getObjectId()); - assertEquals(v1_0.getObject(), cur_p.getPeeledObjectId()); - - // reuses cached peeling later, but not immediately due to - // the implementation so we have to fetch it once. - final Ref tag_p2 = refdir.getRef("refs/tags/v1_0"); - assertFalse(tag_p2.isSymbolic()); - assertTrue(tag_p2.isPeeled()); - assertEquals(v1_0, tag_p2.getObjectId()); - assertEquals(v1_0.getObject(), tag_p2.getPeeledObjectId()); - - assertSame(tag_p2, refdir.getRef("refs/tags/v1_0")); - assertSame(tag_p2, refdir.getRef("refs/tags/current").getTarget()); - assertSame(tag_p2, refdir.peel(tag_p2)); - } - - public void testPeelCommit() throws IOException { - writeLooseRef("refs/heads/master", A); - - Ref master = refdir.getRef("refs/heads/master"); - assertEquals(A, master.getObjectId()); - assertFalse(master.isPeeled()); - assertNull(master.getPeeledObjectId()); - - Ref master_p = refdir.peel(master); - assertNotSame(master, master_p); - assertEquals(A, master_p.getObjectId()); - assertTrue(master_p.isPeeled()); - assertNull(master_p.getPeeledObjectId()); - - // reuses cached peeling later, but not immediately due to - // the implementation so we have to fetch it once. - Ref master_p2 = refdir.getRef("refs/heads/master"); - assertNotSame(master, master_p2); - assertEquals(A, master_p2.getObjectId()); - assertTrue(master_p2.isPeeled()); - assertNull(master_p2.getPeeledObjectId()); - assertSame(master_p2, refdir.peel(master_p2)); - } - - private void writeLooseRef(String name, AnyObjectId id) throws IOException { - writeLooseRef(name, id.name() + "\n"); - } - - private void writeLooseRef(String name, String content) throws IOException { - write(new File(diskRepo.getDirectory(), name), content); - } - - private void writePackedRef(String name, AnyObjectId id) throws IOException { - writePackedRefs(id.name() + " " + name + "\n"); - } - - private void writePackedRefs(String content) throws IOException { - File pr = new File(diskRepo.getDirectory(), "packed-refs"); - write(pr, content); - } - - private void deleteLooseRef(String name) { - File path = new File(diskRepo.getDirectory(), name); - assertTrue("deleted " + name, path.delete()); - } - - /** - * Kick the timestamp of a local file. - *

- * We shouldn't have to make these method calls. The cache is using file - * system timestamps, and on many systems unit tests run faster than the - * modification clock. Dumping the cache after we make an edit behind - * RefDirectory's back allows the tests to pass. - * - * @param name - * the file in the repository to force a time change on. - */ - private void BUG_WorkAroundRacyGitIssues(String name) { - File path = new File(diskRepo.getDirectory(), name); - long old = path.lastModified(); - long set = 1250379778668L; // Sat Aug 15 20:12:58 GMT-03:30 2009 - path.setLastModified(set); - assertTrue("time changed", old != path.lastModified()); - } -} diff --git a/org.eclipse.jgit.test/tst/org/eclipse/jgit/lib/RefUpdateTest.java b/org.eclipse.jgit.test/tst/org/eclipse/jgit/lib/RefUpdateTest.java deleted file mode 100644 index 161bd3eff1..0000000000 --- a/org.eclipse.jgit.test/tst/org/eclipse/jgit/lib/RefUpdateTest.java +++ /dev/null @@ -1,840 +0,0 @@ -/* - * Copyright (C) 2008, Charles O'Farrell - * Copyright (C) 2009-2010, Google Inc. - * Copyright (C) 2008-2009, Robin Rosenberg - * 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.IOException; -import java.util.List; -import java.util.Map; -import java.util.Map.Entry; - -import org.eclipse.jgit.lib.RefUpdate.Result; -import org.eclipse.jgit.revwalk.RevCommit; -import org.eclipse.jgit.revwalk.RevWalk; - -public class RefUpdateTest extends SampleDataRepositoryTestCase { - - private void writeSymref(String src, String dst) throws IOException { - RefUpdate u = db.updateRef(src); - switch (u.link(dst)) { - case NEW: - case FORCED: - case NO_CHANGE: - break; - default: - fail("link " + src + " to " + dst); - } - } - - private RefUpdate updateRef(final String name) throws IOException { - final RefUpdate ref = db.updateRef(name); - ref.setNewObjectId(db.resolve(Constants.HEAD)); - return ref; - } - - private void delete(final RefUpdate ref, final Result expected) - throws IOException { - delete(ref, expected, true, true); - } - - private void delete(final RefUpdate ref, final Result expected, - final boolean exists, final boolean removed) throws IOException { - assertEquals(exists, db.getAllRefs().containsKey(ref.getName())); - assertEquals(expected, ref.delete()); - assertEquals(!removed, db.getAllRefs().containsKey(ref.getName())); - } - - public void testNoCacheObjectIdSubclass() throws IOException { - final String newRef = "refs/heads/abc"; - final RefUpdate ru = updateRef(newRef); - final RevCommit newid = new RevCommit(ru.getNewObjectId()) { - // empty - }; - ru.setNewObjectId(newid); - Result update = ru.update(); - assertEquals(Result.NEW, update); - final Ref r = db.getAllRefs().get(newRef); - assertNotNull(r); - assertEquals(newRef, r.getName()); - assertNotNull(r.getObjectId()); - assertNotSame(newid, r.getObjectId()); - assertSame(ObjectId.class, r.getObjectId().getClass()); - assertEquals(newid.copy(), r.getObjectId()); - List reverseEntries1 = db.getReflogReader("refs/heads/abc").getReverseEntries(); - org.eclipse.jgit.lib.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 reverseEntries2 = db.getReflogReader("HEAD").getReverseEntries(); - assertEquals(0, reverseEntries2.size()); - } - - public void testNewNamespaceConflictWithLoosePrefixNameExists() - throws IOException { - final String newRef = "refs/heads/z"; - final RefUpdate ru = updateRef(newRef); - final RevCommit newid = new RevCommit(ru.getNewObjectId()) { - // empty - }; - ru.setNewObjectId(newid); - Result update = ru.update(); - assertEquals(Result.NEW, update); - // end setup - final String newRef2 = "refs/heads/z/a"; - final RefUpdate ru2 = updateRef(newRef2); - final RevCommit newid2 = new RevCommit(ru2.getNewObjectId()) { - // empty - }; - ru.setNewObjectId(newid2); - Result update2 = ru2.update(); - assertEquals(Result.LOCK_FAILURE, update2); - assertEquals(1, db.getReflogReader("refs/heads/z").getReverseEntries().size()); - assertEquals(0, db.getReflogReader("HEAD").getReverseEntries().size()); - } - - public void testNewNamespaceConflictWithPackedPrefixNameExists() - throws IOException { - final String newRef = "refs/heads/master/x"; - final RefUpdate ru = updateRef(newRef); - final RevCommit newid = new RevCommit(ru.getNewObjectId()) { - // empty - }; - ru.setNewObjectId(newid); - Result update = ru.update(); - assertEquals(Result.LOCK_FAILURE, update); - assertNull(db.getReflogReader("refs/heads/master/x")); - assertEquals(0, db.getReflogReader("HEAD").getReverseEntries().size()); - } - - public void testNewNamespaceConflictWithLoosePrefixOfExisting() - throws IOException { - final String newRef = "refs/heads/z/a"; - final RefUpdate ru = updateRef(newRef); - final RevCommit newid = new RevCommit(ru.getNewObjectId()) { - // empty - }; - ru.setNewObjectId(newid); - Result update = ru.update(); - assertEquals(Result.NEW, update); - // end setup - final String newRef2 = "refs/heads/z"; - final RefUpdate ru2 = updateRef(newRef2); - final RevCommit newid2 = new RevCommit(ru2.getNewObjectId()) { - // empty - }; - ru.setNewObjectId(newid2); - Result update2 = ru2.update(); - assertEquals(Result.LOCK_FAILURE, update2); - assertEquals(1, db.getReflogReader("refs/heads/z/a").getReverseEntries().size()); - assertNull(db.getReflogReader("refs/heads/z")); - assertEquals(0, db.getReflogReader("HEAD").getReverseEntries().size()); - } - - public void testNewNamespaceConflictWithPackedPrefixOfExisting() - throws IOException { - final String newRef = "refs/heads/prefix"; - final RefUpdate ru = updateRef(newRef); - final RevCommit newid = new RevCommit(ru.getNewObjectId()) { - // empty - }; - ru.setNewObjectId(newid); - Result update = ru.update(); - assertEquals(Result.LOCK_FAILURE, update); - assertNull(db.getReflogReader("refs/heads/prefix")); - assertEquals(0, db.getReflogReader("HEAD").getReverseEntries().size()); - } - - /** - * Delete a ref that is pointed to by HEAD - * - * @throws IOException - */ - public void testDeleteHEADreferencedRef() throws IOException { - ObjectId pid = db.resolve("refs/heads/master^"); - RefUpdate updateRef = db.updateRef("refs/heads/master"); - updateRef.setNewObjectId(pid); - updateRef.setForceUpdate(true); - Result update = updateRef.update(); - assertEquals(Result.FORCED, update); // internal - - RefUpdate updateRef2 = db.updateRef("refs/heads/master"); - Result delete = updateRef2.delete(); - assertEquals(Result.REJECTED_CURRENT_BRANCH, delete); - assertEquals(pid, db.resolve("refs/heads/master")); - assertEquals(1,db.getReflogReader("refs/heads/master").getReverseEntries().size()); - assertEquals(0,db.getReflogReader("HEAD").getReverseEntries().size()); - } - - public void testLooseDelete() throws IOException { - final String newRef = "refs/heads/abc"; - RefUpdate ref = updateRef(newRef); - ref.update(); // create loose ref - ref = updateRef(newRef); // refresh - delete(ref, Result.NO_CHANGE); - assertNull(db.getReflogReader("refs/heads/abc")); - } - - public void testDeleteHead() throws IOException { - final RefUpdate ref = updateRef(Constants.HEAD); - delete(ref, Result.REJECTED_CURRENT_BRANCH, true, false); - assertEquals(0, db.getReflogReader("refs/heads/master").getReverseEntries().size()); - assertEquals(0, db.getReflogReader("HEAD").getReverseEntries().size()); - } - - /** - * Delete a loose ref and make sure the directory in refs is deleted too, - * and the reflog dir too - * - * @throws IOException - */ - public void testDeleteLooseAndItsDirectory() throws IOException { - ObjectId pid = db.resolve("refs/heads/c^"); - RefUpdate updateRef = db.updateRef("refs/heads/z/c"); - updateRef.setNewObjectId(pid); - updateRef.setForceUpdate(true); - updateRef.setRefLogMessage("new test ref", false); - Result update = updateRef.update(); - assertEquals(Result.NEW, update); // internal - assertTrue(new File(db.getDirectory(), Constants.R_HEADS + "z") - .exists()); - assertTrue(new File(db.getDirectory(), "logs/refs/heads/z").exists()); - - // The real test here - RefUpdate updateRef2 = db.updateRef("refs/heads/z/c"); - updateRef2.setForceUpdate(true); - Result delete = updateRef2.delete(); - assertEquals(Result.FORCED, delete); - assertNull(db.resolve("refs/heads/z/c")); - assertFalse(new File(db.getDirectory(), Constants.R_HEADS + "z") - .exists()); - assertFalse(new File(db.getDirectory(), "logs/refs/heads/z").exists()); - } - - public void testDeleteNotFound() throws IOException { - final RefUpdate ref = updateRef("refs/heads/xyz"); - delete(ref, Result.NEW, false, true); - } - - public void testDeleteFastForward() throws IOException { - final RefUpdate ref = updateRef("refs/heads/a"); - delete(ref, Result.FAST_FORWARD); - } - - public void testDeleteForce() throws IOException { - final RefUpdate ref = db.updateRef("refs/heads/b"); - ref.setNewObjectId(db.resolve("refs/heads/a")); - delete(ref, Result.REJECTED, true, false); - ref.setForceUpdate(true); - delete(ref, Result.FORCED); - } - - public void testRefKeySameAsName() { - Map allRefs = db.getAllRefs(); - for (Entry e : allRefs.entrySet()) { - assertEquals(e.getKey(), e.getValue().getName()); - - } - } - - /** - * Try modify a ref forward, fast forward - * - * @throws IOException - */ - public void testUpdateRefForward() throws IOException { - ObjectId ppid = db.resolve("refs/heads/master^"); - ObjectId pid = db.resolve("refs/heads/master"); - - RefUpdate updateRef = db.updateRef("refs/heads/master"); - updateRef.setNewObjectId(ppid); - updateRef.setForceUpdate(true); - Result update = updateRef.update(); - assertEquals(Result.FORCED, update); - assertEquals(ppid, db.resolve("refs/heads/master")); - - // real test - RefUpdate updateRef2 = db.updateRef("refs/heads/master"); - updateRef2.setNewObjectId(pid); - Result update2 = updateRef2.update(); - assertEquals(Result.FAST_FORWARD, update2); - assertEquals(pid, db.resolve("refs/heads/master")); - } - - /** - * Update the HEAD ref. Only it should be changed, not what it points to. - * - * @throws Exception - */ - public void testUpdateRefDetached() throws Exception { - ObjectId pid = db.resolve("refs/heads/master"); - ObjectId ppid = db.resolve("refs/heads/master^"); - RefUpdate updateRef = db.updateRef("HEAD", true); - updateRef.setForceUpdate(true); - updateRef.setNewObjectId(ppid); - Result update = updateRef.update(); - assertEquals(Result.FORCED, update); - assertEquals(ppid, db.resolve("HEAD")); - Ref ref = db.getRef("HEAD"); - assertEquals("HEAD", ref.getName()); - assertTrue("is detached", !ref.isSymbolic()); - - // 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); - assertEquals(pid, e.getOldId()); - assertEquals(ppid, e.getNewId()); - assertEquals("GIT_COMMITTER_EMAIL", e.getWho().getEmailAddress()); - assertEquals("GIT_COMMITTER_NAME", e.getWho().getName()); - assertEquals(1250379778000L, e.getWho().getWhen().getTime()); - } - - /** - * Update the HEAD ref when the referenced branch is unborn - * - * @throws Exception - */ - public void testUpdateRefDetachedUnbornHead() throws Exception { - ObjectId ppid = db.resolve("refs/heads/master^"); - writeSymref("HEAD", "refs/heads/unborn"); - RefUpdate updateRef = db.updateRef("HEAD", true); - updateRef.setForceUpdate(true); - updateRef.setNewObjectId(ppid); - Result update = updateRef.update(); - assertEquals(Result.NEW, update); - assertEquals(ppid, db.resolve("HEAD")); - Ref ref = db.getRef("HEAD"); - assertEquals("HEAD", ref.getName()); - assertTrue("is detached", !ref.isSymbolic()); - - // 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); - assertEquals(ObjectId.zeroId(), e.getOldId()); - assertEquals(ppid, e.getNewId()); - assertEquals("GIT_COMMITTER_EMAIL", e.getWho().getEmailAddress()); - assertEquals("GIT_COMMITTER_NAME", e.getWho().getName()); - assertEquals(1250379778000L, e.getWho().getWhen().getTime()); - } - - /** - * Delete a ref that exists both as packed and loose. Make sure the ref - * cannot be resolved after delete. - * - * @throws IOException - */ - public void testDeleteLoosePacked() throws IOException { - ObjectId pid = db.resolve("refs/heads/c^"); - RefUpdate updateRef = db.updateRef("refs/heads/c"); - updateRef.setNewObjectId(pid); - updateRef.setForceUpdate(true); - Result update = updateRef.update(); - assertEquals(Result.FORCED, update); // internal - - // The real test here - RefUpdate updateRef2 = db.updateRef("refs/heads/c"); - updateRef2.setForceUpdate(true); - Result delete = updateRef2.delete(); - assertEquals(Result.FORCED, delete); - assertNull(db.resolve("refs/heads/c")); - } - - /** - * Try modify a ref to same - * - * @throws IOException - */ - public void testUpdateRefNoChange() throws IOException { - ObjectId pid = db.resolve("refs/heads/master"); - RefUpdate updateRef = db.updateRef("refs/heads/master"); - updateRef.setNewObjectId(pid); - Result update = updateRef.update(); - assertEquals(Result.NO_CHANGE, update); - assertEquals(pid, db.resolve("refs/heads/master")); - } - - /** - * Test case originating from - * bug 285991 - * - * Make sure the in memory cache is updated properly after - * update of symref. This one did not fail because the - * ref was packed due to implementation issues. - * - * @throws Exception - */ - public void testRefsCacheAfterUpdate() throws Exception { - // Do not use the defalt repo for this case. - Map allRefs = db.getAllRefs(); - ObjectId oldValue = db.resolve("HEAD"); - ObjectId newValue = db.resolve("HEAD^"); - // first make HEAD refer to loose ref - RefUpdate updateRef = db.updateRef(Constants.HEAD); - updateRef.setForceUpdate(true); - updateRef.setNewObjectId(newValue); - Result update = updateRef.update(); - assertEquals(Result.FORCED, update); - - // now update that ref - updateRef = db.updateRef(Constants.HEAD); - updateRef.setForceUpdate(true); - updateRef.setNewObjectId(oldValue); - update = updateRef.update(); - assertEquals(Result.FAST_FORWARD, update); - - allRefs = db.getAllRefs(); - Ref master = allRefs.get("refs/heads/master"); - Ref head = allRefs.get("HEAD"); - assertEquals("refs/heads/master", master.getName()); - assertEquals("HEAD", head.getName()); - assertTrue("is symbolic reference", head.isSymbolic()); - assertSame(master, head.getTarget()); - } - - /** - * Test case originating from - * bug 285991 - * - * Make sure the in memory cache is updated properly after - * update of symref. - * - * @throws Exception - */ - public void testRefsCacheAfterUpdateLooseOnly() throws Exception { - // Do not use the defalt repo for this case. - Map allRefs = db.getAllRefs(); - ObjectId oldValue = db.resolve("HEAD"); - writeSymref(Constants.HEAD, "refs/heads/newref"); - RefUpdate updateRef = db.updateRef(Constants.HEAD); - updateRef.setForceUpdate(true); - updateRef.setNewObjectId(oldValue); - Result update = updateRef.update(); - assertEquals(Result.NEW, update); - - allRefs = db.getAllRefs(); - Ref head = allRefs.get("HEAD"); - Ref newref = allRefs.get("refs/heads/newref"); - assertEquals("refs/heads/newref", newref.getName()); - assertEquals("HEAD", head.getName()); - assertTrue("is symbolic reference", head.isSymbolic()); - assertSame(newref, head.getTarget()); - } - - /** - * Try modify a ref, but get wrong expected old value - * - * @throws IOException - */ - public void testUpdateRefLockFailureWrongOldValue() throws IOException { - ObjectId pid = db.resolve("refs/heads/master"); - RefUpdate updateRef = db.updateRef("refs/heads/master"); - updateRef.setNewObjectId(pid); - updateRef.setExpectedOldObjectId(db.resolve("refs/heads/master^")); - Result update = updateRef.update(); - assertEquals(Result.LOCK_FAILURE, update); - assertEquals(pid, db.resolve("refs/heads/master")); - } - - /** - * Try modify a ref forward, fast forward, checking old value first - * - * @throws IOException - */ - public void testUpdateRefForwardWithCheck1() throws IOException { - ObjectId ppid = db.resolve("refs/heads/master^"); - ObjectId pid = db.resolve("refs/heads/master"); - - RefUpdate updateRef = db.updateRef("refs/heads/master"); - updateRef.setNewObjectId(ppid); - updateRef.setForceUpdate(true); - Result update = updateRef.update(); - assertEquals(Result.FORCED, update); - assertEquals(ppid, db.resolve("refs/heads/master")); - - // real test - RefUpdate updateRef2 = db.updateRef("refs/heads/master"); - updateRef2.setExpectedOldObjectId(ppid); - updateRef2.setNewObjectId(pid); - Result update2 = updateRef2.update(); - assertEquals(Result.FAST_FORWARD, update2); - assertEquals(pid, db.resolve("refs/heads/master")); - } - - /** - * Try modify a ref forward, fast forward, checking old commit first - * - * @throws IOException - */ - public void testUpdateRefForwardWithCheck2() throws IOException { - ObjectId ppid = db.resolve("refs/heads/master^"); - ObjectId pid = db.resolve("refs/heads/master"); - - RefUpdate updateRef = db.updateRef("refs/heads/master"); - updateRef.setNewObjectId(ppid); - updateRef.setForceUpdate(true); - Result update = updateRef.update(); - assertEquals(Result.FORCED, update); - assertEquals(ppid, db.resolve("refs/heads/master")); - - // real test - RevCommit old = new RevWalk(db).parseCommit(ppid); - RefUpdate updateRef2 = db.updateRef("refs/heads/master"); - updateRef2.setExpectedOldObjectId(old); - updateRef2.setNewObjectId(pid); - Result update2 = updateRef2.update(); - assertEquals(Result.FAST_FORWARD, update2); - assertEquals(pid, db.resolve("refs/heads/master")); - } - - /** - * Try modify a ref that is locked - * - * @throws IOException - */ - public void testUpdateRefLockFailureLocked() throws IOException { - ObjectId opid = db.resolve("refs/heads/master"); - ObjectId pid = db.resolve("refs/heads/master^"); - RefUpdate updateRef = db.updateRef("refs/heads/master"); - updateRef.setNewObjectId(pid); - LockFile lockFile1 = new LockFile(new File(db.getDirectory(),"refs/heads/master")); - try { - assertTrue(lockFile1.lock()); // precondition to test - Result update = updateRef.update(); - assertEquals(Result.LOCK_FAILURE, update); - assertEquals(opid, db.resolve("refs/heads/master")); - LockFile lockFile2 = new LockFile(new File(db.getDirectory(),"refs/heads/master")); - assertFalse(lockFile2.lock()); // was locked, still is - } finally { - lockFile1.unlock(); - } - } - - /** - * Try to delete a ref. Delete requires force. - * - * @throws IOException - */ - public void testDeleteLoosePackedRejected() throws IOException { - ObjectId pid = db.resolve("refs/heads/c^"); - ObjectId oldpid = db.resolve("refs/heads/c"); - RefUpdate updateRef = db.updateRef("refs/heads/c"); - updateRef.setNewObjectId(pid); - Result update = updateRef.update(); - assertEquals(Result.REJECTED, update); - assertEquals(oldpid, db.resolve("refs/heads/c")); - } - - public void testRenameBranchNoPreviousLog() throws IOException { - assertFalse("precondition, no log on old branchg", new File(db - .getDirectory(), "logs/refs/heads/b").exists()); - ObjectId rb = db.resolve("refs/heads/b"); - ObjectId oldHead = db.resolve(Constants.HEAD); - assertFalse(rb.equals(oldHead)); // assumption for this test - RefRename renameRef = db.renameRef("refs/heads/b", - "refs/heads/new/name"); - Result result = renameRef.rename(); - assertEquals(Result.RENAMED, result); - assertEquals(rb, db.resolve("refs/heads/new/name")); - assertNull(db.resolve("refs/heads/b")); - assertEquals(1, db.getReflogReader("new/name").getReverseEntries().size()); - assertEquals("Branch: renamed b to new/name", db.getReflogReader("new/name") - .getLastEntry().getComment()); - assertFalse(new File(db.getDirectory(), "logs/refs/heads/b").exists()); - assertEquals(oldHead, db.resolve(Constants.HEAD)); // unchanged - } - - public void testRenameBranchHasPreviousLog() throws IOException { - ObjectId rb = db.resolve("refs/heads/b"); - ObjectId oldHead = db.resolve(Constants.HEAD); - assertFalse("precondition for this test, branch b != HEAD", rb - .equals(oldHead)); - writeReflog(db, rb, rb, "Just a message", "refs/heads/b"); - assertTrue("log on old branch", new File(db.getDirectory(), - "logs/refs/heads/b").exists()); - RefRename renameRef = db.renameRef("refs/heads/b", - "refs/heads/new/name"); - Result result = renameRef.rename(); - assertEquals(Result.RENAMED, result); - assertEquals(rb, db.resolve("refs/heads/new/name")); - assertNull(db.resolve("refs/heads/b")); - assertEquals(2, db.getReflogReader("new/name").getReverseEntries().size()); - assertEquals("Branch: renamed b to new/name", db.getReflogReader("new/name") - .getLastEntry().getComment()); - assertEquals("Just a message", db.getReflogReader("new/name") - .getReverseEntries().get(1).getComment()); - assertFalse(new File(db.getDirectory(), "logs/refs/heads/b").exists()); - assertEquals(oldHead, db.resolve(Constants.HEAD)); // unchanged - } - - public void testRenameCurrentBranch() throws IOException { - ObjectId rb = db.resolve("refs/heads/b"); - writeSymref(Constants.HEAD, "refs/heads/b"); - ObjectId oldHead = db.resolve(Constants.HEAD); - assertTrue("internal test condition, b == HEAD", rb.equals(oldHead)); - writeReflog(db, rb, rb, "Just a message", "refs/heads/b"); - assertTrue("log on old branch", new File(db.getDirectory(), - "logs/refs/heads/b").exists()); - RefRename renameRef = db.renameRef("refs/heads/b", - "refs/heads/new/name"); - Result result = renameRef.rename(); - assertEquals(Result.RENAMED, result); - assertEquals(rb, db.resolve("refs/heads/new/name")); - assertNull(db.resolve("refs/heads/b")); - assertEquals("Branch: renamed b to new/name", db.getReflogReader( - "new/name").getLastEntry().getComment()); - assertFalse(new File(db.getDirectory(), "logs/refs/heads/b").exists()); - assertEquals(rb, db.resolve(Constants.HEAD)); - assertEquals(2, db.getReflogReader("new/name").getReverseEntries().size()); - assertEquals("Branch: renamed b to new/name", db.getReflogReader("new/name").getReverseEntries().get(0).getComment()); - assertEquals("Just a message", db.getReflogReader("new/name").getReverseEntries().get(1).getComment()); - } - - public void testRenameBranchAlsoInPack() throws IOException { - ObjectId rb = db.resolve("refs/heads/b"); - ObjectId rb2 = db.resolve("refs/heads/b~1"); - assertEquals(Ref.Storage.PACKED, db.getRef("refs/heads/b").getStorage()); - RefUpdate updateRef = db.updateRef("refs/heads/b"); - updateRef.setNewObjectId(rb2); - updateRef.setForceUpdate(true); - Result update = updateRef.update(); - assertEquals("internal check new ref is loose", Result.FORCED, update); - assertEquals(Ref.Storage.LOOSE, db.getRef("refs/heads/b").getStorage()); - writeReflog(db, rb, rb, "Just a message", "refs/heads/b"); - assertTrue("log on old branch", new File(db.getDirectory(), - "logs/refs/heads/b").exists()); - RefRename renameRef = db.renameRef("refs/heads/b", - "refs/heads/new/name"); - Result result = renameRef.rename(); - assertEquals(Result.RENAMED, result); - assertEquals(rb2, db.resolve("refs/heads/new/name")); - assertNull(db.resolve("refs/heads/b")); - assertEquals("Branch: renamed b to new/name", db.getReflogReader( - "new/name").getLastEntry().getComment()); - assertEquals(3, db.getReflogReader("refs/heads/new/name").getReverseEntries().size()); - assertEquals("Branch: renamed b to new/name", db.getReflogReader("refs/heads/new/name").getReverseEntries().get(0).getComment()); - assertEquals(0, db.getReflogReader("HEAD").getReverseEntries().size()); - // make sure b's log file is gone too. - assertFalse(new File(db.getDirectory(), "logs/refs/heads/b").exists()); - - // Create new Repository instance, to reread caches and make sure our - // assumptions are persistent. - Repository ndb = new FileRepository(db.getDirectory()); - assertEquals(rb2, ndb.resolve("refs/heads/new/name")); - assertNull(ndb.resolve("refs/heads/b")); - } - - public void tryRenameWhenLocked(String toLock, String fromName, - String toName, String headPointsTo) throws IOException { - // setup - writeSymref(Constants.HEAD, headPointsTo); - ObjectId oldfromId = db.resolve(fromName); - ObjectId oldHeadId = db.resolve(Constants.HEAD); - writeReflog(db, oldfromId, oldfromId, "Just a message", - fromName); - List oldFromLog = db - .getReflogReader(fromName).getReverseEntries(); - List oldHeadLog = oldHeadId != null ? db - .getReflogReader(Constants.HEAD).getReverseEntries() : null; - - assertTrue("internal check, we have a log", new File(db.getDirectory(), - "logs/" + fromName).exists()); - - // "someone" has branch X locked - LockFile lockFile = new LockFile(new File(db.getDirectory(), toLock)); - try { - assertTrue(lockFile.lock()); - - // Now this is our test - RefRename renameRef = db.renameRef(fromName, toName); - Result result = renameRef.rename(); - assertEquals(Result.LOCK_FAILURE, result); - - // Check that the involved refs are the same despite the failure - assertExists(false, toName); - if (!toLock.equals(toName)) - assertExists(false, toName + ".lock"); - assertExists(true, toLock + ".lock"); - if (!toLock.equals(fromName)) - assertExists(false, "logs/" + fromName + ".lock"); - assertExists(false, "logs/" + toName + ".lock"); - assertEquals(oldHeadId, db.resolve(Constants.HEAD)); - assertEquals(oldfromId, db.resolve(fromName)); - assertNull(db.resolve(toName)); - assertEquals(oldFromLog.toString(), db.getReflogReader(fromName) - .getReverseEntries().toString()); - if (oldHeadId != null) - assertEquals(oldHeadLog.toString(), db.getReflogReader( - Constants.HEAD).getReverseEntries().toString()); - } finally { - lockFile.unlock(); - } - } - - private void assertExists(boolean positive, String toName) { - assertEquals(toName + (positive ? " " : " does not ") + "exist", - positive, new File(db.getDirectory(), toName).exists()); - } - - public void testRenameBranchCannotLockAFileHEADisFromLockHEAD() - throws IOException { - tryRenameWhenLocked("HEAD", "refs/heads/b", "refs/heads/new/name", - "refs/heads/b"); - } - - public void testRenameBranchCannotLockAFileHEADisFromLockFrom() - throws IOException { - tryRenameWhenLocked("refs/heads/b", "refs/heads/b", - "refs/heads/new/name", "refs/heads/b"); - } - - public void testRenameBranchCannotLockAFileHEADisFromLockTo() - throws IOException { - tryRenameWhenLocked("refs/heads/new/name", "refs/heads/b", - "refs/heads/new/name", "refs/heads/b"); - } - - public void testRenameBranchCannotLockAFileHEADisToLockFrom() - throws IOException { - tryRenameWhenLocked("refs/heads/b", "refs/heads/b", - "refs/heads/new/name", "refs/heads/new/name"); - } - - public void testRenameBranchCannotLockAFileHEADisToLockTo() - throws IOException { - tryRenameWhenLocked("refs/heads/new/name", "refs/heads/b", - "refs/heads/new/name", "refs/heads/new/name"); - } - - public void testRenameBranchCannotLockAFileHEADisOtherLockFrom() - throws IOException { - tryRenameWhenLocked("refs/heads/b", "refs/heads/b", - "refs/heads/new/name", "refs/heads/a"); - } - - public void testRenameBranchCannotLockAFileHEADisOtherLockTo() - throws IOException { - tryRenameWhenLocked("refs/heads/new/name", "refs/heads/b", - "refs/heads/new/name", "refs/heads/a"); - } - - public void testRenameRefNameColission1avoided() throws IOException { - // setup - ObjectId rb = db.resolve("refs/heads/b"); - writeSymref(Constants.HEAD, "refs/heads/a"); - RefUpdate updateRef = db.updateRef("refs/heads/a"); - updateRef.setNewObjectId(rb); - updateRef.setRefLogMessage("Setup", false); - assertEquals(Result.FAST_FORWARD, updateRef.update()); - ObjectId oldHead = db.resolve(Constants.HEAD); - assertTrue(rb.equals(oldHead)); // assumption for this test - writeReflog(db, rb, rb, "Just a message", "refs/heads/a"); - assertTrue("internal check, we have a log", new File(db.getDirectory(), - "logs/refs/heads/a").exists()); - - // Now this is our test - RefRename renameRef = db.renameRef("refs/heads/a", "refs/heads/a/b"); - Result result = renameRef.rename(); - assertEquals(Result.RENAMED, result); - assertNull(db.resolve("refs/heads/a")); - assertEquals(rb, db.resolve("refs/heads/a/b")); - assertEquals(3, db.getReflogReader("a/b").getReverseEntries().size()); - assertEquals("Branch: renamed a to a/b", db.getReflogReader("a/b") - .getReverseEntries().get(0).getComment()); - assertEquals("Just a message", db.getReflogReader("a/b") - .getReverseEntries().get(1).getComment()); - assertEquals("Setup", db.getReflogReader("a/b").getReverseEntries() - .get(2).getComment()); - // same thing was logged to HEAD - assertEquals("Branch: renamed a to a/b", db.getReflogReader("HEAD") - .getReverseEntries().get(0).getComment()); - } - - public void testRenameRefNameColission2avoided() throws IOException { - // setup - ObjectId rb = db.resolve("refs/heads/b"); - writeSymref(Constants.HEAD, "refs/heads/prefix/a"); - RefUpdate updateRef = db.updateRef("refs/heads/prefix/a"); - updateRef.setNewObjectId(rb); - updateRef.setRefLogMessage("Setup", false); - updateRef.setForceUpdate(true); - assertEquals(Result.FORCED, updateRef.update()); - ObjectId oldHead = db.resolve(Constants.HEAD); - assertTrue(rb.equals(oldHead)); // assumption for this test - writeReflog(db, rb, rb, "Just a message", - "refs/heads/prefix/a"); - assertTrue("internal check, we have a log", new File(db.getDirectory(), - "logs/refs/heads/prefix/a").exists()); - - // Now this is our test - RefRename renameRef = db.renameRef("refs/heads/prefix/a", - "refs/heads/prefix"); - Result result = renameRef.rename(); - assertEquals(Result.RENAMED, result); - - assertNull(db.resolve("refs/heads/prefix/a")); - assertEquals(rb, db.resolve("refs/heads/prefix")); - assertEquals(3, db.getReflogReader("prefix").getReverseEntries().size()); - assertEquals("Branch: renamed prefix/a to prefix", db.getReflogReader( - "prefix").getReverseEntries().get(0).getComment()); - assertEquals("Just a message", db.getReflogReader("prefix") - .getReverseEntries().get(1).getComment()); - assertEquals("Setup", db.getReflogReader("prefix").getReverseEntries() - .get(2).getComment()); - assertEquals("Branch: renamed prefix/a to prefix", db.getReflogReader( - "HEAD").getReverseEntries().get(0).getComment()); - } - - private void writeReflog(Repository db, ObjectId oldId, ObjectId newId, - String msg, String refName) throws IOException { - RefDirectory refs = (RefDirectory) db.getRefDatabase(); - RefDirectoryUpdate update = refs.newUpdate(refName, true); - update.setNewObjectId(newId); - refs.log(update, msg, true); - } -} diff --git a/org.eclipse.jgit.test/tst/org/eclipse/jgit/lib/ReflogReaderTest.java b/org.eclipse.jgit.test/tst/org/eclipse/jgit/lib/ReflogReaderTest.java deleted file mode 100644 index 6144851fcd..0000000000 --- a/org.eclipse.jgit.test/tst/org/eclipse/jgit/lib/ReflogReaderTest.java +++ /dev/null @@ -1,198 +0,0 @@ -/* - * Copyright (C) 2009, Robin Rosenberg - * Copyright (C) 2009, Robin Rosenberg - * 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.FileOutputStream; -import java.io.IOException; -import java.text.SimpleDateFormat; -import java.util.List; - -import org.eclipse.jgit.lib.ReflogReader.Entry; - -public class ReflogReaderTest extends SampleDataRepositoryTestCase { - - static byte[] oneLine = "da85355dfc525c9f6f3927b876f379f46ccf826e 3e7549db262d1e836d9bf0af7e22355468f1717c A O Thor Too 1243028200 +0200\tcommit: Add a toString for debugging to RemoteRefUpdate\n" - .getBytes(); - - static byte[] twoLine = ("0000000000000000000000000000000000000000 c6734895958052a9dbc396cff4459dc1a25029ab A U Thor 1243028201 -0100\tbranch: Created from rr/renamebranchv4\n" - + "c6734895958052a9dbc396cff4459dc1a25029ab 54794942a18a237c57a80719afed44bb78172b10 Same A U Thor 1243028202 +0100\trebase finished: refs/heads/rr/renamebranch5 onto c6e3b9fe2da0293f11eae202ec35fb343191a82d\n") - .getBytes(); - - static byte[] twoLineWithAppendInProgress = ("0000000000000000000000000000000000000000 c6734895958052a9dbc396cff4459dc1a25029ab A U Thor 1243028201 -0100\tbranch: Created from rr/renamebranchv4\n" - + "c6734895958052a9dbc396cff4459dc1a25029ab 54794942a18a237c57a80719afed44bb78172b10 Same A U Thor 1243028202 +0100\trebase finished: refs/heads/rr/renamebranch5 onto c6e3b9fe2da0293f11eae202ec35fb343191a82d\n" - + "54794942a18a237c57a80719afed44bb78172b10 ") - .getBytes(); - - static byte[] aLine = "1111111111111111111111111111111111111111 3e7549db262d1e836d9bf0af7e22355468f1717c A U Thor 1243028201 -0100\tbranch: change to a\n" - .getBytes(); - - static byte[] masterLine = "2222222222222222222222222222222222222222 3e7549db262d1e836d9bf0af7e22355468f1717c A U Thor 1243028201 -0100\tbranch: change to master\n" - .getBytes(); - - static byte[] headLine = "3333333333333333333333333333333333333333 3e7549db262d1e836d9bf0af7e22355468f1717c A U Thor 1243028201 -0100\tbranch: change to HEAD\n" - .getBytes(); - - public void testReadOneLine() throws Exception { - setupReflog("logs/refs/heads/master", oneLine); - - ReflogReader reader = new ReflogReader(db, "refs/heads/master"); - Entry e = reader.getLastEntry(); - assertEquals(ObjectId - .fromString("da85355dfc525c9f6f3927b876f379f46ccf826e"), e - .getOldId()); - assertEquals(ObjectId - .fromString("3e7549db262d1e836d9bf0af7e22355468f1717c"), e - .getNewId()); - assertEquals("A O Thor Too", e.getWho().getName()); - assertEquals("authortoo@wri.tr", e.getWho().getEmailAddress()); - assertEquals(120, e.getWho().getTimeZoneOffset()); - assertEquals("2009-05-22T23:36:40", iso(e.getWho())); - assertEquals("commit: Add a toString for debugging to RemoteRefUpdate", - e.getComment()); - } - - private String iso(PersonIdent id) { - final SimpleDateFormat fmt; - fmt = new SimpleDateFormat("yyyy-MM-dd'T'HH:mm:ss"); - fmt.setTimeZone(id.getTimeZone()); - return fmt.format(id.getWhen()); - } - - public void testReadTwoLine() throws Exception { - setupReflog("logs/refs/heads/master", twoLine); - - ReflogReader reader = new ReflogReader(db, "refs/heads/master"); - List reverseEntries = reader.getReverseEntries(); - assertEquals(2, reverseEntries.size()); - Entry e = reverseEntries.get(0); - assertEquals(ObjectId - .fromString("c6734895958052a9dbc396cff4459dc1a25029ab"), e - .getOldId()); - assertEquals(ObjectId - .fromString("54794942a18a237c57a80719afed44bb78172b10"), e - .getNewId()); - assertEquals("Same A U Thor", e.getWho().getName()); - assertEquals("same.author@example.com", e.getWho().getEmailAddress()); - assertEquals(60, e.getWho().getTimeZoneOffset()); - assertEquals("2009-05-22T22:36:42", iso(e.getWho())); - assertEquals( - "rebase finished: refs/heads/rr/renamebranch5 onto c6e3b9fe2da0293f11eae202ec35fb343191a82d", - e.getComment()); - - e = reverseEntries.get(1); - assertEquals(ObjectId - .fromString("0000000000000000000000000000000000000000"), e - .getOldId()); - assertEquals(ObjectId - .fromString("c6734895958052a9dbc396cff4459dc1a25029ab"), e - .getNewId()); - assertEquals("A U Thor", e.getWho().getName()); - assertEquals("thor@committer.au", e.getWho().getEmailAddress()); - assertEquals(-60, e.getWho().getTimeZoneOffset()); - assertEquals("2009-05-22T20:36:41", iso(e.getWho())); - assertEquals("branch: Created from rr/renamebranchv4", e.getComment()); - } - - public void testReadWhileAppendIsInProgress() throws Exception { - setupReflog("logs/refs/heads/master", twoLineWithAppendInProgress); - ReflogReader reader = new ReflogReader(db, "refs/heads/master"); - List reverseEntries = reader.getReverseEntries(); - assertEquals(2, reverseEntries.size()); - Entry e = reverseEntries.get(0); - assertEquals(ObjectId - .fromString("c6734895958052a9dbc396cff4459dc1a25029ab"), e - .getOldId()); - assertEquals(ObjectId - .fromString("54794942a18a237c57a80719afed44bb78172b10"), e - .getNewId()); - assertEquals("Same A U Thor", e.getWho().getName()); - assertEquals("same.author@example.com", e.getWho().getEmailAddress()); - assertEquals(60, e.getWho().getTimeZoneOffset()); - assertEquals("2009-05-22T22:36:42", iso(e.getWho())); - assertEquals( - "rebase finished: refs/heads/rr/renamebranch5 onto c6e3b9fe2da0293f11eae202ec35fb343191a82d", - e.getComment()); - // while similar to testReadTwoLine, we can assume that if we get the last entry - // right, everything else is too - } - - - public void testReadRightLog() throws Exception { - setupReflog("logs/refs/heads/a", aLine); - setupReflog("logs/refs/heads/master", masterLine); - setupReflog("logs/HEAD", headLine); - assertEquals("branch: change to master", db.getReflogReader("master") - .getLastEntry().getComment()); - assertEquals("branch: change to a", db.getReflogReader("a") - .getLastEntry().getComment()); - assertEquals("branch: change to HEAD", db.getReflogReader("HEAD") - .getLastEntry().getComment()); - } - - public void testNoLog() throws Exception { - assertEquals(0, db.getReflogReader("master").getReverseEntries().size()); - assertNull(db.getReflogReader("master").getLastEntry()); - } - - private void setupReflog(String logName, byte[] data) - throws FileNotFoundException, IOException { - File logfile = new File(db.getDirectory(), logName); - if (!logfile.getParentFile().mkdirs() - && !logfile.getParentFile().isDirectory()) { - throw new IOException( - "oops, cannot create the directory for the test reflog file" - + logfile); - } - FileOutputStream fileOutputStream = new FileOutputStream(logfile); - try { - fileOutputStream.write(data); - } finally { - fileOutputStream.close(); - } - } - -} diff --git a/org.eclipse.jgit.test/tst/org/eclipse/jgit/lib/RepositorySetupWorkDirTest.java b/org.eclipse.jgit.test/tst/org/eclipse/jgit/lib/RepositorySetupWorkDirTest.java deleted file mode 100644 index 52cb46bae1..0000000000 --- a/org.eclipse.jgit.test/tst/org/eclipse/jgit/lib/RepositorySetupWorkDirTest.java +++ /dev/null @@ -1,208 +0,0 @@ -/* - * Copyright (C) 2010, Mathias Kinzler - * - * 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.IOException; - -import org.eclipse.jgit.errors.ConfigInvalidException; -import org.eclipse.jgit.junit.LocalDiskRepositoryTestCase; - -/** - * Tests for setting up the working directory when creating a Repository - */ -public class RepositorySetupWorkDirTest extends LocalDiskRepositoryTestCase { - - public void testIsBare_CreateRepositoryFromArbitraryGitDir() - throws Exception { - File gitDir = getFile("workdir"); - assertTrue(new FileRepository(gitDir).isBare()); - } - - public void testNotBare_CreateRepositoryFromDotGitGitDir() throws Exception { - File gitDir = getFile("workdir", Constants.DOT_GIT); - Repository repo = new FileRepository(gitDir); - assertFalse(repo.isBare()); - assertWorkdirPath(repo, "workdir"); - assertGitdirPath(repo, "workdir", Constants.DOT_GIT); - } - - public void testWorkdirIsParentDir_CreateRepositoryFromDotGitGitDir() - throws Exception { - File gitDir = getFile("workdir", Constants.DOT_GIT); - Repository repo = new FileRepository(gitDir); - String workdir = repo.getWorkTree().getName(); - assertEquals(workdir, "workdir"); - } - - public void testNotBare_CreateRepositoryFromWorkDirOnly() throws Exception { - File workdir = getFile("workdir", "repo"); - FileRepository repo = new FileRepositoryBuilder().setWorkTree(workdir).build(); - assertFalse(repo.isBare()); - assertWorkdirPath(repo, "workdir", "repo"); - assertGitdirPath(repo, "workdir", "repo", Constants.DOT_GIT); - } - - public void testWorkdirIsDotGit_CreateRepositoryFromWorkDirOnly() - throws Exception { - File workdir = getFile("workdir", "repo"); - FileRepository repo = new FileRepositoryBuilder().setWorkTree(workdir).build(); - assertGitdirPath(repo, "workdir", "repo", Constants.DOT_GIT); - } - - public void testNotBare_CreateRepositoryFromGitDirOnlyWithWorktreeConfig() - throws Exception { - File gitDir = getFile("workdir", "repoWithConfig"); - File workTree = getFile("workdir", "treeRoot"); - setWorkTree(gitDir, workTree); - FileRepository repo = new FileRepositoryBuilder().setGitDir(gitDir).build(); - assertFalse(repo.isBare()); - assertWorkdirPath(repo, "workdir", "treeRoot"); - assertGitdirPath(repo, "workdir", "repoWithConfig"); - } - - public void testBare_CreateRepositoryFromGitDirOnlyWithBareConfigTrue() - throws Exception { - File gitDir = getFile("workdir", "repoWithConfig"); - setBare(gitDir, true); - FileRepository repo = new FileRepositoryBuilder().setGitDir(gitDir).build(); - assertTrue(repo.isBare()); - } - - public void testWorkdirIsParent_CreateRepositoryFromGitDirOnlyWithBareConfigFalse() - throws Exception { - File gitDir = getFile("workdir", "repoWithBareConfigTrue", "child"); - setBare(gitDir, false); - FileRepository repo = new FileRepositoryBuilder().setGitDir(gitDir).build(); - assertWorkdirPath(repo, "workdir", "repoWithBareConfigTrue"); - } - - public void testNotBare_CreateRepositoryFromGitDirOnlyWithBareConfigFalse() - throws Exception { - File gitDir = getFile("workdir", "repoWithBareConfigFalse", "child"); - setBare(gitDir, false); - FileRepository repo = new FileRepositoryBuilder().setGitDir(gitDir).build(); - assertFalse(repo.isBare()); - assertWorkdirPath(repo, "workdir", "repoWithBareConfigFalse"); - assertGitdirPath(repo, "workdir", "repoWithBareConfigFalse", "child"); - } - - public void testExceptionThrown_BareRepoGetWorkDir() throws Exception { - File gitDir = getFile("workdir"); - try { - new FileRepository(gitDir).getWorkTree(); - fail("Expected IllegalStateException missing"); - } catch (IllegalStateException e) { - // expected - } - } - - public void testExceptionThrown_BareRepoGetIndex() throws Exception { - File gitDir = getFile("workdir"); - try { - new FileRepository(gitDir).getIndex(); - fail("Expected IllegalStateException missing"); - } catch (IllegalStateException e) { - // expected - } - } - - public void testExceptionThrown_BareRepoGetIndexFile() throws Exception { - File gitDir = getFile("workdir"); - try { - new FileRepository(gitDir).getIndexFile(); - fail("Expected Exception missing"); - } catch (IllegalStateException e) { - // expected - } - } - - private File getFile(String... pathComponents) { - String rootPath = new File(new File("target"), "trash").getPath(); - for (String pathComponent : pathComponents) - rootPath = rootPath + File.separatorChar + pathComponent; - File result = new File(rootPath); - result.mkdir(); - return result; - } - - 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); - 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 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) - throws IOException { - File exp = getFile(expected).getCanonicalFile(); - File act = repo.getDirectory().getCanonicalFile(); - assertEquals("Wrong Git Directory", exp, act); - } - - private void assertWorkdirPath(Repository repo, String... expected) - throws IOException { - File exp = getFile(expected).getCanonicalFile(); - File act = repo.getWorkTree().getCanonicalFile(); - assertEquals("Wrong working Directory", exp, act); - } -} 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 cc45722447..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. diff --git a/org.eclipse.jgit.test/tst/org/eclipse/jgit/lib/T0003_Basic.java b/org.eclipse.jgit.test/tst/org/eclipse/jgit/lib/T0003_Basic.java deleted file mode 100644 index 484692ee21..0000000000 --- a/org.eclipse.jgit.test/tst/org/eclipse/jgit/lib/T0003_Basic.java +++ /dev/null @@ -1,754 +0,0 @@ -/* - * Copyright (C) 2007, Dave Watson - * Copyright (C) 2007-2010, Robin Rosenberg - * Copyright (C) 2006-2008, Shawn O. Pearce - * 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.File; -import java.io.FileInputStream; -import java.io.FileReader; -import java.io.FileWriter; -import java.io.IOException; -import java.io.PrintWriter; - -import org.eclipse.jgit.errors.ConfigInvalidException; - -public class T0003_Basic extends SampleDataRepositoryTestCase { - public void test001_Initalize() { - final File gitdir = new File(trash, Constants.DOT_GIT); - final File objects = new File(gitdir, "objects"); - final File objects_pack = new File(objects, "pack"); - final File objects_info = new File(objects, "info"); - final File refs = new File(gitdir, "refs"); - final File refs_heads = new File(refs, "heads"); - final File refs_tags = new File(refs, "tags"); - final File HEAD = new File(gitdir, "HEAD"); - - assertTrue("Exists " + trash, trash.isDirectory()); - assertTrue("Exists " + objects, objects.isDirectory()); - assertTrue("Exists " + objects_pack, objects_pack.isDirectory()); - assertTrue("Exists " + objects_info, objects_info.isDirectory()); - assertEquals(2, objects.listFiles().length); - assertTrue("Exists " + refs, refs.isDirectory()); - assertTrue("Exists " + refs_heads, refs_heads.isDirectory()); - assertTrue("Exists " + refs_tags, refs_tags.isDirectory()); - assertTrue("Exists " + HEAD, HEAD.isFile()); - assertEquals(23, HEAD.length()); - } - - public void test000_openRepoBadArgs() throws IOException { - try { - 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", - e.getMessage()); - } - } - - /** - * Check the default rules for looking up directories and files within a - * repo when the gitDir is given. - * - * @throws IOException - */ - public void test000_openrepo_default_gitDirSet() throws IOException { - File repo1Parent = new File(trash.getParentFile(), "r1"); - Repository repo1initial = new FileRepository(new File(repo1Parent, Constants.DOT_GIT)); - repo1initial.create(); - repo1initial.close(); - - File theDir = new File(repo1Parent, Constants.DOT_GIT); - FileRepository r = new FileRepositoryBuilder().setGitDir(theDir).build(); - assertEqualsPath(theDir, r.getDirectory()); - assertEqualsPath(repo1Parent, r.getWorkTree()); - assertEqualsPath(new File(theDir, "index"), r.getIndexFile()); - assertEqualsPath(new File(theDir, "objects"), r.getObjectDatabase().getDirectory()); - } - - /** - * Check that we can pass both a git directory and a work tree - * repo when the gitDir is given. - * - * @throws IOException - */ - public void test000_openrepo_default_gitDirAndWorkTreeSet() throws IOException { - File repo1Parent = new File(trash.getParentFile(), "r1"); - Repository repo1initial = new FileRepository(new File(repo1Parent, Constants.DOT_GIT)); - repo1initial.create(); - repo1initial.close(); - - File theDir = new File(repo1Parent, Constants.DOT_GIT); - FileRepository r = new FileRepositoryBuilder().setGitDir(theDir) - .setWorkTree(repo1Parent.getParentFile()).build(); - assertEqualsPath(theDir, r.getDirectory()); - assertEqualsPath(repo1Parent.getParentFile(), r.getWorkTree()); - assertEqualsPath(new File(theDir, "index"), r.getIndexFile()); - assertEqualsPath(new File(theDir, "objects"), r.getObjectDatabase().getDirectory()); - } - - /** - * Check the default rules for looking up directories and files within a - * repo when the workTree is given. - * - * @throws IOException - */ - public void test000_openrepo_default_workDirSet() throws IOException { - File repo1Parent = new File(trash.getParentFile(), "r1"); - Repository repo1initial = new FileRepository(new File(repo1Parent, Constants.DOT_GIT)); - repo1initial.create(); - repo1initial.close(); - - File theDir = new File(repo1Parent, Constants.DOT_GIT); - FileRepository r = new FileRepositoryBuilder().setWorkTree(repo1Parent).build(); - assertEqualsPath(theDir, r.getDirectory()); - assertEqualsPath(repo1Parent, r.getWorkTree()); - assertEqualsPath(new File(theDir, "index"), r.getIndexFile()); - assertEqualsPath(new File(theDir, "objects"), r.getObjectDatabase().getDirectory()); - } - - /** - * Check that worktree config has an effect, given absolute path. - * - * @throws IOException - */ - public void test000_openrepo_default_absolute_workdirconfig() - throws IOException { - File repo1Parent = new File(trash.getParentFile(), "r1"); - File workdir = new File(trash.getParentFile(), "rw"); - workdir.mkdir(); - FileRepository repo1initial = new FileRepository(new File(repo1Parent, Constants.DOT_GIT)); - repo1initial.create(); - repo1initial.getConfig().setString("core", null, "worktree", - workdir.getAbsolutePath()); - repo1initial.getConfig().save(); - repo1initial.close(); - - File theDir = new File(repo1Parent, Constants.DOT_GIT); - FileRepository r = new FileRepositoryBuilder().setGitDir(theDir).build(); - assertEqualsPath(theDir, r.getDirectory()); - assertEqualsPath(workdir, r.getWorkTree()); - assertEqualsPath(new File(theDir, "index"), r.getIndexFile()); - assertEqualsPath(new File(theDir, "objects"), r.getObjectDatabase().getDirectory()); - } - - /** - * Check that worktree config has an effect, given a relative path. - * - * @throws IOException - */ - public void test000_openrepo_default_relative_workdirconfig() - throws IOException { - File repo1Parent = new File(trash.getParentFile(), "r1"); - File workdir = new File(trash.getParentFile(), "rw"); - workdir.mkdir(); - FileRepository repo1initial = new FileRepository(new File(repo1Parent, Constants.DOT_GIT)); - repo1initial.create(); - repo1initial.getConfig() - .setString("core", null, "worktree", "../../rw"); - repo1initial.getConfig().save(); - repo1initial.close(); - - File theDir = new File(repo1Parent, Constants.DOT_GIT); - FileRepository r = new FileRepositoryBuilder().setGitDir(theDir).build(); - assertEqualsPath(theDir, r.getDirectory()); - assertEqualsPath(workdir, r.getWorkTree()); - assertEqualsPath(new File(theDir, "index"), r.getIndexFile()); - assertEqualsPath(new File(theDir, "objects"), r.getObjectDatabase().getDirectory()); - } - - /** - * Check that the given index file is honored and the alternate object - * directories too - * - * @throws IOException - */ - public void test000_openrepo_alternate_index_file_and_objdirs() - throws IOException { - File repo1Parent = new File(trash.getParentFile(), "r1"); - File indexFile = new File(trash, "idx"); - File objDir = new File(trash, "../obj"); - 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); - FileRepository r = new FileRepositoryBuilder() // - .setGitDir(theDir).setObjectDirectory(objDir) // - .addAlternateObjectDirectory(altObjDir) // - .setIndexFile(indexFile) // - .build(); - assertEqualsPath(theDir, r.getDirectory()); - assertEqualsPath(theDir.getParentFile(), r.getWorkTree()); - assertEqualsPath(indexFile, r.getIndexFile()); - 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. - r.close(); - } - - protected void assertEqualsPath(File expected, File actual) - throws IOException { - assertEquals(expected.getCanonicalPath(), actual.getCanonicalPath()); - } - - public void test002_WriteEmptyTree() throws IOException { - // One of our test packs contains the empty tree object. If the pack is - // open when we create it we won't write the object file out as a loose - // object (as it already exists in the pack). - // - final Repository newdb = createBareRepository(); - final Tree t = new Tree(newdb); - t.accept(new WriteTree(trash, newdb), TreeEntry.MODIFIED_ONLY); - assertEquals("4b825dc642cb6eb9a060e54bf8d69288fbee4904", t.getId() - .name()); - final File o = new File(new File(new File(newdb.getDirectory(), - "objects"), "4b"), "825dc642cb6eb9a060e54bf8d69288fbee4904"); - assertTrue("Exists " + o, o.isFile()); - assertTrue("Read-only " + o, !o.canWrite()); - } - - public void test002_WriteEmptyTree2() throws IOException { - // File shouldn't exist as it is in a test pack. - // - final Tree t = new Tree(db); - t.accept(new WriteTree(trash, db), TreeEntry.MODIFIED_ONLY); - assertEquals("4b825dc642cb6eb9a060e54bf8d69288fbee4904", t.getId() - .name()); - final File o = new File(new File( - new File(db.getDirectory(), "objects"), "4b"), - "825dc642cb6eb9a060e54bf8d69288fbee4904"); - assertFalse("Exists " + o, o.isFile()); - } - - public void test003_WriteShouldBeEmptyTree() throws IOException { - final Tree t = new Tree(db); - final ObjectId emptyId = new ObjectWriter(db).writeBlob(new byte[0]); - t.addFile("should-be-empty").setId(emptyId); - t.accept(new WriteTree(trash, db), TreeEntry.MODIFIED_ONLY); - assertEquals("7bb943559a305bdd6bdee2cef6e5df2413c3d30a", t.getId() - .name()); - - File o; - o = new File(new File(new File(db.getDirectory(), "objects"), "7b"), - "b943559a305bdd6bdee2cef6e5df2413c3d30a"); - assertTrue("Exists " + o, o.isFile()); - assertTrue("Read-only " + o, !o.canWrite()); - - o = new File(new File(new File(db.getDirectory(), "objects"), "e6"), - "9de29bb2d1d6434b8b29ae775ad8c2e48c5391"); - assertTrue("Exists " + o, o.isFile()); - assertTrue("Read-only " + o, !o.canWrite()); - } - - public void test005_ReadSimpleConfig() { - final Config c = db.getConfig(); - assertNotNull(c); - assertEquals("0", c.getString("core", null, "repositoryformatversion")); - assertEquals("0", c.getString("CoRe", null, "REPOSITORYFoRmAtVeRsIoN")); - assertEquals("true", c.getString("core", null, "filemode")); - assertEquals("true", c.getString("cOrE", null, "fIlEModE")); - assertNull(c.getString("notavalue", null, "reallyNotAValue")); - } - - public void test006_ReadUglyConfig() throws IOException, - ConfigInvalidException { - 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" - + " email = A U Thor # Just an example...\n" - + " name = \"A Thor \\\\ \\\"\\t \"\n" - + " defaultCheckInComment = a many line\\n\\\ncomment\\n\\\n" - + " to test\n"; - pw.write(configStr); - pw.close(); - c.load(); - assertEquals("yes", c.getString("core", null, "filemode")); - assertEquals("A U Thor ", c - .getString("user", null, "email")); - assertEquals("A Thor \\ \"\t ", c.getString("user", null, "name")); - assertEquals("a many line\ncomment\n to test", c.getString("user", - null, "defaultCheckInComment")); - c.save(); - final FileReader fr = new FileReader(cfg); - final char[] cbuf = new char[configStr.length()]; - fr.read(cbuf); - fr.close(); - assertEquals(configStr, new String(cbuf)); - } - - public void test007_Open() throws IOException { - final FileRepository db2 = new FileRepository(db.getDirectory()); - assertEquals(db.getDirectory(), db2.getDirectory()); - assertEquals(db.getObjectDatabase().getDirectory(), db2.getObjectDatabase().getDirectory()); - assertNotSame(db.getConfig(), db2.getConfig()); - } - - public void test008_FailOnWrongVersion() throws IOException { - final File cfg = new File(db.getDirectory(), "config"); - final FileWriter pw = new FileWriter(cfg); - final String badvers = "ihopethisisneveraversion"; - final String configStr = "[core]\n" + "\trepositoryFormatVersion=" - + badvers + "\n"; - pw.write(configStr); - pw.close(); - - try { - new FileRepository(db.getDirectory()); - fail("incorrectly opened a bad repository"); - } catch (IOException ioe) { - assertTrue(ioe.getMessage().indexOf("format") > 0); - assertTrue(ioe.getMessage().indexOf(badvers) > 0); - } - } - - 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"); - t.accept(new WriteTree(trash, db), TreeEntry.MODIFIED_ONLY); - assertEquals(ObjectId.fromString("00b1f73724f493096d1ffa0b0f1f1482dbb8c936"), - t.getTreeId()); - - final Commit c = new Commit(db); - c.setAuthor(new PersonIdent(author, 1154236443000L, -4 * 60)); - c.setCommitter(new PersonIdent(committer, 1154236443000L, -4 * 60)); - c.setMessage("A Commit\n"); - c.setTree(t); - assertEquals(t.getTreeId(), c.getTreeId()); - c.commit(); - final ObjectId cmtid = ObjectId.fromString( - "803aec4aba175e8ab1d666873c984c0308179099"); - assertEquals(cmtid, c.getCommitId()); - - // Verify the commit we just wrote is in the correct format. - 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()); - assertTrue(0x789c % 31 == 0); - } finally { - xis.close(); - } - - // Verify we can read it. - final Commit c2 = db.mapCommit(cmtid); - assertNotNull(c2); - assertEquals(c.getMessage(), c2.getMessage()); - assertEquals(c.getTreeId(), c2.getTreeId()); - assertEquals(c.getAuthor(), c2.getAuthor()); - assertEquals(c.getCommitter(), c2.getCommitter()); - } - - public void test012_SubtreeExternalSorting() throws IOException { - final ObjectId emptyBlob = new ObjectWriter(db).writeBlob(new byte[0]); - final Tree t = new Tree(db); - final FileTreeEntry e0 = t.addFile("a-"); - final FileTreeEntry e1 = t.addFile("a-b"); - final FileTreeEntry e2 = t.addFile("a/b"); - final FileTreeEntry e3 = t.addFile("a="); - final FileTreeEntry e4 = t.addFile("a=b"); - - e0.setId(emptyBlob); - e1.setId(emptyBlob); - e2.setId(emptyBlob); - e3.setId(emptyBlob); - e4.setId(emptyBlob); - - t.accept(new WriteTree(trash, db), TreeEntry.MODIFIED_ONLY); - assertEquals(ObjectId.fromString("b47a8f0a4190f7572e11212769090523e23eb1ea"), - t.getId()); - } - - public void test020_createBlobTag() throws IOException { - final ObjectId emptyId = new ObjectWriter(db).writeBlob(new byte[0]); - final Tag t = new Tag(db); - t.setObjId(emptyId); - t.setType("blob"); - t.setTag("test020"); - t.setAuthor(new PersonIdent(author, 1154236443000L, -4 * 60)); - t.setMessage("test020 tagged\n"); - t.tag(); - assertEquals("6759556b09fbb4fd8ae5e315134481cc25d46954", t.getTagId().name()); - - Tag mapTag = db.mapTag("test020"); - assertEquals("blob", mapTag.getType()); - assertEquals("test020 tagged\n", mapTag.getMessage()); - assertEquals(new PersonIdent(author, 1154236443000L, -4 * 60), mapTag.getAuthor()); - assertEquals("e69de29bb2d1d6434b8b29ae775ad8c2e48c5391", mapTag.getObjId().name()); - } - - public void test020b_createBlobPlainTag() throws IOException { - test020_createBlobTag(); - Tag t = new Tag(db); - t.setTag("test020b"); - t.setObjId(ObjectId.fromString("e69de29bb2d1d6434b8b29ae775ad8c2e48c5391")); - t.tag(); - - Tag mapTag = db.mapTag("test020b"); - assertEquals("e69de29bb2d1d6434b8b29ae775ad8c2e48c5391", mapTag.getObjId().name()); - - // We do not repeat the plain tag test for other object types - } - - public void test021_createTreeTag() throws IOException { - final ObjectId emptyId = new ObjectWriter(db).writeBlob(new byte[0]); - final Tree almostEmptyTree = new Tree(db); - almostEmptyTree.addEntry(new FileTreeEntry(almostEmptyTree, emptyId, "empty".getBytes(), false)); - final ObjectId almostEmptyTreeId = new ObjectWriter(db).writeTree(almostEmptyTree); - final Tag t = new Tag(db); - t.setObjId(almostEmptyTreeId); - t.setType("tree"); - t.setTag("test021"); - t.setAuthor(new PersonIdent(author, 1154236443000L, -4 * 60)); - t.setMessage("test021 tagged\n"); - t.tag(); - assertEquals("b0517bc8dbe2096b419d42424cd7030733f4abe5", t.getTagId().name()); - - Tag mapTag = db.mapTag("test021"); - assertEquals("tree", mapTag.getType()); - assertEquals("test021 tagged\n", mapTag.getMessage()); - assertEquals(new PersonIdent(author, 1154236443000L, -4 * 60), mapTag.getAuthor()); - assertEquals("417c01c8795a35b8e835113a85a5c0c1c77f67fb", mapTag.getObjId().name()); - } - - public void test022_createCommitTag() throws IOException { - final ObjectId emptyId = new ObjectWriter(db).writeBlob(new byte[0]); - final Tree almostEmptyTree = new Tree(db); - almostEmptyTree.addEntry(new FileTreeEntry(almostEmptyTree, emptyId, "empty".getBytes(), false)); - final ObjectId almostEmptyTreeId = new ObjectWriter(db).writeTree(almostEmptyTree); - final Commit almostEmptyCommit = new Commit(db); - almostEmptyCommit.setAuthor(new PersonIdent(author, 1154236443000L, -2 * 60)); // not exactly the same - almostEmptyCommit.setCommitter(new PersonIdent(author, 1154236443000L, -2 * 60)); - almostEmptyCommit.setMessage("test022\n"); - almostEmptyCommit.setTreeId(almostEmptyTreeId); - ObjectId almostEmptyCommitId = new ObjectWriter(db).writeCommit(almostEmptyCommit); - final Tag t = new Tag(db); - t.setObjId(almostEmptyCommitId); - t.setType("commit"); - t.setTag("test022"); - t.setAuthor(new PersonIdent(author, 1154236443000L, -4 * 60)); - t.setMessage("test022 tagged\n"); - t.tag(); - assertEquals("0ce2ebdb36076ef0b38adbe077a07d43b43e3807", t.getTagId().name()); - - Tag mapTag = db.mapTag("test022"); - assertEquals("commit", mapTag.getType()); - assertEquals("test022 tagged\n", mapTag.getMessage()); - assertEquals(new PersonIdent(author, 1154236443000L, -4 * 60), mapTag.getAuthor()); - assertEquals("b5d3b45a96b340441f5abb9080411705c51cc86c", mapTag.getObjId().name()); - } - - public void test023_createCommitNonAnullii() throws IOException { - final ObjectId emptyId = new ObjectWriter(db).writeBlob(new byte[0]); - final Tree almostEmptyTree = new Tree(db); - almostEmptyTree.addEntry(new FileTreeEntry(almostEmptyTree, emptyId, "empty".getBytes(), false)); - final ObjectId almostEmptyTreeId = new ObjectWriter(db).writeTree(almostEmptyTree); - Commit commit = new Commit(db); - commit.setTreeId(almostEmptyTreeId); - commit.setAuthor(new PersonIdent("Joe H\u00e4cker","joe@example.com",4294967295000L,60)); - commit.setCommitter(new PersonIdent("Joe Hacker","joe2@example.com",4294967295000L,60)); - commit.setEncoding("UTF-8"); - commit.setMessage("\u00dcbergeeks"); - ObjectId cid = new ObjectWriter(db).writeCommit(commit); - assertEquals("4680908112778718f37e686cbebcc912730b3154", cid.name()); - Commit loadedCommit = db.mapCommit(cid); - assertNotSame(loadedCommit, commit); - assertEquals(commit.getMessage(), loadedCommit.getMessage()); - } - - public void test024_createCommitNonAscii() throws IOException { - final ObjectId emptyId = new ObjectWriter(db).writeBlob(new byte[0]); - final Tree almostEmptyTree = new Tree(db); - almostEmptyTree.addEntry(new FileTreeEntry(almostEmptyTree, emptyId, "empty".getBytes(), false)); - final ObjectId almostEmptyTreeId = new ObjectWriter(db).writeTree(almostEmptyTree); - Commit commit = new Commit(db); - commit.setTreeId(almostEmptyTreeId); - commit.setAuthor(new PersonIdent("Joe H\u00e4cker","joe@example.com",4294967295000L,60)); - commit.setCommitter(new PersonIdent("Joe Hacker","joe2@example.com",4294967295000L,60)); - commit.setEncoding("ISO-8859-1"); - commit.setMessage("\u00dcbergeeks"); - ObjectId cid = new ObjectWriter(db).writeCommit(commit); - assertEquals("2979b39d385014b33287054b87f77bcb3ecb5ebf", cid.name()); - } - - public void test025_packedRefs() throws IOException { - test020_createBlobTag(); - test021_createTreeTag(); - test022_createCommitTag(); - - if (!new File(db.getDirectory(),"refs/tags/test020").delete()) throw new Error("Cannot delete unpacked tag"); - if (!new File(db.getDirectory(),"refs/tags/test021").delete()) throw new Error("Cannot delete unpacked tag"); - if (!new File(db.getDirectory(),"refs/tags/test022").delete()) throw new Error("Cannot delete unpacked tag"); - - // We cannot resolve it now, since we have no ref - Tag mapTag20missing = db.mapTag("test020"); - assertNull(mapTag20missing); - - // Construct packed refs file - PrintWriter w = new PrintWriter(new FileWriter(new File(db.getDirectory(), "packed-refs"))); - w.println("# packed-refs with: peeled"); - w.println("6759556b09fbb4fd8ae5e315134481cc25d46954 refs/tags/test020"); - w.println("^e69de29bb2d1d6434b8b29ae775ad8c2e48c5391"); - w.println("b0517bc8dbe2096b419d42424cd7030733f4abe5 refs/tags/test021"); - w.println("^417c01c8795a35b8e835113a85a5c0c1c77f67fb"); - w.println("0ce2ebdb36076ef0b38adbe077a07d43b43e3807 refs/tags/test022"); - w.println("^b5d3b45a96b340441f5abb9080411705c51cc86c"); - w.close(); - ((RefDirectory)db.getRefDatabase()).rescan(); - - Tag mapTag20 = db.mapTag("test020"); - assertNotNull("have tag test020", mapTag20); - assertEquals("blob", mapTag20.getType()); - assertEquals("test020 tagged\n", mapTag20.getMessage()); - assertEquals(new PersonIdent(author, 1154236443000L, -4 * 60), mapTag20.getAuthor()); - assertEquals("e69de29bb2d1d6434b8b29ae775ad8c2e48c5391", mapTag20.getObjId().name()); - - Tag mapTag21 = db.mapTag("test021"); - assertEquals("tree", mapTag21.getType()); - assertEquals("test021 tagged\n", mapTag21.getMessage()); - assertEquals(new PersonIdent(author, 1154236443000L, -4 * 60), mapTag21.getAuthor()); - assertEquals("417c01c8795a35b8e835113a85a5c0c1c77f67fb", mapTag21.getObjId().name()); - - Tag mapTag22 = db.mapTag("test022"); - assertEquals("commit", mapTag22.getType()); - assertEquals("test022 tagged\n", mapTag22.getMessage()); - assertEquals(new PersonIdent(author, 1154236443000L, -4 * 60), mapTag22.getAuthor()); - assertEquals("b5d3b45a96b340441f5abb9080411705c51cc86c", mapTag22.getObjId().name()); - } - - public void test025_computeSha1NoStore() throws IOException { - byte[] data = "test025 some data, more than 16 bytes to get good coverage" - .getBytes("ISO-8859-1"); - // TODO: but we do not test legacy header writing - final ObjectId id = new ObjectWriter(db).computeBlobSha1(data.length, - new ByteArrayInputStream(data)); - assertEquals("4f561df5ecf0dfbd53a0dc0f37262fef075d9dde", id.name()); - } - - public void test026_CreateCommitMultipleparents() 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"); - t.accept(new WriteTree(trash, db), TreeEntry.MODIFIED_ONLY); - assertEquals(ObjectId.fromString("00b1f73724f493096d1ffa0b0f1f1482dbb8c936"), - t.getTreeId()); - - final Commit c1 = new Commit(db); - c1.setAuthor(new PersonIdent(author, 1154236443000L, -4 * 60)); - c1.setCommitter(new PersonIdent(committer, 1154236443000L, -4 * 60)); - c1.setMessage("A Commit\n"); - c1.setTree(t); - assertEquals(t.getTreeId(), c1.getTreeId()); - c1.commit(); - final ObjectId cmtid1 = ObjectId.fromString( - "803aec4aba175e8ab1d666873c984c0308179099"); - assertEquals(cmtid1, c1.getCommitId()); - - final Commit c2 = new Commit(db); - c2.setAuthor(new PersonIdent(author, 1154236443000L, -4 * 60)); - c2.setCommitter(new PersonIdent(committer, 1154236443000L, -4 * 60)); - c2.setMessage("A Commit 2\n"); - c2.setTree(t); - assertEquals(t.getTreeId(), c2.getTreeId()); - c2.setParentIds(new ObjectId[] { c1.getCommitId() } ); - c2.commit(); - final ObjectId cmtid2 = ObjectId.fromString( - "95d068687c91c5c044fb8c77c5154d5247901553"); - assertEquals(cmtid2, c2.getCommitId()); - - Commit rm2 = db.mapCommit(cmtid2); - assertNotSame(c2, rm2); // assert the parsed objects is not from the cache - assertEquals(c2.getAuthor(), rm2.getAuthor()); - assertEquals(c2.getCommitId(), rm2.getCommitId()); - assertEquals(c2.getMessage(), rm2.getMessage()); - assertEquals(c2.getTree().getTreeId(), rm2.getTree().getTreeId()); - assertEquals(1, rm2.getParentIds().length); - assertEquals(c1.getCommitId(), rm2.getParentIds()[0]); - - final Commit c3 = new Commit(db); - c3.setAuthor(new PersonIdent(author, 1154236443000L, -4 * 60)); - c3.setCommitter(new PersonIdent(committer, 1154236443000L, -4 * 60)); - c3.setMessage("A Commit 3\n"); - c3.setTree(t); - assertEquals(t.getTreeId(), c3.getTreeId()); - c3.setParentIds(new ObjectId[] { c1.getCommitId(), c2.getCommitId() }); - c3.commit(); - final ObjectId cmtid3 = ObjectId.fromString( - "ce6e1ce48fbeeb15a83f628dc8dc2debefa066f4"); - assertEquals(cmtid3, c3.getCommitId()); - - Commit rm3 = db.mapCommit(cmtid3); - assertNotSame(c3, rm3); // assert the parsed objects is not from the cache - assertEquals(c3.getAuthor(), rm3.getAuthor()); - assertEquals(c3.getCommitId(), rm3.getCommitId()); - assertEquals(c3.getMessage(), rm3.getMessage()); - assertEquals(c3.getTree().getTreeId(), rm3.getTree().getTreeId()); - assertEquals(2, rm3.getParentIds().length); - assertEquals(c1.getCommitId(), rm3.getParentIds()[0]); - assertEquals(c2.getCommitId(), rm3.getParentIds()[1]); - - final Commit c4 = new Commit(db); - c4.setAuthor(new PersonIdent(author, 1154236443000L, -4 * 60)); - c4.setCommitter(new PersonIdent(committer, 1154236443000L, -4 * 60)); - c4.setMessage("A Commit 4\n"); - c4.setTree(t); - assertEquals(t.getTreeId(), c3.getTreeId()); - c4.setParentIds(new ObjectId[] { c1.getCommitId(), c2.getCommitId(), c3.getCommitId() }); - c4.commit(); - final ObjectId cmtid4 = ObjectId.fromString( - "d1fca9fe3fef54e5212eb67902c8ed3e79736e27"); - assertEquals(cmtid4, c4.getCommitId()); - - Commit rm4 = db.mapCommit(cmtid4); - assertNotSame(c4, rm3); // assert the parsed objects is not from the cache - assertEquals(c4.getAuthor(), rm4.getAuthor()); - assertEquals(c4.getCommitId(), rm4.getCommitId()); - assertEquals(c4.getMessage(), rm4.getMessage()); - assertEquals(c4.getTree().getTreeId(), rm4.getTree().getTreeId()); - assertEquals(3, rm4.getParentIds().length); - assertEquals(c1.getCommitId(), rm4.getParentIds()[0]); - assertEquals(c2.getCommitId(), rm4.getParentIds()[1]); - assertEquals(c3.getCommitId(), rm4.getParentIds()[2]); - } - - public void test027_UnpackedRefHigherPriorityThanPacked() throws IOException { - PrintWriter writer = new PrintWriter(new FileWriter(new File(db.getDirectory(), "refs/heads/a"))); - String unpackedId = "7f822839a2fe9760f386cbbbcb3f92c5fe81def7"; - writer.print(unpackedId); - writer.print('\n'); - writer.close(); - - ObjectId resolved = db.resolve("refs/heads/a"); - assertEquals(unpackedId, resolved.name()); - } - - public void test028_LockPackedRef() throws IOException { - writeTrashFile(".git/packed-refs", "7f822839a2fe9760f386cbbbcb3f92c5fe81def7 refs/heads/foobar"); - writeTrashFile(".git/HEAD", "ref: refs/heads/foobar\n"); - BUG_WorkAroundRacyGitIssues("packed-refs"); - BUG_WorkAroundRacyGitIssues("HEAD"); - - ObjectId resolve = db.resolve("HEAD"); - assertEquals("7f822839a2fe9760f386cbbbcb3f92c5fe81def7", resolve.name()); - - RefUpdate lockRef = db.updateRef("HEAD"); - ObjectId newId = ObjectId.fromString("07f822839a2fe9760f386cbbbcb3f92c5fe81def"); - lockRef.setNewObjectId(newId); - assertEquals(RefUpdate.Result.FORCED, lockRef.forceUpdate()); - - assertTrue(new File(db.getDirectory(), "refs/heads/foobar").exists()); - assertEquals(newId, db.resolve("refs/heads/foobar")); - - // Again. The ref already exists - RefUpdate lockRef2 = db.updateRef("HEAD"); - ObjectId newId2 = ObjectId.fromString("7f822839a2fe9760f386cbbbcb3f92c5fe81def7"); - lockRef2.setNewObjectId(newId2); - assertEquals(RefUpdate.Result.FORCED, lockRef2.forceUpdate()); - - assertTrue(new File(db.getDirectory(), "refs/heads/foobar").exists()); - assertEquals(newId2, db.resolve("refs/heads/foobar")); - } - - public void test029_mapObject() throws IOException { - assertEquals(new byte[0].getClass(), db.mapObject(ObjectId.fromString("5b6e7c66c276e7610d4a73c70ec1a1f7c1003259"), null).getClass()); - assertEquals(Commit.class, db.mapObject(ObjectId.fromString("540a36d136cf413e4b064c2b0e0a4db60f77feab"), null).getClass()); - assertEquals(Tree.class, db.mapObject(ObjectId.fromString("aabf2ffaec9b497f0950352b3e582d73035c2035"), null).getClass()); - assertEquals(Tag.class, db.mapObject(ObjectId.fromString("17768080a2318cd89bba4c8b87834401e2095703"), null).getClass()); - } - - public void test30_stripWorkDir() { - File relCwd = new File("."); - File absCwd = relCwd.getAbsoluteFile(); - File absBase = new File(new File(absCwd, "repo"), "workdir"); - File relBase = new File(new File(relCwd, "repo"), "workdir"); - assertEquals(absBase.getAbsolutePath(), relBase.getAbsolutePath()); - - File relBaseFile = new File(new File(relBase, "other"), "module.c"); - File absBaseFile = new File(new File(absBase, "other"), "module.c"); - assertEquals("other/module.c", Repository.stripWorkDir(relBase, relBaseFile)); - assertEquals("other/module.c", Repository.stripWorkDir(relBase, absBaseFile)); - assertEquals("other/module.c", Repository.stripWorkDir(absBase, relBaseFile)); - assertEquals("other/module.c", Repository.stripWorkDir(absBase, absBaseFile)); - - File relNonFile = new File(new File(relCwd, "not-repo"), ".gitignore"); - File absNonFile = new File(new File(absCwd, "not-repo"), ".gitignore"); - assertEquals("", Repository.stripWorkDir(relBase, relNonFile)); - assertEquals("", Repository.stripWorkDir(absBase, absNonFile)); - - assertEquals("", Repository.stripWorkDir(db.getWorkTree(), db.getWorkTree())); - - File file = new File(new File(db.getWorkTree(), "subdir"), "File.java"); - assertEquals("subdir/File.java", Repository.stripWorkDir(db.getWorkTree(), file)); - - } - - /** - * Kick the timestamp of a local file. - *

- * We shouldn't have to make these method calls. The cache is using file - * system timestamps, and on many systems unit tests run faster than the - * modification clock. Dumping the cache after we make an edit behind - * RefDirectory's back allows the tests to pass. - * - * @param name - * the file in the repository to force a time change on. - */ - private void BUG_WorkAroundRacyGitIssues(String name) { - File path = new File(db.getDirectory(), name); - long old = path.lastModified(); - long set = 1250379778668L; // Sat Aug 15 20:12:58 GMT-03:30 2009 - path.setLastModified(set); - assertTrue("time changed", old != path.lastModified()); - } -} diff --git a/org.eclipse.jgit.test/tst/org/eclipse/jgit/lib/T0004_PackReader.java b/org.eclipse.jgit.test/tst/org/eclipse/jgit/lib/T0004_PackReader.java deleted file mode 100644 index 54816f6f5e..0000000000 --- a/org.eclipse.jgit.test/tst/org/eclipse/jgit/lib/T0004_PackReader.java +++ /dev/null @@ -1,86 +0,0 @@ -/* - * Copyright (C) 2008-2009, Google Inc. - * Copyright (C) 2008, Imran M Yousuf - * Copyright (C) 2007, Robin Rosenberg - * Copyright (C) 2006-2008, Shawn O. Pearce - * 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.IOException; - -import org.eclipse.jgit.util.JGitTestUtil; - -public class T0004_PackReader extends SampleDataRepositoryTestCase { - private static final String PACK_NAME = "pack-34be9032ac282b11fa9babdc2b2a93ca996c9c2f"; - private static final File TEST_PACK = JGitTestUtil.getTestResourceFile(PACK_NAME + ".pack"); - private static final File TEST_IDX = JGitTestUtil.getTestResourceFile(PACK_NAME + ".idx"); - - public void test003_lookupCompressedObject() throws IOException { - final PackFile pr; - final ObjectId id; - final PackedObjectLoader or; - - id = ObjectId.fromString("902d5476fa249b7abc9d84c611577a81381f0327"); - pr = new PackFile(TEST_IDX, TEST_PACK); - 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(); - } - - public void test004_lookupDeltifiedObject() throws IOException { - final ObjectId id; - final ObjectLoader or; - - id = ObjectId.fromString("5b6e7c66c276e7610d4a73c70ec1a1f7c1003259"); - or = db.openObject(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/lib/WindowCacheGetTest.java b/org.eclipse.jgit.test/tst/org/eclipse/jgit/lib/WindowCacheGetTest.java deleted file mode 100644 index 8ff022ddc4..0000000000 --- a/org.eclipse.jgit.test/tst/org/eclipse/jgit/lib/WindowCacheGetTest.java +++ /dev/null @@ -1,149 +0,0 @@ -/* - * 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.BufferedReader; -import java.io.FileInputStream; -import java.io.IOException; -import java.io.InputStreamReader; -import java.util.ArrayList; -import java.util.List; - -import org.eclipse.jgit.errors.CorruptObjectException; -import org.eclipse.jgit.util.JGitTestUtil; -import org.eclipse.jgit.util.MutableInteger; - -public class WindowCacheGetTest extends SampleDataRepositoryTestCase { - private List toLoad; - - @Override - public void setUp() throws Exception { - super.setUp(); - - toLoad = new ArrayList(); - final BufferedReader br = new BufferedReader(new InputStreamReader( - new FileInputStream(JGitTestUtil - .getTestResourceFile("all_packed_objects.txt")), - Constants.CHARSET)); - try { - String line; - while ((line = br.readLine()) != null) { - final String[] parts = line.split(" {1,}"); - final TestObject o = new TestObject(); - o.id = ObjectId.fromString(parts[0]); - o.setType(parts[1]); - o.rawSize = Integer.parseInt(parts[2]); - // parts[3] is the size-in-pack - o.offset = Long.parseLong(parts[4]); - toLoad.add(o); - } - } finally { - br.close(); - } - assertEquals(96, toLoad.size()); - } - - public void testCache_Defaults() throws IOException { - final WindowCacheConfig cfg = new WindowCacheConfig(); - WindowCache.reconfigure(cfg); - doCacheTests(); - checkLimits(cfg); - - final WindowCache cache = WindowCache.getInstance(); - assertEquals(6, cache.getOpenFiles()); - assertEquals(17346, cache.getOpenBytes()); - } - - public void testCache_TooFewFiles() throws IOException { - final WindowCacheConfig cfg = new WindowCacheConfig(); - cfg.setPackedGitOpenFiles(2); - WindowCache.reconfigure(cfg); - doCacheTests(); - checkLimits(cfg); - } - - public void testCache_TooSmallLimit() throws IOException { - final WindowCacheConfig cfg = new WindowCacheConfig(); - cfg.setPackedGitWindowSize(4096); - cfg.setPackedGitLimit(4096); - WindowCache.reconfigure(cfg); - doCacheTests(); - checkLimits(cfg); - } - - private void checkLimits(final WindowCacheConfig cfg) { - final WindowCache cache = WindowCache.getInstance(); - assertTrue(cache.getOpenFiles() <= cfg.getPackedGitOpenFiles()); - assertTrue(cache.getOpenBytes() <= cfg.getPackedGitLimit()); - assertTrue(0 < cache.getOpenFiles()); - assertTrue(0 < cache.getOpenBytes()); - } - - private void doCacheTests() throws IOException { - for (final TestObject o : toLoad) { - final ObjectLoader or = db.openObject(o.id); - assertNotNull(or); - assertTrue(or instanceof PackedObjectLoader); - assertEquals(o.type, or.getType()); - assertEquals(o.rawSize, or.getRawSize()); - assertEquals(o.offset, ((PackedObjectLoader) or).getObjectOffset()); - } - } - - private class TestObject { - ObjectId id; - - int type; - - int rawSize; - - long offset; - - void setType(final String typeStr) throws CorruptObjectException { - final byte[] typeRaw = Constants.encode(typeStr + " "); - final MutableInteger ptr = new MutableInteger(); - type = Constants.decodeTypeString(id, typeRaw, (byte) ' ', ptr); - } - } -} diff --git a/org.eclipse.jgit.test/tst/org/eclipse/jgit/lib/WindowCacheReconfigureTest.java b/org.eclipse.jgit.test/tst/org/eclipse/jgit/lib/WindowCacheReconfigureTest.java deleted file mode 100644 index 9e093c85bd..0000000000 --- a/org.eclipse.jgit.test/tst/org/eclipse/jgit/lib/WindowCacheReconfigureTest.java +++ /dev/null @@ -1,125 +0,0 @@ -/* - * 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; - -public class WindowCacheReconfigureTest extends RepositoryTestCase { - public void testConfigureCache_PackedGitLimit_0() { - try { - final WindowCacheConfig cfg = new WindowCacheConfig(); - cfg.setPackedGitLimit(0); - WindowCache.reconfigure(cfg); - fail("incorrectly permitted PackedGitLimit = 0"); - } catch (IllegalArgumentException e) { - // - } - } - - public void testConfigureCache_PackedGitWindowSize_0() { - try { - final WindowCacheConfig cfg = new WindowCacheConfig(); - cfg.setPackedGitWindowSize(0); - WindowCache.reconfigure(cfg); - fail("incorrectly permitted PackedGitWindowSize = 0"); - } catch (IllegalArgumentException e) { - assertEquals("Invalid window size", e.getMessage()); - } - } - - public void testConfigureCache_PackedGitWindowSize_512() { - try { - final WindowCacheConfig cfg = new WindowCacheConfig(); - cfg.setPackedGitWindowSize(512); - WindowCache.reconfigure(cfg); - fail("incorrectly permitted PackedGitWindowSize = 512"); - } catch (IllegalArgumentException e) { - assertEquals("Invalid window size", e.getMessage()); - } - } - - public void testConfigureCache_PackedGitWindowSize_4097() { - try { - final WindowCacheConfig cfg = new WindowCacheConfig(); - cfg.setPackedGitWindowSize(4097); - WindowCache.reconfigure(cfg); - fail("incorrectly permitted PackedGitWindowSize = 4097"); - } catch (IllegalArgumentException e) { - assertEquals("Window size must be power of 2", e.getMessage()); - } - } - - public void testConfigureCache_PackedGitOpenFiles_0() { - try { - final WindowCacheConfig cfg = new WindowCacheConfig(); - cfg.setPackedGitOpenFiles(0); - WindowCache.reconfigure(cfg); - fail("incorrectly permitted PackedGitOpenFiles = 0"); - } catch (IllegalArgumentException e) { - assertEquals("Open files must be >= 1", e.getMessage()); - } - } - - public void testConfigureCache_PackedGitWindowSizeAbovePackedGitLimit() { - try { - final WindowCacheConfig cfg = new WindowCacheConfig(); - cfg.setPackedGitLimit(1024); - cfg.setPackedGitWindowSize(8192); - WindowCache.reconfigure(cfg); - fail("incorrectly permitted PackedGitWindowSize > PackedGitLimit"); - } catch (IllegalArgumentException e) { - assertEquals("Window size must be < limit", e.getMessage()); - } - } - - public void testConfigureCache_Limits1() { - // This test is just to force coverage over some lower bounds for - // the table. We don't want the table to wind up with too small - // of a size. This is highly dependent upon the table allocation - // algorithm actually implemented in WindowCache. - // - final WindowCacheConfig cfg = new WindowCacheConfig(); - cfg.setPackedGitLimit(6 * 4096 / 5); - cfg.setPackedGitWindowSize(4096); - WindowCache.reconfigure(cfg); - } -} diff --git a/org.eclipse.jgit.test/tst/org/eclipse/jgit/lib/XInputStream.java b/org.eclipse.jgit.test/tst/org/eclipse/jgit/lib/XInputStream.java deleted file mode 100644 index eef32b9276..0000000000 --- a/org.eclipse.jgit.test/tst/org/eclipse/jgit/lib/XInputStream.java +++ /dev/null @@ -1,88 +0,0 @@ -/* - * Copyright (C) 2006-2008, Shawn O. Pearce - * 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.BufferedInputStream; -import java.io.EOFException; -import java.io.IOException; -import java.io.InputStream; - -import org.eclipse.jgit.util.NB; - -class XInputStream extends BufferedInputStream { - private final byte[] intbuf = new byte[8]; - - XInputStream(final InputStream s) { - super(s); - } - - synchronized byte[] readFully(final int len) throws IOException { - final byte[] b = new byte[len]; - readFully(b, 0, len); - return b; - } - - synchronized void readFully(final byte[] b, int o, int len) - throws IOException { - int r; - while (len > 0 && (r = read(b, o, len)) > 0) { - o += r; - len -= r; - } - if (len > 0) - throw new EOFException(); - } - - int readUInt8() throws IOException { - final int r = read(); - if (r < 0) - throw new EOFException(); - return r; - } - - long readUInt32() throws IOException { - readFully(intbuf, 0, 4); - return NB.decodeUInt32(intbuf, 0); - } -} diff --git a/org.eclipse.jgit.test/tst/org/eclipse/jgit/storage/file/ConcurrentRepackTest.java b/org.eclipse.jgit.test/tst/org/eclipse/jgit/storage/file/ConcurrentRepackTest.java new file mode 100644 index 0000000000..db770f777d --- /dev/null +++ b/org.eclipse.jgit.test/tst/org/eclipse/jgit/storage/file/ConcurrentRepackTest.java @@ -0,0 +1,275 @@ +/* + * Copyright (C) 2009-2010, Google Inc. + * Copyright (C) 2009, Robin Rosenberg + * 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.BufferedOutputStream; +import java.io.File; +import java.io.FileOutputStream; +import java.io.IOException; +import java.io.OutputStream; +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.PackWriter; +import org.eclipse.jgit.lib.Repository; +import org.eclipse.jgit.lib.RepositoryTestCase; +import org.eclipse.jgit.revwalk.RevObject; +import org.eclipse.jgit.revwalk.RevWalk; + +public class ConcurrentRepackTest extends RepositoryTestCase { + public void setUp() throws Exception { + WindowCacheConfig windowCacheConfig = new WindowCacheConfig(); + windowCacheConfig.setPackedGitOpenFiles(1); + WindowCache.reconfigure(windowCacheConfig); + super.setUp(); + } + + protected void tearDown() throws Exception { + super.tearDown(); + WindowCacheConfig windowCacheConfig = new WindowCacheConfig(); + WindowCache.reconfigure(windowCacheConfig); + } + + public void testObjectInNewPack() throws IncorrectObjectTypeException, + IOException { + // Create a new object in a new pack, and test that it is present. + // + final Repository eden = createBareRepository(); + final RevObject o1 = writeBlob(eden, "o1"); + pack(eden, o1); + assertEquals(o1.name(), parse(o1).name()); + } + + public void testObjectMovedToNewPack1() + throws IncorrectObjectTypeException, IOException { + // Create an object and pack it. Then remove that pack and put the + // object into a different pack file, with some other object. We + // still should be able to access the objects. + // + final Repository eden = createBareRepository(); + final RevObject o1 = writeBlob(eden, "o1"); + final File[] out1 = pack(eden, o1); + assertEquals(o1.name(), parse(o1).name()); + + final RevObject o2 = writeBlob(eden, "o2"); + pack(eden, o2, o1); + + // Force close, and then delete, the old pack. + // + whackCache(); + delete(out1); + + // Now here is the interesting thing. Will git figure the new + // object exists in the new pack, and not the old one. + // + assertEquals(o2.name(), parse(o2).name()); + assertEquals(o1.name(), parse(o1).name()); + } + + public void testObjectMovedWithinPack() + throws IncorrectObjectTypeException, IOException { + // Create an object and pack it. + // + final Repository eden = createBareRepository(); + final RevObject o1 = writeBlob(eden, "o1"); + final File[] out1 = pack(eden, o1); + assertEquals(o1.name(), parse(o1).name()); + + // Force close the old pack. + // + whackCache(); + + // Now overwrite the old pack in place. This method of creating a + // different pack under the same file name is partially broken. We + // should also have a different file name because the list of objects + // within the pack has been modified. + // + final RevObject o2 = writeBlob(eden, "o2"); + final PackWriter pw = new PackWriter(eden, NullProgressMonitor.INSTANCE); + pw.addObject(o2); + pw.addObject(o1); + write(out1, pw); + + // 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. + // + assertEquals(o1.name(), parse(o1).name()); + assertEquals(o2.name(), parse(o2).name()); + } + + public void testObjectMovedToNewPack2() + throws IncorrectObjectTypeException, IOException { + // Create an object and pack it. Then remove that pack and put the + // object into a different pack file, with some other object. We + // still should be able to access the objects. + // + final Repository eden = createBareRepository(); + final RevObject o1 = writeBlob(eden, "o1"); + final File[] out1 = pack(eden, o1); + assertEquals(o1.name(), parse(o1).name()); + + final ObjectLoader load1 = db.openBlob(o1); + assertNotNull(load1); + + final RevObject o2 = writeBlob(eden, "o2"); + pack(eden, o2, o1); + + // Force close, and then delete, the old pack. + // + whackCache(); + delete(out1); + + // Now here is the interesting thing... can the loader we made + // earlier still resolve the object, even though its underlying + // pack is gone, but the object still exists. + // + final ObjectLoader load2 = db.openBlob(o1); + assertNotNull(load2); + assertNotSame(load1, load2); + + final byte[] data2 = load2.getCachedBytes(); + final byte[] data1 = load1.getCachedBytes(); + assertNotNull(data2); + assertNotNull(data1); + assertNotSame(data1, data2); // cache should be per-pack, not per object + assertTrue(Arrays.equals(data1, data2)); + assertEquals(load2.getType(), load1.getType()); + } + + private static void whackCache() { + final WindowCacheConfig config = new WindowCacheConfig(); + config.setPackedGitOpenFiles(1); + WindowCache.reconfigure(config); + } + + private RevObject parse(final AnyObjectId id) + throws MissingObjectException, IOException { + return new RevWalk(db).parseAny(id); + } + + private File[] pack(final Repository src, final RevObject... list) + throws IOException { + final PackWriter pw = new PackWriter(src, NullProgressMonitor.INSTANCE); + for (final RevObject o : list) { + pw.addObject(o); + } + + final ObjectId name = pw.computeName(); + final File packFile = fullPackFileName(name, ".pack"); + final File idxFile = fullPackFileName(name, ".idx"); + final File[] files = new File[] { packFile, idxFile }; + write(files, pw); + return files; + } + + private static void write(final File[] files, final PackWriter pw) + throws IOException { + final long begin = files[0].getParentFile().lastModified(); + OutputStream out; + + out = new BufferedOutputStream(new FileOutputStream(files[0])); + try { + pw.writePack(out); + } finally { + out.close(); + } + + out = new BufferedOutputStream(new FileOutputStream(files[1])); + try { + pw.writeIndex(out); + } finally { + out.close(); + } + + touch(begin, files[0].getParentFile()); + } + + private static void delete(final File[] list) { + final long begin = list[0].getParentFile().lastModified(); + for (final File f : list) { + f.delete(); + assertFalse(f + " was removed", f.exists()); + } + touch(begin, list[0].getParentFile()); + } + + private static void touch(final long begin, final File dir) { + while (begin >= dir.lastModified()) { + try { + Thread.sleep(25); + } catch (InterruptedException ie) { + // + } + dir.setLastModified(System.currentTimeMillis()); + } + } + + private File fullPackFileName(final ObjectId name, final String suffix) { + final File packdir = new File(db.getObjectDatabase().getDirectory(), "pack"); + return new File(packdir, "pack-" + name.name() + suffix); + } + + private RevObject writeBlob(final Repository repo, final String data) + throws IOException { + final RevWalk revWalk = new RevWalk(repo); + final byte[] bytes = Constants.encode(data); + final ObjectWriter ow = new ObjectWriter(repo); + final ObjectId id = ow.writeBlob(bytes); + try { + parse(id); + fail("Object " + id.name() + " should not exist in test repository"); + } catch (MissingObjectException e) { + // Ok + } + return revWalk.lookupBlob(id); + } +} diff --git a/org.eclipse.jgit.test/tst/org/eclipse/jgit/storage/file/PackIndexTestCase.java b/org.eclipse.jgit.test/tst/org/eclipse/jgit/storage/file/PackIndexTestCase.java new file mode 100644 index 0000000000..9884142e5c --- /dev/null +++ b/org.eclipse.jgit.test/tst/org/eclipse/jgit/storage/file/PackIndexTestCase.java @@ -0,0 +1,169 @@ +/* + * Copyright (C) 2008, Marek Zawirski + * 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.util.Iterator; +import java.util.NoSuchElementException; + +import org.eclipse.jgit.errors.MissingObjectException; +import org.eclipse.jgit.lib.RepositoryTestCase; +import org.eclipse.jgit.storage.file.PackIndex.MutableEntry; + +public abstract class PackIndexTestCase extends RepositoryTestCase { + + PackIndex smallIdx; + + PackIndex denseIdx; + + public void setUp() throws Exception { + super.setUp(); + smallIdx = PackIndex.open(getFileForPack34be9032()); + denseIdx = PackIndex.open(getFileForPackdf2982f28()); + } + + /** + * Return file with appropriate index version for prepared pack. + * + * @return file with index + */ + public abstract File getFileForPack34be9032(); + + /** + * Return file with appropriate index version for prepared pack. + * + * @return file with index + */ + public abstract File getFileForPackdf2982f28(); + + /** + * Verify CRC32 support. + * + * @throws MissingObjectException + * @throws UnsupportedOperationException + */ + public abstract void testCRC32() throws MissingObjectException, + UnsupportedOperationException; + + /** + * Test contracts of Iterator methods and this implementation remove() + * limitations. + */ + public void testIteratorMethodsContract() { + Iterator iter = smallIdx.iterator(); + while (iter.hasNext()) { + iter.next(); + } + + try { + iter.next(); + fail("next() unexpectedly returned element"); + } catch (NoSuchElementException x) { + // expected + } + + try { + iter.remove(); + fail("remove() shouldn't be implemented"); + } catch (UnsupportedOperationException x) { + // expected + } + } + + /** + * Test results of iterator comparing to content of well-known (prepared) + * small index. + */ + public void testIteratorReturnedValues1() { + Iterator iter = smallIdx.iterator(); + assertEquals("4b825dc642cb6eb9a060e54bf8d69288fbee4904", iter.next() + .name()); + assertEquals("540a36d136cf413e4b064c2b0e0a4db60f77feab", iter.next() + .name()); + assertEquals("5b6e7c66c276e7610d4a73c70ec1a1f7c1003259", iter.next() + .name()); + assertEquals("6ff87c4664981e4397625791c8ea3bbb5f2279a3", iter.next() + .name()); + assertEquals("82c6b885ff600be425b4ea96dee75dca255b69e7", iter.next() + .name()); + assertEquals("902d5476fa249b7abc9d84c611577a81381f0327", iter.next() + .name()); + assertEquals("aabf2ffaec9b497f0950352b3e582d73035c2035", iter.next() + .name()); + assertEquals("c59759f143fb1fe21c197981df75a7ee00290799", iter.next() + .name()); + assertFalse(iter.hasNext()); + } + + /** + * Compare offset from iterator entries with output of findOffset() method. + */ + public void testCompareEntriesOffsetsWithFindOffsets() { + for (MutableEntry me : smallIdx) { + assertEquals(smallIdx.findOffset(me.toObjectId()), me.getOffset()); + } + for (MutableEntry me : denseIdx) { + assertEquals(denseIdx.findOffset(me.toObjectId()), me.getOffset()); + } + } + + /** + * Test partial results of iterator comparing to content of well-known + * (prepared) dense index, that may need multi-level indexing. + */ + public void testIteratorReturnedValues2() { + Iterator iter = denseIdx.iterator(); + while (!iter.next().name().equals( + "0a3d7772488b6b106fb62813c4d6d627918d9181")) { + // just iterating + } + assertEquals("1004d0d7ac26fbf63050a234c9b88a46075719d3", iter.next() + .name()); // same level-1 + assertEquals("10da5895682013006950e7da534b705252b03be6", iter.next() + .name()); // same level-1 + assertEquals("1203b03dc816ccbb67773f28b3c19318654b0bc8", iter.next() + .name()); + } + +} diff --git a/org.eclipse.jgit.test/tst/org/eclipse/jgit/storage/file/PackIndexV1Test.java b/org.eclipse.jgit.test/tst/org/eclipse/jgit/storage/file/PackIndexV1Test.java new file mode 100644 index 0000000000..303eeff72d --- /dev/null +++ b/org.eclipse.jgit.test/tst/org/eclipse/jgit/storage/file/PackIndexV1Test.java @@ -0,0 +1,83 @@ +/* + * Copyright (C) 2008, Imran M Yousuf + * Copyright (C) 2008, Marek Zawirski + * Copyright (C) 2009, Matthias Sohn + * 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 org.eclipse.jgit.errors.MissingObjectException; +import org.eclipse.jgit.lib.ObjectId; +import org.eclipse.jgit.util.JGitTestUtil; + +public class PackIndexV1Test extends PackIndexTestCase { + @Override + public File getFileForPack34be9032() { + return JGitTestUtil.getTestResourceFile( + "pack-34be9032ac282b11fa9babdc2b2a93ca996c9c2f.idx"); + } + + @Override + public File getFileForPackdf2982f28() { + return JGitTestUtil.getTestResourceFile( + "pack-df2982f284bbabb6bdb59ee3fcc6eb0983e20371.idx"); + } + + /** + * Verify CRC32 - V1 should not index anything. + * + * @throws MissingObjectException + */ + @Override + public void testCRC32() throws MissingObjectException { + assertFalse(smallIdx.hasCRC32Support()); + try { + smallIdx.findCRC32(ObjectId + .fromString("4b825dc642cb6eb9a060e54bf8d69288fbee4904")); + fail("index V1 shouldn't support CRC"); + } catch (UnsupportedOperationException x) { + // expected + } + } +} diff --git a/org.eclipse.jgit.test/tst/org/eclipse/jgit/storage/file/PackIndexV2Test.java b/org.eclipse.jgit.test/tst/org/eclipse/jgit/storage/file/PackIndexV2Test.java new file mode 100644 index 0000000000..2d3ec7b729 --- /dev/null +++ b/org.eclipse.jgit.test/tst/org/eclipse/jgit/storage/file/PackIndexV2Test.java @@ -0,0 +1,94 @@ +/* + * Copyright (C) 2008, Imran M Yousuf + * Copyright (C) 2008, Marek Zawirski + * Copyright (C) 2009, Matthias Sohn + * 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 org.eclipse.jgit.errors.MissingObjectException; +import org.eclipse.jgit.lib.ObjectId; +import org.eclipse.jgit.util.JGitTestUtil; + +public class PackIndexV2Test extends PackIndexTestCase { + @Override + public File getFileForPack34be9032() { + return JGitTestUtil.getTestResourceFile( + "pack-34be9032ac282b11fa9babdc2b2a93ca996c9c2f.idxV2"); + } + + @Override + public File getFileForPackdf2982f28() { + return JGitTestUtil.getTestResourceFile( + "pack-df2982f284bbabb6bdb59ee3fcc6eb0983e20371.idxV2"); + } + + /** + * Verify CRC32 indexing. + * + * @throws UnsupportedOperationException + * @throws MissingObjectException + */ + @Override + public void testCRC32() throws MissingObjectException, + UnsupportedOperationException { + assertTrue(smallIdx.hasCRC32Support()); + assertEquals(0x00000000C2B64258l, smallIdx.findCRC32(ObjectId + .fromString("4b825dc642cb6eb9a060e54bf8d69288fbee4904"))); + assertEquals(0x0000000072AD57C2l, smallIdx.findCRC32(ObjectId + .fromString("540a36d136cf413e4b064c2b0e0a4db60f77feab"))); + assertEquals(0x00000000FF10A479l, smallIdx.findCRC32(ObjectId + .fromString("5b6e7c66c276e7610d4a73c70ec1a1f7c1003259"))); + assertEquals(0x0000000034B27DDCl, smallIdx.findCRC32(ObjectId + .fromString("6ff87c4664981e4397625791c8ea3bbb5f2279a3"))); + assertEquals(0x000000004743F1E4l, smallIdx.findCRC32(ObjectId + .fromString("82c6b885ff600be425b4ea96dee75dca255b69e7"))); + assertEquals(0x00000000640B358Bl, smallIdx.findCRC32(ObjectId + .fromString("902d5476fa249b7abc9d84c611577a81381f0327"))); + assertEquals(0x000000002A17CB5El, smallIdx.findCRC32(ObjectId + .fromString("aabf2ffaec9b497f0950352b3e582d73035c2035"))); + assertEquals(0x000000000B3B5BA6l, smallIdx.findCRC32(ObjectId + .fromString("c59759f143fb1fe21c197981df75a7ee00290799"))); + } +} diff --git a/org.eclipse.jgit.test/tst/org/eclipse/jgit/storage/file/PackReverseIndexTest.java b/org.eclipse.jgit.test/tst/org/eclipse/jgit/storage/file/PackReverseIndexTest.java new file mode 100644 index 0000000000..07a40a425f --- /dev/null +++ b/org.eclipse.jgit.test/tst/org/eclipse/jgit/storage/file/PackReverseIndexTest.java @@ -0,0 +1,122 @@ +/* + * Copyright (C) 2008, Imran M Yousuf + * Copyright (C) 2008, Marek Zawirski + * 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.errors.CorruptObjectException; +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 { + + private PackIndex idx; + + private PackReverseIndex reverseIdx; + + /** + * Set up tested class instance, test constructor by the way. + */ + public void setUp() throws Exception { + super.setUp(); + // index with both small (< 2^31) and big offsets + idx = PackIndex.open(JGitTestUtil.getTestResourceFile( + "pack-huge.idx")); + reverseIdx = new PackReverseIndex(idx); + } + + /** + * Test findObject() for all index entries. + */ + public void testFindObject() { + for (MutableEntry me : idx) + assertEquals(me.toObjectId(), reverseIdx.findObject(me.getOffset())); + } + + /** + * Test findObject() with illegal argument. + */ + public void testFindObjectWrongOffset() { + assertNull(reverseIdx.findObject(0)); + } + + /** + * Test findNextOffset() for all index entries. + * + * @throws CorruptObjectException + */ + public void testFindNextOffset() throws CorruptObjectException { + long offset = findFirstOffset(); + assertTrue(offset > 0); + for (int i = 0; i < idx.getObjectCount(); i++) { + long newOffset = reverseIdx.findNextOffset(offset, Long.MAX_VALUE); + assertTrue(newOffset > offset); + if (i == idx.getObjectCount() - 1) + assertEquals(newOffset, Long.MAX_VALUE); + else + assertEquals(newOffset, idx.findOffset(reverseIdx + .findObject(newOffset))); + offset = newOffset; + } + } + + /** + * Test findNextOffset() with wrong illegal argument as offset. + */ + public void testFindNextOffsetWrongOffset() { + try { + reverseIdx.findNextOffset(0, Long.MAX_VALUE); + fail("findNextOffset() should throw exception"); + } catch (CorruptObjectException x) { + // expected + } + } + + private long findFirstOffset() { + long min = Long.MAX_VALUE; + for (MutableEntry me : idx) + min = Math.min(min, me.getOffset()); + return min; + } +} diff --git a/org.eclipse.jgit.test/tst/org/eclipse/jgit/storage/file/PackWriterTest.java b/org.eclipse.jgit.test/tst/org/eclipse/jgit/storage/file/PackWriterTest.java new file mode 100644 index 0000000000..cf0d7eb6a3 --- /dev/null +++ b/org.eclipse.jgit.test/tst/org/eclipse/jgit/storage/file/PackWriterTest.java @@ -0,0 +1,534 @@ +/* + * Copyright (C) 2008, Marek Zawirski + * 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.FileOutputStream; +import java.io.IOException; +import java.io.InputStream; +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.LinkedList; +import java.util.List; + +import org.eclipse.jgit.errors.MissingObjectException; +import org.eclipse.jgit.lib.ObjectId; +import org.eclipse.jgit.lib.PackWriter; +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.transport.IndexPack; +import org.eclipse.jgit.util.JGitTestUtil; + +public class PackWriterTest extends SampleDataRepositoryTestCase { + + private static final List EMPTY_LIST_OBJECT = Collections + . emptyList(); + + private static final List EMPTY_LIST_REVS = Collections + . emptyList(); + + private PackWriter writer; + + private ByteArrayOutputStream os; + + private File packBase; + + private File packFile; + + private File indexFile; + + private PackFile pack; + + public void setUp() throws Exception { + super.setUp(); + os = new ByteArrayOutputStream(); + 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()); + } + + /** + * Test constructor for exceptions, default settings, initialization. + */ + public void testContructor() { + assertEquals(false, writer.isDeltaBaseAsOffset()); + assertEquals(true, writer.isReuseDeltas()); + assertEquals(true, writer.isReuseObjects()); + assertEquals(0, writer.getObjectsNumber()); + } + + /** + * Change default settings and verify them. + */ + public void testModifySettings() { + writer.setDeltaBaseAsOffset(true); + writer.setReuseDeltas(false); + writer.setReuseObjects(false); + + assertEquals(true, writer.isDeltaBaseAsOffset()); + assertEquals(false, writer.isReuseDeltas()); + assertEquals(false, writer.isReuseObjects()); + } + + /** + * Write empty pack by providing empty sets of interesting/uninteresting + * objects and check for correct format. + * + * @throws IOException + */ + public void testWriteEmptyPack1() throws IOException { + createVerifyOpenPack(EMPTY_LIST_OBJECT, EMPTY_LIST_OBJECT, false, false); + + assertEquals(0, writer.getObjectsNumber()); + assertEquals(0, pack.getObjectCount()); + assertEquals("da39a3ee5e6b4b0d3255bfef95601890afd80709", writer + .computeName().name()); + } + + /** + * Write empty pack by providing empty iterator of objects to write and + * check for correct format. + * + * @throws IOException + */ + public void testWriteEmptyPack2() throws IOException { + createVerifyOpenPack(EMPTY_LIST_REVS.iterator()); + + assertEquals(0, writer.getObjectsNumber()); + assertEquals(0, pack.getObjectCount()); + } + + /** + * Try to pass non-existing object as uninteresting, with non-ignoring + * setting. + * + * @throws IOException + */ + public void testNotIgnoreNonExistingObjects() throws IOException { + final ObjectId nonExisting = ObjectId + .fromString("0000000000000000000000000000000000000001"); + try { + createVerifyOpenPack(EMPTY_LIST_OBJECT, Collections.nCopies(1, + nonExisting), false, false); + fail("Should have thrown MissingObjectException"); + } catch (MissingObjectException x) { + // expected + } + } + + /** + * Try to pass non-existing object as uninteresting, with ignoring setting. + * + * @throws IOException + */ + public void testIgnoreNonExistingObjects() throws IOException { + final ObjectId nonExisting = ObjectId + .fromString("0000000000000000000000000000000000000001"); + createVerifyOpenPack(EMPTY_LIST_OBJECT, Collections.nCopies(1, + nonExisting), false, true); + // shouldn't throw anything + } + + /** + * Create pack basing on only interesting objects, then precisely verify + * content. No delta reuse here. + * + * @throws IOException + */ + public void testWritePack1() throws IOException { + writer.setReuseDeltas(false); + writeVerifyPack1(); + } + + /** + * Test writing pack without object reuse. Pack content/preparation as in + * {@link #testWritePack1()}. + * + * @throws IOException + */ + public void testWritePack1NoObjectReuse() throws IOException { + writer.setReuseDeltas(false); + writer.setReuseObjects(false); + writeVerifyPack1(); + } + + /** + * Create pack basing on both interesting and uninteresting objects, then + * precisely verify content. No delta reuse here. + * + * @throws IOException + */ + public void testWritePack2() throws IOException { + writeVerifyPack2(false); + } + + /** + * Test pack writing with deltas reuse, delta-base first rule. Pack + * content/preparation as in {@link #testWritePack2()}. + * + * @throws IOException + */ + public void testWritePack2DeltasReuseRefs() throws IOException { + writeVerifyPack2(true); + } + + /** + * Test pack writing with delta reuse. Delta bases referred as offsets. Pack + * configuration as in {@link #testWritePack2DeltasReuseRefs()}. + * + * @throws IOException + */ + public void testWritePack2DeltasReuseOffsets() throws IOException { + writer.setDeltaBaseAsOffset(true); + writeVerifyPack2(true); + } + + /** + * Test pack writing with delta reuse. Raw-data copy (reuse) is made on a + * pack with CRC32 index. Pack configuration as in + * {@link #testWritePack2DeltasReuseRefs()}. + * + * @throws IOException + */ + public void testWritePack2DeltasCRC32Copy() throws IOException { + final File packDir = new File(db.getObjectDatabase().getDirectory(), "pack"); + final File crc32Pack = new File(packDir, + "pack-34be9032ac282b11fa9babdc2b2a93ca996c9c2f.pack"); + final File crc32Idx = new File(packDir, + "pack-34be9032ac282b11fa9babdc2b2a93ca996c9c2f.idx"); + copyFile(JGitTestUtil.getTestResourceFile( + "pack-34be9032ac282b11fa9babdc2b2a93ca996c9c2f.idxV2"), + crc32Idx); + db.openPack(crc32Pack, crc32Idx); + + writeVerifyPack2(true); + } + + /** + * Create pack basing on fixed objects list, then precisely verify content. + * No delta reuse here. + * + * @throws IOException + * @throws MissingObjectException + * + */ + public void testWritePack3() throws MissingObjectException, IOException { + writer.setReuseDeltas(false); + final ObjectId forcedOrder[] = new ObjectId[] { + ObjectId.fromString("82c6b885ff600be425b4ea96dee75dca255b69e7"), + ObjectId.fromString("c59759f143fb1fe21c197981df75a7ee00290799"), + ObjectId.fromString("aabf2ffaec9b497f0950352b3e582d73035c2035"), + ObjectId.fromString("902d5476fa249b7abc9d84c611577a81381f0327"), + ObjectId.fromString("5b6e7c66c276e7610d4a73c70ec1a1f7c1003259"), + ObjectId.fromString("6ff87c4664981e4397625791c8ea3bbb5f2279a3") }; + final RevWalk parser = new RevWalk(db); + final RevObject forcedOrderRevs[] = new RevObject[forcedOrder.length]; + for (int i = 0; i < forcedOrder.length; i++) + forcedOrderRevs[i] = parser.parseAny(forcedOrder[i]); + + createVerifyOpenPack(Arrays.asList(forcedOrderRevs).iterator()); + + assertEquals(forcedOrder.length, writer.getObjectsNumber()); + verifyObjectsOrder(forcedOrder); + assertEquals("ed3f96b8327c7c66b0f8f70056129f0769323d86", writer + .computeName().name()); + } + + /** + * Another pack creation: basing on both interesting and uninteresting + * objects. No delta reuse possible here, as this is a specific case when we + * write only 1 commit, associated with 1 tree, 1 blob. + * + * @throws IOException + */ + public void testWritePack4() throws IOException { + writeVerifyPack4(false); + } + + /** + * Test thin pack writing: 1 blob delta base is on objects edge. Pack + * configuration as in {@link #testWritePack4()}. + * + * @throws IOException + */ + public void testWritePack4ThinPack() throws IOException { + writeVerifyPack4(true); + } + + /** + * Compare sizes of packs created using {@link #testWritePack2()} and + * {@link #testWritePack2DeltasReuseRefs()}. The pack using deltas should + * be smaller. + * + * @throws Exception + */ + public void testWritePack2SizeDeltasVsNoDeltas() throws Exception { + testWritePack2(); + final long sizePack2NoDeltas = os.size(); + tearDown(); + setUp(); + testWritePack2DeltasReuseRefs(); + final long sizePack2DeltasRefs = os.size(); + + assertTrue(sizePack2NoDeltas > sizePack2DeltasRefs); + } + + /** + * Compare sizes of packs created using + * {@link #testWritePack2DeltasReuseRefs()} and + * {@link #testWritePack2DeltasReuseOffsets()}. The pack with delta bases + * written as offsets should be smaller. + * + * @throws Exception + */ + public void testWritePack2SizeOffsetsVsRefs() throws Exception { + testWritePack2DeltasReuseRefs(); + final long sizePack2DeltasRefs = os.size(); + tearDown(); + setUp(); + testWritePack2DeltasReuseOffsets(); + final long sizePack2DeltasOffsets = os.size(); + + assertTrue(sizePack2DeltasRefs > sizePack2DeltasOffsets); + } + + /** + * Compare sizes of packs created using {@link #testWritePack4()} and + * {@link #testWritePack4ThinPack()}. Obviously, the thin pack should be + * smaller. + * + * @throws Exception + */ + public void testWritePack4SizeThinVsNoThin() throws Exception { + testWritePack4(); + final long sizePack4 = os.size(); + tearDown(); + setUp(); + testWritePack4ThinPack(); + final long sizePack4Thin = os.size(); + + assertTrue(sizePack4 > sizePack4Thin); + } + + public void testWriteIndex() throws Exception { + writer.setIndexVersion(2); + writeVerifyPack4(false); + + // Validate that IndexPack came up with the right CRC32 value. + final PackIndex idx1 = PackIndex.open(indexFile); + assertTrue(idx1 instanceof PackIndexV2); + assertEquals(0x4743F1E4L, idx1.findCRC32(ObjectId + .fromString("82c6b885ff600be425b4ea96dee75dca255b69e7"))); + + // Validate that an index written by PackWriter is the same. + final File idx2File = new File(indexFile.getAbsolutePath() + ".2"); + final FileOutputStream is = new FileOutputStream(idx2File); + try { + writer.writeIndex(is); + } finally { + is.close(); + } + final PackIndex idx2 = PackIndex.open(idx2File); + assertTrue(idx2 instanceof PackIndexV2); + assertEquals(idx1.getObjectCount(), idx2.getObjectCount()); + assertEquals(idx1.getOffset64Count(), idx2.getOffset64Count()); + + for (int i = 0; i < idx1.getObjectCount(); i++) { + final ObjectId id = idx1.getObjectId(i); + assertEquals(id, idx2.getObjectId(i)); + assertEquals(idx1.findOffset(id), idx2.findOffset(id)); + assertEquals(idx1.findCRC32(id), idx2.findCRC32(id)); + } + } + + // TODO: testWritePackDeltasCycle() + // TODO: testWritePackDeltasDepth() + + private void writeVerifyPack1() throws IOException { + final LinkedList interestings = new LinkedList(); + interestings.add(ObjectId + .fromString("82c6b885ff600be425b4ea96dee75dca255b69e7")); + createVerifyOpenPack(interestings, EMPTY_LIST_OBJECT, false, false); + + final ObjectId expectedOrder[] = new ObjectId[] { + ObjectId.fromString("82c6b885ff600be425b4ea96dee75dca255b69e7"), + ObjectId.fromString("c59759f143fb1fe21c197981df75a7ee00290799"), + ObjectId.fromString("540a36d136cf413e4b064c2b0e0a4db60f77feab"), + ObjectId.fromString("aabf2ffaec9b497f0950352b3e582d73035c2035"), + ObjectId.fromString("902d5476fa249b7abc9d84c611577a81381f0327"), + ObjectId.fromString("4b825dc642cb6eb9a060e54bf8d69288fbee4904"), + ObjectId.fromString("5b6e7c66c276e7610d4a73c70ec1a1f7c1003259"), + ObjectId.fromString("6ff87c4664981e4397625791c8ea3bbb5f2279a3") }; + + assertEquals(expectedOrder.length, writer.getObjectsNumber()); + verifyObjectsOrder(expectedOrder); + assertEquals("34be9032ac282b11fa9babdc2b2a93ca996c9c2f", writer + .computeName().name()); + } + + private void writeVerifyPack2(boolean deltaReuse) throws IOException { + writer.setReuseDeltas(deltaReuse); + final LinkedList interestings = new LinkedList(); + interestings.add(ObjectId + .fromString("82c6b885ff600be425b4ea96dee75dca255b69e7")); + final LinkedList uninterestings = new LinkedList(); + uninterestings.add(ObjectId + .fromString("540a36d136cf413e4b064c2b0e0a4db60f77feab")); + createVerifyOpenPack(interestings, uninterestings, false, false); + + final ObjectId expectedOrder[] = new ObjectId[] { + ObjectId.fromString("82c6b885ff600be425b4ea96dee75dca255b69e7"), + ObjectId.fromString("c59759f143fb1fe21c197981df75a7ee00290799"), + ObjectId.fromString("aabf2ffaec9b497f0950352b3e582d73035c2035"), + ObjectId.fromString("902d5476fa249b7abc9d84c611577a81381f0327"), + ObjectId.fromString("5b6e7c66c276e7610d4a73c70ec1a1f7c1003259"), + ObjectId.fromString("6ff87c4664981e4397625791c8ea3bbb5f2279a3") }; + if (deltaReuse) { + // objects order influenced (swapped) by delta-base first rule + ObjectId temp = expectedOrder[4]; + expectedOrder[4] = expectedOrder[5]; + expectedOrder[5] = temp; + } + assertEquals(expectedOrder.length, writer.getObjectsNumber()); + verifyObjectsOrder(expectedOrder); + assertEquals("ed3f96b8327c7c66b0f8f70056129f0769323d86", writer + .computeName().name()); + } + + private void writeVerifyPack4(final boolean thin) throws IOException { + final LinkedList interestings = new LinkedList(); + interestings.add(ObjectId + .fromString("82c6b885ff600be425b4ea96dee75dca255b69e7")); + final LinkedList uninterestings = new LinkedList(); + uninterestings.add(ObjectId + .fromString("c59759f143fb1fe21c197981df75a7ee00290799")); + createVerifyOpenPack(interestings, uninterestings, thin, false); + + final ObjectId writtenObjects[] = new ObjectId[] { + ObjectId.fromString("82c6b885ff600be425b4ea96dee75dca255b69e7"), + ObjectId.fromString("aabf2ffaec9b497f0950352b3e582d73035c2035"), + ObjectId.fromString("5b6e7c66c276e7610d4a73c70ec1a1f7c1003259") }; + assertEquals(writtenObjects.length, writer.getObjectsNumber()); + ObjectId expectedObjects[]; + if (thin) { + expectedObjects = new ObjectId[4]; + System.arraycopy(writtenObjects, 0, expectedObjects, 0, + writtenObjects.length); + expectedObjects[3] = ObjectId + .fromString("6ff87c4664981e4397625791c8ea3bbb5f2279a3"); + + } else { + expectedObjects = writtenObjects; + } + verifyObjectsOrder(expectedObjects); + assertEquals("cded4b74176b4456afa456768b2b5aafb41c44fc", writer + .computeName().name()); + } + + private void createVerifyOpenPack(final Collection interestings, + final Collection uninterestings, final boolean thin, + final boolean ignoreMissingUninteresting) + throws MissingObjectException, IOException { + writer.setThin(thin); + writer.setIgnoreMissingUninteresting(ignoreMissingUninteresting); + writer.preparePack(interestings, uninterestings); + writer.writePack(os); + verifyOpenPack(thin); + } + + private void createVerifyOpenPack(final Iterator objectSource) + throws MissingObjectException, IOException { + writer.preparePack(objectSource); + writer.writePack(os); + verifyOpenPack(false); + } + + private void verifyOpenPack(final boolean thin) throws IOException { + if (thin) { + final InputStream is = new ByteArrayInputStream(os.toByteArray()); + final IndexPack indexer = new IndexPack(db, is, packBase); + try { + indexer.index(new TextProgressMonitor()); + fail("indexer should grumble about missing object"); + } catch (IOException x) { + // expected + } + } + final InputStream is = new ByteArrayInputStream(os.toByteArray()); + final IndexPack indexer = new IndexPack(db, is, packBase); + indexer.setKeepEmpty(true); + indexer.setFixThin(thin); + indexer.setIndexVersion(2); + indexer.index(new TextProgressMonitor()); + pack = new PackFile(indexFile, packFile); + } + + private void verifyObjectsOrder(final ObjectId objectsOrder[]) { + final List entries = new ArrayList(); + + for (MutableEntry me : pack) { + entries.add(me.cloneEntry()); + } + Collections.sort(entries, new Comparator() { + public int compare(MutableEntry o1, MutableEntry o2) { + return Long.signum(o1.getOffset() - o2.getOffset()); + } + }); + + int i = 0; + for (MutableEntry me : entries) { + assertEquals(objectsOrder[i++].toObjectId(), me.toObjectId()); + } + } +} diff --git a/org.eclipse.jgit.test/tst/org/eclipse/jgit/storage/file/RefDirectoryTest.java b/org.eclipse.jgit.test/tst/org/eclipse/jgit/storage/file/RefDirectoryTest.java new file mode 100644 index 0000000000..6e98541603 --- /dev/null +++ b/org.eclipse.jgit.test/tst/org/eclipse/jgit/storage/file/RefDirectoryTest.java @@ -0,0 +1,1028 @@ +/* + * 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 static org.eclipse.jgit.lib.Constants.HEAD; +import static org.eclipse.jgit.lib.Constants.R_HEADS; +import static org.eclipse.jgit.lib.Constants.R_TAGS; +import static org.eclipse.jgit.lib.Ref.Storage.LOOSE; +import static org.eclipse.jgit.lib.Ref.Storage.NEW; + +import java.io.File; +import java.io.IOException; +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; + +public class RefDirectoryTest extends LocalDiskRepositoryTestCase { + private Repository diskRepo; + + private TestRepository repo; + + private RefDirectory refdir; + + private RevCommit A; + + private RevCommit B; + + private RevTag v1_0; + + protected void setUp() throws Exception { + super.setUp(); + + diskRepo = createBareRepository(); + refdir = (RefDirectory) diskRepo.getRefDatabase(); + + repo = new TestRepository(diskRepo); + A = repo.commit().create(); + B = repo.commit(repo.getRevWalk().parseCommit(A)); + v1_0 = repo.tag("v1_0", B); + repo.getRevWalk().parseBody(v1_0); + } + + public void testCreate() throws IOException { + // setUp above created the directory. We just have to test it. + File d = diskRepo.getDirectory(); + assertSame(diskRepo, refdir.getRepository()); + + assertTrue(new File(d, "refs").isDirectory()); + assertTrue(new File(d, "logs").isDirectory()); + assertTrue(new File(d, "logs/refs").isDirectory()); + assertFalse(new File(d, "packed-refs").exists()); + + assertTrue(new File(d, "refs/heads").isDirectory()); + assertTrue(new File(d, "refs/tags").isDirectory()); + assertEquals(2, new File(d, "refs").list().length); + assertEquals(0, new File(d, "refs/heads").list().length); + assertEquals(0, new File(d, "refs/tags").list().length); + + assertTrue(new File(d, "logs/refs/heads").isDirectory()); + assertFalse(new File(d, "logs/HEAD").exists()); + assertEquals(0, new File(d, "logs/refs/heads").list().length); + + assertEquals("ref: refs/heads/master\n", read(new File(d, HEAD))); + } + + public void testGetRefs_EmptyDatabase() throws IOException { + Map all; + + all = refdir.getRefs(RefDatabase.ALL); + assertTrue("no references", all.isEmpty()); + + all = refdir.getRefs(R_HEADS); + assertTrue("no references", all.isEmpty()); + + all = refdir.getRefs(R_TAGS); + assertTrue("no references", all.isEmpty()); + } + + public void testGetRefs_HeadOnOneBranch() throws IOException { + Map all; + Ref head, master; + + writeLooseRef("refs/heads/master", A); + + all = refdir.getRefs(RefDatabase.ALL); + assertEquals(2, all.size()); + assertTrue("has HEAD", all.containsKey(HEAD)); + assertTrue("has master", all.containsKey("refs/heads/master")); + + head = all.get(HEAD); + master = all.get("refs/heads/master"); + + assertEquals(HEAD, head.getName()); + assertTrue(head.isSymbolic()); + assertSame(LOOSE, head.getStorage()); + assertSame("uses same ref as target", master, head.getTarget()); + + assertEquals("refs/heads/master", master.getName()); + assertFalse(master.isSymbolic()); + assertSame(LOOSE, master.getStorage()); + assertEquals(A, master.getObjectId()); + } + + public void testGetRefs_DeatchedHead1() throws IOException { + Map all; + Ref head; + + writeLooseRef(HEAD, A); + BUG_WorkAroundRacyGitIssues(HEAD); + + all = refdir.getRefs(RefDatabase.ALL); + assertEquals(1, all.size()); + assertTrue("has HEAD", all.containsKey(HEAD)); + + head = all.get(HEAD); + + assertEquals(HEAD, head.getName()); + assertFalse(head.isSymbolic()); + assertSame(LOOSE, head.getStorage()); + assertEquals(A, head.getObjectId()); + } + + public void testGetRefs_DeatchedHead2() throws IOException { + Map all; + Ref head, master; + + writeLooseRef(HEAD, A); + writeLooseRef("refs/heads/master", B); + BUG_WorkAroundRacyGitIssues(HEAD); + + all = refdir.getRefs(RefDatabase.ALL); + assertEquals(2, all.size()); + + head = all.get(HEAD); + master = all.get("refs/heads/master"); + + assertEquals(HEAD, head.getName()); + assertFalse(head.isSymbolic()); + assertSame(LOOSE, head.getStorage()); + assertEquals(A, head.getObjectId()); + + assertEquals("refs/heads/master", master.getName()); + assertFalse(master.isSymbolic()); + assertSame(LOOSE, master.getStorage()); + assertEquals(B, master.getObjectId()); + } + + public void testGetRefs_DeeplyNestedBranch() throws IOException { + String name = "refs/heads/a/b/c/d/e/f/g/h/i/j/k"; + Map all; + Ref r; + + writeLooseRef(name, A); + + all = refdir.getRefs(RefDatabase.ALL); + assertEquals(1, all.size()); + + r = all.get(name); + assertEquals(name, r.getName()); + assertFalse(r.isSymbolic()); + assertSame(LOOSE, r.getStorage()); + assertEquals(A, r.getObjectId()); + } + + public void testGetRefs_HeadBranchNotBorn() throws IOException { + Map all; + Ref a, b; + + writeLooseRef("refs/heads/A", A); + writeLooseRef("refs/heads/B", B); + + all = refdir.getRefs(RefDatabase.ALL); + assertEquals(2, all.size()); + assertFalse("no HEAD", all.containsKey(HEAD)); + + a = all.get("refs/heads/A"); + b = all.get("refs/heads/B"); + + assertEquals(A, a.getObjectId()); + assertEquals(B, b.getObjectId()); + + assertEquals("refs/heads/A", a.getName()); + assertEquals("refs/heads/B", b.getName()); + } + + public void testGetRefs_LooseOverridesPacked() throws IOException { + Map heads; + Ref a; + + writeLooseRef("refs/heads/master", B); + writePackedRef("refs/heads/master", A); + + heads = refdir.getRefs(R_HEADS); + assertEquals(1, heads.size()); + + a = heads.get("master"); + assertEquals("refs/heads/master", a.getName()); + assertEquals(B, a.getObjectId()); + } + + public void testGetRefs_IgnoresGarbageRef1() throws IOException { + Map heads; + Ref a; + + writeLooseRef("refs/heads/A", A); + write(new File(diskRepo.getDirectory(), "refs/heads/bad"), "FAIL\n"); + + heads = refdir.getRefs(RefDatabase.ALL); + assertEquals(1, heads.size()); + + a = heads.get("refs/heads/A"); + assertEquals("refs/heads/A", a.getName()); + assertEquals(A, a.getObjectId()); + } + + public void testGetRefs_IgnoresGarbageRef2() throws IOException { + Map heads; + Ref a; + + writeLooseRef("refs/heads/A", A); + write(new File(diskRepo.getDirectory(), "refs/heads/bad"), ""); + + heads = refdir.getRefs(RefDatabase.ALL); + assertEquals(1, heads.size()); + + a = heads.get("refs/heads/A"); + assertEquals("refs/heads/A", a.getName()); + assertEquals(A, a.getObjectId()); + } + + public void testGetRefs_IgnoresGarbageRef3() throws IOException { + Map heads; + Ref a; + + writeLooseRef("refs/heads/A", A); + write(new File(diskRepo.getDirectory(), "refs/heads/bad"), "\n"); + + heads = refdir.getRefs(RefDatabase.ALL); + assertEquals(1, heads.size()); + + a = heads.get("refs/heads/A"); + assertEquals("refs/heads/A", a.getName()); + assertEquals(A, a.getObjectId()); + } + + public void testGetRefs_IgnoresGarbageRef4() throws IOException { + Map heads; + Ref a, b, c; + + writeLooseRef("refs/heads/A", A); + writeLooseRef("refs/heads/B", B); + writeLooseRef("refs/heads/C", A); + heads = refdir.getRefs(RefDatabase.ALL); + assertEquals(3, heads.size()); + assertTrue(heads.containsKey("refs/heads/A")); + assertTrue(heads.containsKey("refs/heads/B")); + assertTrue(heads.containsKey("refs/heads/C")); + + writeLooseRef("refs/heads/B", "FAIL\n"); + BUG_WorkAroundRacyGitIssues("refs/heads/B"); + + heads = refdir.getRefs(RefDatabase.ALL); + assertEquals(2, heads.size()); + + a = heads.get("refs/heads/A"); + b = heads.get("refs/heads/B"); + c = heads.get("refs/heads/C"); + + assertEquals("refs/heads/A", a.getName()); + assertEquals(A, a.getObjectId()); + + assertNull("no refs/heads/B", b); + + assertEquals("refs/heads/C", c.getName()); + assertEquals(A, c.getObjectId()); + } + + public void testGetRefs_InvalidName() throws IOException { + writeLooseRef("refs/heads/A", A); + + assertTrue("empty refs/heads", refdir.getRefs("refs/heads").isEmpty()); + assertTrue("empty objects", refdir.getRefs("objects").isEmpty()); + assertTrue("empty objects/", refdir.getRefs("objects/").isEmpty()); + } + + public void testGetRefs_HeadsOnly_AllLoose() throws IOException { + Map heads; + Ref a, b; + + writeLooseRef("refs/heads/A", A); + writeLooseRef("refs/heads/B", B); + writeLooseRef("refs/tags/v1.0", v1_0); + + heads = refdir.getRefs(R_HEADS); + assertEquals(2, heads.size()); + + a = heads.get("A"); + b = heads.get("B"); + + assertEquals("refs/heads/A", a.getName()); + assertEquals("refs/heads/B", b.getName()); + + assertEquals(A, a.getObjectId()); + assertEquals(B, b.getObjectId()); + } + + public void testGetRefs_HeadsOnly_AllPacked1() throws IOException { + Map heads; + Ref a; + + deleteLooseRef(HEAD); + writePackedRef("refs/heads/A", A); + + heads = refdir.getRefs(R_HEADS); + assertEquals(1, heads.size()); + + a = heads.get("A"); + + assertEquals("refs/heads/A", a.getName()); + assertEquals(A, a.getObjectId()); + } + + public void testGetRefs_HeadsOnly_SymrefToPacked() throws IOException { + Map heads; + Ref master, other; + + writeLooseRef("refs/heads/other", "ref: refs/heads/master\n"); + writePackedRef("refs/heads/master", A); + + heads = refdir.getRefs(R_HEADS); + assertEquals(2, heads.size()); + + master = heads.get("master"); + other = heads.get("other"); + + assertEquals("refs/heads/master", master.getName()); + assertEquals(A, master.getObjectId()); + + assertEquals("refs/heads/other", other.getName()); + assertEquals(A, other.getObjectId()); + assertSame(master, other.getTarget()); + } + + public void testGetRefs_HeadsOnly_Mixed() throws IOException { + Map heads; + Ref a, b; + + writeLooseRef("refs/heads/A", A); + writeLooseRef("refs/heads/B", B); + writePackedRef("refs/tags/v1.0", v1_0); + + heads = refdir.getRefs(R_HEADS); + assertEquals(2, heads.size()); + + a = heads.get("A"); + b = heads.get("B"); + + assertEquals("refs/heads/A", a.getName()); + assertEquals("refs/heads/B", b.getName()); + + assertEquals(A, a.getObjectId()); + assertEquals(B, b.getObjectId()); + } + + public void testGetRefs_TagsOnly_AllLoose() throws IOException { + Map tags; + Ref a; + + writeLooseRef("refs/heads/A", A); + writeLooseRef("refs/tags/v1.0", v1_0); + + tags = refdir.getRefs(R_TAGS); + assertEquals(1, tags.size()); + + a = tags.get("v1.0"); + + assertEquals("refs/tags/v1.0", a.getName()); + assertEquals(v1_0, a.getObjectId()); + } + + public void testGetRefs_TagsOnly_AllPacked() throws IOException { + Map tags; + Ref a; + + deleteLooseRef(HEAD); + writePackedRef("refs/tags/v1.0", v1_0); + + tags = refdir.getRefs(R_TAGS); + assertEquals(1, tags.size()); + + a = tags.get("v1.0"); + + assertEquals("refs/tags/v1.0", a.getName()); + assertEquals(v1_0, a.getObjectId()); + } + + public void testGetRefs_DiscoversNewLoose1() throws IOException { + Map orig, next; + Ref orig_r, next_r; + + writeLooseRef("refs/heads/master", A); + orig = refdir.getRefs(RefDatabase.ALL); + + writeLooseRef("refs/heads/next", B); + next = refdir.getRefs(RefDatabase.ALL); + + assertEquals(2, orig.size()); + assertEquals(3, next.size()); + + assertFalse(orig.containsKey("refs/heads/next")); + assertTrue(next.containsKey("refs/heads/next")); + + orig_r = orig.get("refs/heads/master"); + next_r = next.get("refs/heads/master"); + assertEquals(A, orig_r.getObjectId()); + assertSame("uses cached instance", orig_r, next_r); + assertSame("same HEAD", orig_r, orig.get(HEAD).getTarget()); + assertSame("same HEAD", orig_r, next.get(HEAD).getTarget()); + + next_r = next.get("refs/heads/next"); + assertSame(LOOSE, next_r.getStorage()); + assertEquals(B, next_r.getObjectId()); + } + + public void testGetRefs_DiscoversNewLoose2() throws IOException { + Map orig, next, news; + + writeLooseRef("refs/heads/pu", A); + orig = refdir.getRefs(RefDatabase.ALL); + + writeLooseRef("refs/heads/new/B", B); + news = refdir.getRefs("refs/heads/new/"); + next = refdir.getRefs(RefDatabase.ALL); + + assertEquals(1, orig.size()); + assertEquals(2, next.size()); + assertEquals(1, news.size()); + + assertTrue(orig.containsKey("refs/heads/pu")); + assertTrue(next.containsKey("refs/heads/pu")); + assertFalse(news.containsKey("refs/heads/pu")); + + assertFalse(orig.containsKey("refs/heads/new/B")); + assertTrue(next.containsKey("refs/heads/new/B")); + assertTrue(news.containsKey("B")); + } + + public void testGetRefs_DiscoversModifiedLoose() throws IOException { + Map all; + + writeLooseRef("refs/heads/master", A); + all = refdir.getRefs(RefDatabase.ALL); + assertEquals(A, all.get(HEAD).getObjectId()); + + writeLooseRef("refs/heads/master", B); + BUG_WorkAroundRacyGitIssues("refs/heads/master"); + all = refdir.getRefs(RefDatabase.ALL); + assertEquals(B, all.get(HEAD).getObjectId()); + } + + public void testGetRef_DiscoversModifiedLoose() throws IOException { + Map all; + + writeLooseRef("refs/heads/master", A); + all = refdir.getRefs(RefDatabase.ALL); + assertEquals(A, all.get(HEAD).getObjectId()); + + writeLooseRef("refs/heads/master", B); + BUG_WorkAroundRacyGitIssues("refs/heads/master"); + + Ref master = refdir.getRef("refs/heads/master"); + assertEquals(B, master.getObjectId()); + } + + public void testGetRefs_DiscoversDeletedLoose1() throws IOException { + Map orig, next; + Ref orig_r, next_r; + + writeLooseRef("refs/heads/B", B); + writeLooseRef("refs/heads/master", A); + orig = refdir.getRefs(RefDatabase.ALL); + + deleteLooseRef("refs/heads/B"); + next = refdir.getRefs(RefDatabase.ALL); + + assertEquals(3, orig.size()); + assertEquals(2, next.size()); + + assertTrue(orig.containsKey("refs/heads/B")); + assertFalse(next.containsKey("refs/heads/B")); + + orig_r = orig.get("refs/heads/master"); + next_r = next.get("refs/heads/master"); + assertEquals(A, orig_r.getObjectId()); + assertSame("uses cached instance", orig_r, next_r); + assertSame("same HEAD", orig_r, orig.get(HEAD).getTarget()); + assertSame("same HEAD", orig_r, next.get(HEAD).getTarget()); + + orig_r = orig.get("refs/heads/B"); + assertSame(LOOSE, orig_r.getStorage()); + assertEquals(B, orig_r.getObjectId()); + } + + public void testGetRef_DiscoversDeletedLoose() throws IOException { + Map all; + + writeLooseRef("refs/heads/master", A); + all = refdir.getRefs(RefDatabase.ALL); + assertEquals(A, all.get(HEAD).getObjectId()); + + deleteLooseRef("refs/heads/master"); + assertNull(refdir.getRef("refs/heads/master")); + assertTrue(refdir.getRefs(RefDatabase.ALL).isEmpty()); + } + + public void testGetRefs_DiscoversDeletedLoose2() throws IOException { + Map orig, next; + + writeLooseRef("refs/heads/master", A); + writeLooseRef("refs/heads/pu", B); + orig = refdir.getRefs(RefDatabase.ALL); + + deleteLooseRef("refs/heads/pu"); + next = refdir.getRefs(RefDatabase.ALL); + + assertEquals(3, orig.size()); + assertEquals(2, next.size()); + + assertTrue(orig.containsKey("refs/heads/pu")); + assertFalse(next.containsKey("refs/heads/pu")); + } + + public void testGetRefs_DiscoversDeletedLoose3() throws IOException { + Map orig, next; + + writeLooseRef("refs/heads/master", A); + writeLooseRef("refs/heads/next", B); + writeLooseRef("refs/heads/pu", B); + writeLooseRef("refs/tags/v1.0", v1_0); + orig = refdir.getRefs(RefDatabase.ALL); + + deleteLooseRef("refs/heads/pu"); + deleteLooseRef("refs/heads/next"); + next = refdir.getRefs(RefDatabase.ALL); + + assertEquals(5, orig.size()); + assertEquals(3, next.size()); + + assertTrue(orig.containsKey("refs/heads/pu")); + assertTrue(orig.containsKey("refs/heads/next")); + assertFalse(next.containsKey("refs/heads/pu")); + assertFalse(next.containsKey("refs/heads/next")); + } + + public void testGetRefs_DiscoversDeletedLoose4() throws IOException { + Map orig, next; + Ref orig_r, next_r; + + writeLooseRef("refs/heads/B", B); + writeLooseRef("refs/heads/master", A); + orig = refdir.getRefs(RefDatabase.ALL); + + deleteLooseRef("refs/heads/master"); + next = refdir.getRefs("refs/heads/"); + + assertEquals(3, orig.size()); + assertEquals(1, next.size()); + + assertTrue(orig.containsKey("refs/heads/B")); + assertTrue(orig.containsKey("refs/heads/master")); + assertTrue(next.containsKey("B")); + assertFalse(next.containsKey("master")); + + orig_r = orig.get("refs/heads/B"); + next_r = next.get("B"); + assertEquals(B, orig_r.getObjectId()); + assertSame("uses cached instance", orig_r, next_r); + } + + public void testGetRefs_DiscoversDeletedLoose5() throws IOException { + Map orig, next; + + writeLooseRef("refs/heads/master", A); + writeLooseRef("refs/heads/pu", B); + orig = refdir.getRefs(RefDatabase.ALL); + + deleteLooseRef("refs/heads/pu"); + writeLooseRef("refs/tags/v1.0", v1_0); + next = refdir.getRefs(RefDatabase.ALL); + + assertEquals(3, orig.size()); + assertEquals(3, next.size()); + + assertTrue(orig.containsKey("refs/heads/pu")); + assertFalse(orig.containsKey("refs/tags/v1.0")); + assertFalse(next.containsKey("refs/heads/pu")); + assertTrue(next.containsKey("refs/tags/v1.0")); + } + + public void testGetRefs_SkipsLockFiles() throws IOException { + Map all; + + writeLooseRef("refs/heads/master", A); + writeLooseRef("refs/heads/pu.lock", B); + all = refdir.getRefs(RefDatabase.ALL); + + assertEquals(2, all.size()); + + assertTrue(all.containsKey(HEAD)); + assertTrue(all.containsKey("refs/heads/master")); + assertFalse(all.containsKey("refs/heads/pu.lock")); + } + + public void testGetRefs_CycleInSymbolicRef() throws IOException { + Map all; + Ref r; + + writeLooseRef("refs/1", "ref: refs/2\n"); + writeLooseRef("refs/2", "ref: refs/3\n"); + writeLooseRef("refs/3", "ref: refs/4\n"); + writeLooseRef("refs/4", "ref: refs/5\n"); + writeLooseRef("refs/5", "ref: refs/end\n"); + writeLooseRef("refs/end", A); + + all = refdir.getRefs(RefDatabase.ALL); + r = all.get("refs/1"); + assertNotNull("has 1", r); + + assertEquals("refs/1", r.getName()); + assertEquals(A, r.getObjectId()); + assertTrue(r.isSymbolic()); + + r = r.getTarget(); + assertEquals("refs/2", r.getName()); + assertEquals(A, r.getObjectId()); + assertTrue(r.isSymbolic()); + + r = r.getTarget(); + assertEquals("refs/3", r.getName()); + assertEquals(A, r.getObjectId()); + assertTrue(r.isSymbolic()); + + r = r.getTarget(); + assertEquals("refs/4", r.getName()); + assertEquals(A, r.getObjectId()); + assertTrue(r.isSymbolic()); + + r = r.getTarget(); + assertEquals("refs/5", r.getName()); + assertEquals(A, r.getObjectId()); + assertTrue(r.isSymbolic()); + + r = r.getTarget(); + assertEquals("refs/end", r.getName()); + assertEquals(A, r.getObjectId()); + assertFalse(r.isSymbolic()); + + writeLooseRef("refs/5", "ref: refs/6\n"); + writeLooseRef("refs/6", "ref: refs/end\n"); + BUG_WorkAroundRacyGitIssues("refs/5"); + all = refdir.getRefs(RefDatabase.ALL); + r = all.get("refs/1"); + assertNull("mising 1 due to cycle", r); + } + + public void testGetRefs_PackedNotPeeled_Sorted() throws IOException { + Map all; + + writePackedRefs("" + // + A.name() + " refs/heads/master\n" + // + B.name() + " refs/heads/other\n" + // + v1_0.name() + " refs/tags/v1.0\n"); + all = refdir.getRefs(RefDatabase.ALL); + + assertEquals(4, all.size()); + final Ref head = all.get(HEAD); + final Ref master = all.get("refs/heads/master"); + final Ref other = all.get("refs/heads/other"); + final Ref tag = all.get("refs/tags/v1.0"); + + assertEquals(A, master.getObjectId()); + assertFalse(master.isPeeled()); + assertNull(master.getPeeledObjectId()); + + assertEquals(B, other.getObjectId()); + assertFalse(other.isPeeled()); + assertNull(other.getPeeledObjectId()); + + assertSame(master, head.getTarget()); + assertEquals(A, head.getObjectId()); + assertFalse(head.isPeeled()); + assertNull(head.getPeeledObjectId()); + + assertEquals(v1_0, tag.getObjectId()); + assertFalse(tag.isPeeled()); + assertNull(tag.getPeeledObjectId()); + } + + public void testGetRef_PackedNotPeeled_WrongSort() throws IOException { + writePackedRefs("" + // + v1_0.name() + " refs/tags/v1.0\n" + // + B.name() + " refs/heads/other\n" + // + A.name() + " refs/heads/master\n"); + + final Ref head = refdir.getRef(HEAD); + final Ref master = refdir.getRef("refs/heads/master"); + final Ref other = refdir.getRef("refs/heads/other"); + final Ref tag = refdir.getRef("refs/tags/v1.0"); + + assertEquals(A, master.getObjectId()); + assertFalse(master.isPeeled()); + assertNull(master.getPeeledObjectId()); + + assertEquals(B, other.getObjectId()); + assertFalse(other.isPeeled()); + assertNull(other.getPeeledObjectId()); + + assertSame(master, head.getTarget()); + assertEquals(A, head.getObjectId()); + assertFalse(head.isPeeled()); + assertNull(head.getPeeledObjectId()); + + assertEquals(v1_0, tag.getObjectId()); + assertFalse(tag.isPeeled()); + assertNull(tag.getPeeledObjectId()); + } + + public void testGetRefs_PackedWithPeeled() throws IOException { + Map all; + + writePackedRefs("# pack-refs with: peeled \n" + // + A.name() + " refs/heads/master\n" + // + B.name() + " refs/heads/other\n" + // + v1_0.name() + " refs/tags/v1.0\n" + // + "^" + v1_0.getObject().name() + "\n"); + all = refdir.getRefs(RefDatabase.ALL); + + assertEquals(4, all.size()); + final Ref head = all.get(HEAD); + final Ref master = all.get("refs/heads/master"); + final Ref other = all.get("refs/heads/other"); + final Ref tag = all.get("refs/tags/v1.0"); + + assertEquals(A, master.getObjectId()); + assertTrue(master.isPeeled()); + assertNull(master.getPeeledObjectId()); + + assertEquals(B, other.getObjectId()); + assertTrue(other.isPeeled()); + assertNull(other.getPeeledObjectId()); + + assertSame(master, head.getTarget()); + assertEquals(A, head.getObjectId()); + assertTrue(head.isPeeled()); + assertNull(head.getPeeledObjectId()); + + assertEquals(v1_0, tag.getObjectId()); + assertTrue(tag.isPeeled()); + assertEquals(v1_0.getObject(), tag.getPeeledObjectId()); + } + + public void testGetRef_EmptyDatabase() throws IOException { + Ref r; + + r = refdir.getRef(HEAD); + assertTrue(r.isSymbolic()); + assertSame(LOOSE, r.getStorage()); + assertEquals("refs/heads/master", r.getTarget().getName()); + assertSame(NEW, r.getTarget().getStorage()); + assertNull(r.getTarget().getObjectId()); + + assertNull(refdir.getRef("refs/heads/master")); + assertNull(refdir.getRef("refs/tags/v1.0")); + assertNull(refdir.getRef("FETCH_HEAD")); + assertNull(refdir.getRef("NOT.A.REF.NAME")); + assertNull(refdir.getRef("master")); + assertNull(refdir.getRef("v1.0")); + } + + public void testGetRef_FetchHead() throws IOException { + // This is an odd special case where we need to make sure we read + // exactly the first 40 bytes of the file and nothing further on + // that line, or the remainder of the file. + write(new File(diskRepo.getDirectory(), "FETCH_HEAD"), A.name() + + "\tnot-for-merge" + + "\tbranch 'master' of git://egit.eclipse.org/jgit\n"); + + Ref r = refdir.getRef("FETCH_HEAD"); + assertFalse(r.isSymbolic()); + assertEquals(A, r.getObjectId()); + assertEquals("FETCH_HEAD", r.getName()); + assertFalse(r.isPeeled()); + assertNull(r.getPeeledObjectId()); + } + + public void testGetRef_AnyHeadWithGarbage() throws IOException { + write(new File(diskRepo.getDirectory(), "refs/heads/A"), A.name() + + "012345 . this is not a standard reference\n" + + "#and even more junk\n"); + + Ref r = refdir.getRef("refs/heads/A"); + assertFalse(r.isSymbolic()); + assertEquals(A, r.getObjectId()); + assertEquals("refs/heads/A", r.getName()); + assertFalse(r.isPeeled()); + assertNull(r.getPeeledObjectId()); + } + + public void testGetRefs_CorruptSymbolicReference() throws IOException { + String name = "refs/heads/A"; + writeLooseRef(name, "ref: \n"); + assertTrue(refdir.getRefs(RefDatabase.ALL).isEmpty()); + } + + public void testGetRef_CorruptSymbolicReference() throws IOException { + String name = "refs/heads/A"; + writeLooseRef(name, "ref: \n"); + try { + refdir.getRef(name); + fail("read an invalid reference"); + } catch (IOException err) { + String msg = err.getMessage(); + assertEquals("Not a ref: " + name + ": ref:", msg); + } + } + + public void testGetRefs_CorruptObjectIdReference() throws IOException { + String name = "refs/heads/A"; + String content = "zoo" + A.name(); + writeLooseRef(name, content + "\n"); + assertTrue(refdir.getRefs(RefDatabase.ALL).isEmpty()); + } + + public void testGetRef_CorruptObjectIdReference() throws IOException { + String name = "refs/heads/A"; + String content = "zoo" + A.name(); + writeLooseRef(name, content + "\n"); + try { + refdir.getRef(name); + fail("read an invalid reference"); + } catch (IOException err) { + String msg = err.getMessage(); + assertEquals("Not a ref: " + name + ": " + content, msg); + } + } + + public void testIsNameConflicting() throws IOException { + writeLooseRef("refs/heads/a/b", A); + writePackedRef("refs/heads/q", B); + + // new references cannot replace an existing container + assertTrue(refdir.isNameConflicting("refs")); + assertTrue(refdir.isNameConflicting("refs/heads")); + assertTrue(refdir.isNameConflicting("refs/heads/a")); + + // existing reference is not conflicting + assertFalse(refdir.isNameConflicting("refs/heads/a/b")); + + // new references are not conflicting + assertFalse(refdir.isNameConflicting("refs/heads/a/d")); + assertFalse(refdir.isNameConflicting("refs/heads/master")); + + // existing reference must not be used as a container + assertTrue(refdir.isNameConflicting("refs/heads/a/b/c")); + assertTrue(refdir.isNameConflicting("refs/heads/q/master")); + } + + public void testPeelLooseTag() throws IOException { + writeLooseRef("refs/tags/v1_0", v1_0); + writeLooseRef("refs/tags/current", "ref: refs/tags/v1_0\n"); + + final Ref tag = refdir.getRef("refs/tags/v1_0"); + final Ref cur = refdir.getRef("refs/tags/current"); + + assertEquals(v1_0, tag.getObjectId()); + assertFalse(tag.isSymbolic()); + assertFalse(tag.isPeeled()); + assertNull(tag.getPeeledObjectId()); + + assertEquals(v1_0, cur.getObjectId()); + assertTrue(cur.isSymbolic()); + assertFalse(cur.isPeeled()); + assertNull(cur.getPeeledObjectId()); + + final Ref tag_p = refdir.peel(tag); + final Ref cur_p = refdir.peel(cur); + + assertNotSame(tag, tag_p); + assertFalse(tag_p.isSymbolic()); + assertTrue(tag_p.isPeeled()); + assertEquals(v1_0, tag_p.getObjectId()); + assertEquals(v1_0.getObject(), tag_p.getPeeledObjectId()); + assertSame(tag_p, refdir.peel(tag_p)); + + assertNotSame(cur, cur_p); + assertEquals("refs/tags/current", cur_p.getName()); + assertTrue(cur_p.isSymbolic()); + assertEquals("refs/tags/v1_0", cur_p.getTarget().getName()); + assertTrue(cur_p.isPeeled()); + assertEquals(v1_0, cur_p.getObjectId()); + assertEquals(v1_0.getObject(), cur_p.getPeeledObjectId()); + + // reuses cached peeling later, but not immediately due to + // the implementation so we have to fetch it once. + final Ref tag_p2 = refdir.getRef("refs/tags/v1_0"); + assertFalse(tag_p2.isSymbolic()); + assertTrue(tag_p2.isPeeled()); + assertEquals(v1_0, tag_p2.getObjectId()); + assertEquals(v1_0.getObject(), tag_p2.getPeeledObjectId()); + + assertSame(tag_p2, refdir.getRef("refs/tags/v1_0")); + assertSame(tag_p2, refdir.getRef("refs/tags/current").getTarget()); + assertSame(tag_p2, refdir.peel(tag_p2)); + } + + public void testPeelCommit() throws IOException { + writeLooseRef("refs/heads/master", A); + + Ref master = refdir.getRef("refs/heads/master"); + assertEquals(A, master.getObjectId()); + assertFalse(master.isPeeled()); + assertNull(master.getPeeledObjectId()); + + Ref master_p = refdir.peel(master); + assertNotSame(master, master_p); + assertEquals(A, master_p.getObjectId()); + assertTrue(master_p.isPeeled()); + assertNull(master_p.getPeeledObjectId()); + + // reuses cached peeling later, but not immediately due to + // the implementation so we have to fetch it once. + Ref master_p2 = refdir.getRef("refs/heads/master"); + assertNotSame(master, master_p2); + assertEquals(A, master_p2.getObjectId()); + assertTrue(master_p2.isPeeled()); + assertNull(master_p2.getPeeledObjectId()); + assertSame(master_p2, refdir.peel(master_p2)); + } + + private void writeLooseRef(String name, AnyObjectId id) throws IOException { + writeLooseRef(name, id.name() + "\n"); + } + + private void writeLooseRef(String name, String content) throws IOException { + write(new File(diskRepo.getDirectory(), name), content); + } + + private void writePackedRef(String name, AnyObjectId id) throws IOException { + writePackedRefs(id.name() + " " + name + "\n"); + } + + private void writePackedRefs(String content) throws IOException { + File pr = new File(diskRepo.getDirectory(), "packed-refs"); + write(pr, content); + } + + private void deleteLooseRef(String name) { + File path = new File(diskRepo.getDirectory(), name); + assertTrue("deleted " + name, path.delete()); + } + + /** + * Kick the timestamp of a local file. + *

+ * We shouldn't have to make these method calls. The cache is using file + * system timestamps, and on many systems unit tests run faster than the + * modification clock. Dumping the cache after we make an edit behind + * RefDirectory's back allows the tests to pass. + * + * @param name + * the file in the repository to force a time change on. + */ + private void BUG_WorkAroundRacyGitIssues(String name) { + File path = new File(diskRepo.getDirectory(), name); + long old = path.lastModified(); + long set = 1250379778668L; // Sat Aug 15 20:12:58 GMT-03:30 2009 + path.setLastModified(set); + assertTrue("time changed", old != path.lastModified()); + } +} diff --git a/org.eclipse.jgit.test/tst/org/eclipse/jgit/storage/file/RefUpdateTest.java b/org.eclipse.jgit.test/tst/org/eclipse/jgit/storage/file/RefUpdateTest.java new file mode 100644 index 0000000000..875c2e96f5 --- /dev/null +++ b/org.eclipse.jgit.test/tst/org/eclipse/jgit/storage/file/RefUpdateTest.java @@ -0,0 +1,853 @@ +/* + * Copyright (C) 2008, Charles O'Farrell + * Copyright (C) 2009-2010, Google Inc. + * Copyright (C) 2008-2009, Robin Rosenberg + * 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.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 { + + private void writeSymref(String src, String dst) throws IOException { + RefUpdate u = db.updateRef(src); + switch (u.link(dst)) { + case NEW: + case FORCED: + case NO_CHANGE: + break; + default: + fail("link " + src + " to " + dst); + } + } + + private RefUpdate updateRef(final String name) throws IOException { + final RefUpdate ref = db.updateRef(name); + ref.setNewObjectId(db.resolve(Constants.HEAD)); + return ref; + } + + private void delete(final RefUpdate ref, final Result expected) + throws IOException { + delete(ref, expected, true, true); + } + + private void delete(final RefUpdate ref, final Result expected, + final boolean exists, final boolean removed) throws IOException { + assertEquals(exists, db.getAllRefs().containsKey(ref.getName())); + assertEquals(expected, ref.delete()); + assertEquals(!removed, db.getAllRefs().containsKey(ref.getName())); + } + + public void testNoCacheObjectIdSubclass() throws IOException { + final String newRef = "refs/heads/abc"; + final RefUpdate ru = updateRef(newRef); + final RevCommit newid = new RevCommit(ru.getNewObjectId()) { + // empty + }; + ru.setNewObjectId(newid); + Result update = ru.update(); + assertEquals(Result.NEW, update); + final Ref r = db.getAllRefs().get(newRef); + assertNotNull(r); + assertEquals(newRef, r.getName()); + assertNotNull(r.getObjectId()); + assertNotSame(newid, r.getObjectId()); + assertSame(ObjectId.class, r.getObjectId().getClass()); + assertEquals(newid.copy(), r.getObjectId()); + List 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 reverseEntries2 = db.getReflogReader("HEAD").getReverseEntries(); + assertEquals(0, reverseEntries2.size()); + } + + public void testNewNamespaceConflictWithLoosePrefixNameExists() + throws IOException { + final String newRef = "refs/heads/z"; + final RefUpdate ru = updateRef(newRef); + final RevCommit newid = new RevCommit(ru.getNewObjectId()) { + // empty + }; + ru.setNewObjectId(newid); + Result update = ru.update(); + assertEquals(Result.NEW, update); + // end setup + final String newRef2 = "refs/heads/z/a"; + final RefUpdate ru2 = updateRef(newRef2); + final RevCommit newid2 = new RevCommit(ru2.getNewObjectId()) { + // empty + }; + ru.setNewObjectId(newid2); + Result update2 = ru2.update(); + assertEquals(Result.LOCK_FAILURE, update2); + assertEquals(1, db.getReflogReader("refs/heads/z").getReverseEntries().size()); + assertEquals(0, db.getReflogReader("HEAD").getReverseEntries().size()); + } + + public void testNewNamespaceConflictWithPackedPrefixNameExists() + throws IOException { + final String newRef = "refs/heads/master/x"; + final RefUpdate ru = updateRef(newRef); + final RevCommit newid = new RevCommit(ru.getNewObjectId()) { + // empty + }; + ru.setNewObjectId(newid); + Result update = ru.update(); + assertEquals(Result.LOCK_FAILURE, update); + assertNull(db.getReflogReader("refs/heads/master/x")); + assertEquals(0, db.getReflogReader("HEAD").getReverseEntries().size()); + } + + public void testNewNamespaceConflictWithLoosePrefixOfExisting() + throws IOException { + final String newRef = "refs/heads/z/a"; + final RefUpdate ru = updateRef(newRef); + final RevCommit newid = new RevCommit(ru.getNewObjectId()) { + // empty + }; + ru.setNewObjectId(newid); + Result update = ru.update(); + assertEquals(Result.NEW, update); + // end setup + final String newRef2 = "refs/heads/z"; + final RefUpdate ru2 = updateRef(newRef2); + final RevCommit newid2 = new RevCommit(ru2.getNewObjectId()) { + // empty + }; + ru.setNewObjectId(newid2); + Result update2 = ru2.update(); + assertEquals(Result.LOCK_FAILURE, update2); + assertEquals(1, db.getReflogReader("refs/heads/z/a").getReverseEntries().size()); + assertNull(db.getReflogReader("refs/heads/z")); + assertEquals(0, db.getReflogReader("HEAD").getReverseEntries().size()); + } + + public void testNewNamespaceConflictWithPackedPrefixOfExisting() + throws IOException { + final String newRef = "refs/heads/prefix"; + final RefUpdate ru = updateRef(newRef); + final RevCommit newid = new RevCommit(ru.getNewObjectId()) { + // empty + }; + ru.setNewObjectId(newid); + Result update = ru.update(); + assertEquals(Result.LOCK_FAILURE, update); + assertNull(db.getReflogReader("refs/heads/prefix")); + assertEquals(0, db.getReflogReader("HEAD").getReverseEntries().size()); + } + + /** + * Delete a ref that is pointed to by HEAD + * + * @throws IOException + */ + public void testDeleteHEADreferencedRef() throws IOException { + ObjectId pid = db.resolve("refs/heads/master^"); + RefUpdate updateRef = db.updateRef("refs/heads/master"); + updateRef.setNewObjectId(pid); + updateRef.setForceUpdate(true); + Result update = updateRef.update(); + assertEquals(Result.FORCED, update); // internal + + RefUpdate updateRef2 = db.updateRef("refs/heads/master"); + Result delete = updateRef2.delete(); + assertEquals(Result.REJECTED_CURRENT_BRANCH, delete); + assertEquals(pid, db.resolve("refs/heads/master")); + assertEquals(1,db.getReflogReader("refs/heads/master").getReverseEntries().size()); + assertEquals(0,db.getReflogReader("HEAD").getReverseEntries().size()); + } + + public void testLooseDelete() throws IOException { + final String newRef = "refs/heads/abc"; + RefUpdate ref = updateRef(newRef); + ref.update(); // create loose ref + ref = updateRef(newRef); // refresh + delete(ref, Result.NO_CHANGE); + assertNull(db.getReflogReader("refs/heads/abc")); + } + + public void testDeleteHead() throws IOException { + final RefUpdate ref = updateRef(Constants.HEAD); + delete(ref, Result.REJECTED_CURRENT_BRANCH, true, false); + assertEquals(0, db.getReflogReader("refs/heads/master").getReverseEntries().size()); + assertEquals(0, db.getReflogReader("HEAD").getReverseEntries().size()); + } + + /** + * Delete a loose ref and make sure the directory in refs is deleted too, + * and the reflog dir too + * + * @throws IOException + */ + public void testDeleteLooseAndItsDirectory() throws IOException { + ObjectId pid = db.resolve("refs/heads/c^"); + RefUpdate updateRef = db.updateRef("refs/heads/z/c"); + updateRef.setNewObjectId(pid); + updateRef.setForceUpdate(true); + updateRef.setRefLogMessage("new test ref", false); + Result update = updateRef.update(); + assertEquals(Result.NEW, update); // internal + assertTrue(new File(db.getDirectory(), Constants.R_HEADS + "z") + .exists()); + assertTrue(new File(db.getDirectory(), "logs/refs/heads/z").exists()); + + // The real test here + RefUpdate updateRef2 = db.updateRef("refs/heads/z/c"); + updateRef2.setForceUpdate(true); + Result delete = updateRef2.delete(); + assertEquals(Result.FORCED, delete); + assertNull(db.resolve("refs/heads/z/c")); + assertFalse(new File(db.getDirectory(), Constants.R_HEADS + "z") + .exists()); + assertFalse(new File(db.getDirectory(), "logs/refs/heads/z").exists()); + } + + public void testDeleteNotFound() throws IOException { + final RefUpdate ref = updateRef("refs/heads/xyz"); + delete(ref, Result.NEW, false, true); + } + + public void testDeleteFastForward() throws IOException { + final RefUpdate ref = updateRef("refs/heads/a"); + delete(ref, Result.FAST_FORWARD); + } + + public void testDeleteForce() throws IOException { + final RefUpdate ref = db.updateRef("refs/heads/b"); + ref.setNewObjectId(db.resolve("refs/heads/a")); + delete(ref, Result.REJECTED, true, false); + ref.setForceUpdate(true); + delete(ref, Result.FORCED); + } + + public void testRefKeySameAsName() { + Map allRefs = db.getAllRefs(); + for (Entry e : allRefs.entrySet()) { + assertEquals(e.getKey(), e.getValue().getName()); + + } + } + + /** + * Try modify a ref forward, fast forward + * + * @throws IOException + */ + public void testUpdateRefForward() throws IOException { + ObjectId ppid = db.resolve("refs/heads/master^"); + ObjectId pid = db.resolve("refs/heads/master"); + + RefUpdate updateRef = db.updateRef("refs/heads/master"); + updateRef.setNewObjectId(ppid); + updateRef.setForceUpdate(true); + Result update = updateRef.update(); + assertEquals(Result.FORCED, update); + assertEquals(ppid, db.resolve("refs/heads/master")); + + // real test + RefUpdate updateRef2 = db.updateRef("refs/heads/master"); + updateRef2.setNewObjectId(pid); + Result update2 = updateRef2.update(); + assertEquals(Result.FAST_FORWARD, update2); + assertEquals(pid, db.resolve("refs/heads/master")); + } + + /** + * Update the HEAD ref. Only it should be changed, not what it points to. + * + * @throws Exception + */ + public void testUpdateRefDetached() throws Exception { + ObjectId pid = db.resolve("refs/heads/master"); + ObjectId ppid = db.resolve("refs/heads/master^"); + RefUpdate updateRef = db.updateRef("HEAD", true); + updateRef.setForceUpdate(true); + updateRef.setNewObjectId(ppid); + Result update = updateRef.update(); + assertEquals(Result.FORCED, update); + assertEquals(ppid, db.resolve("HEAD")); + Ref ref = db.getRef("HEAD"); + assertEquals("HEAD", ref.getName()); + assertTrue("is detached", !ref.isSymbolic()); + + // the branch HEAD referred to is left untouched + assertEquals(pid, db.resolve("refs/heads/master")); + ReflogReader reflogReader = new ReflogReader(db, "HEAD"); + 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()); + assertEquals("GIT_COMMITTER_NAME", e.getWho().getName()); + assertEquals(1250379778000L, e.getWho().getWhen().getTime()); + } + + /** + * Update the HEAD ref when the referenced branch is unborn + * + * @throws Exception + */ + public void testUpdateRefDetachedUnbornHead() throws Exception { + ObjectId ppid = db.resolve("refs/heads/master^"); + writeSymref("HEAD", "refs/heads/unborn"); + RefUpdate updateRef = db.updateRef("HEAD", true); + updateRef.setForceUpdate(true); + updateRef.setNewObjectId(ppid); + Result update = updateRef.update(); + assertEquals(Result.NEW, update); + assertEquals(ppid, db.resolve("HEAD")); + Ref ref = db.getRef("HEAD"); + assertEquals("HEAD", ref.getName()); + assertTrue("is detached", !ref.isSymbolic()); + + // the branch HEAD referred to is left untouched + assertNull(db.resolve("refs/heads/unborn")); + ReflogReader reflogReader = new ReflogReader(db, "HEAD"); + 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()); + assertEquals("GIT_COMMITTER_NAME", e.getWho().getName()); + assertEquals(1250379778000L, e.getWho().getWhen().getTime()); + } + + /** + * Delete a ref that exists both as packed and loose. Make sure the ref + * cannot be resolved after delete. + * + * @throws IOException + */ + public void testDeleteLoosePacked() throws IOException { + ObjectId pid = db.resolve("refs/heads/c^"); + RefUpdate updateRef = db.updateRef("refs/heads/c"); + updateRef.setNewObjectId(pid); + updateRef.setForceUpdate(true); + Result update = updateRef.update(); + assertEquals(Result.FORCED, update); // internal + + // The real test here + RefUpdate updateRef2 = db.updateRef("refs/heads/c"); + updateRef2.setForceUpdate(true); + Result delete = updateRef2.delete(); + assertEquals(Result.FORCED, delete); + assertNull(db.resolve("refs/heads/c")); + } + + /** + * Try modify a ref to same + * + * @throws IOException + */ + public void testUpdateRefNoChange() throws IOException { + ObjectId pid = db.resolve("refs/heads/master"); + RefUpdate updateRef = db.updateRef("refs/heads/master"); + updateRef.setNewObjectId(pid); + Result update = updateRef.update(); + assertEquals(Result.NO_CHANGE, update); + assertEquals(pid, db.resolve("refs/heads/master")); + } + + /** + * Test case originating from + * bug 285991 + * + * Make sure the in memory cache is updated properly after + * update of symref. This one did not fail because the + * ref was packed due to implementation issues. + * + * @throws Exception + */ + public void testRefsCacheAfterUpdate() throws Exception { + // Do not use the defalt repo for this case. + Map allRefs = db.getAllRefs(); + ObjectId oldValue = db.resolve("HEAD"); + ObjectId newValue = db.resolve("HEAD^"); + // first make HEAD refer to loose ref + RefUpdate updateRef = db.updateRef(Constants.HEAD); + updateRef.setForceUpdate(true); + updateRef.setNewObjectId(newValue); + Result update = updateRef.update(); + assertEquals(Result.FORCED, update); + + // now update that ref + updateRef = db.updateRef(Constants.HEAD); + updateRef.setForceUpdate(true); + updateRef.setNewObjectId(oldValue); + update = updateRef.update(); + assertEquals(Result.FAST_FORWARD, update); + + allRefs = db.getAllRefs(); + Ref master = allRefs.get("refs/heads/master"); + Ref head = allRefs.get("HEAD"); + assertEquals("refs/heads/master", master.getName()); + assertEquals("HEAD", head.getName()); + assertTrue("is symbolic reference", head.isSymbolic()); + assertSame(master, head.getTarget()); + } + + /** + * Test case originating from + * bug 285991 + * + * Make sure the in memory cache is updated properly after + * update of symref. + * + * @throws Exception + */ + public void testRefsCacheAfterUpdateLooseOnly() throws Exception { + // Do not use the defalt repo for this case. + Map allRefs = db.getAllRefs(); + ObjectId oldValue = db.resolve("HEAD"); + writeSymref(Constants.HEAD, "refs/heads/newref"); + RefUpdate updateRef = db.updateRef(Constants.HEAD); + updateRef.setForceUpdate(true); + updateRef.setNewObjectId(oldValue); + Result update = updateRef.update(); + assertEquals(Result.NEW, update); + + allRefs = db.getAllRefs(); + Ref head = allRefs.get("HEAD"); + Ref newref = allRefs.get("refs/heads/newref"); + assertEquals("refs/heads/newref", newref.getName()); + assertEquals("HEAD", head.getName()); + assertTrue("is symbolic reference", head.isSymbolic()); + assertSame(newref, head.getTarget()); + } + + /** + * Try modify a ref, but get wrong expected old value + * + * @throws IOException + */ + public void testUpdateRefLockFailureWrongOldValue() throws IOException { + ObjectId pid = db.resolve("refs/heads/master"); + RefUpdate updateRef = db.updateRef("refs/heads/master"); + updateRef.setNewObjectId(pid); + updateRef.setExpectedOldObjectId(db.resolve("refs/heads/master^")); + Result update = updateRef.update(); + assertEquals(Result.LOCK_FAILURE, update); + assertEquals(pid, db.resolve("refs/heads/master")); + } + + /** + * Try modify a ref forward, fast forward, checking old value first + * + * @throws IOException + */ + public void testUpdateRefForwardWithCheck1() throws IOException { + ObjectId ppid = db.resolve("refs/heads/master^"); + ObjectId pid = db.resolve("refs/heads/master"); + + RefUpdate updateRef = db.updateRef("refs/heads/master"); + updateRef.setNewObjectId(ppid); + updateRef.setForceUpdate(true); + Result update = updateRef.update(); + assertEquals(Result.FORCED, update); + assertEquals(ppid, db.resolve("refs/heads/master")); + + // real test + RefUpdate updateRef2 = db.updateRef("refs/heads/master"); + updateRef2.setExpectedOldObjectId(ppid); + updateRef2.setNewObjectId(pid); + Result update2 = updateRef2.update(); + assertEquals(Result.FAST_FORWARD, update2); + assertEquals(pid, db.resolve("refs/heads/master")); + } + + /** + * Try modify a ref forward, fast forward, checking old commit first + * + * @throws IOException + */ + public void testUpdateRefForwardWithCheck2() throws IOException { + ObjectId ppid = db.resolve("refs/heads/master^"); + ObjectId pid = db.resolve("refs/heads/master"); + + RefUpdate updateRef = db.updateRef("refs/heads/master"); + updateRef.setNewObjectId(ppid); + updateRef.setForceUpdate(true); + Result update = updateRef.update(); + assertEquals(Result.FORCED, update); + assertEquals(ppid, db.resolve("refs/heads/master")); + + // real test + RevCommit old = new RevWalk(db).parseCommit(ppid); + RefUpdate updateRef2 = db.updateRef("refs/heads/master"); + updateRef2.setExpectedOldObjectId(old); + updateRef2.setNewObjectId(pid); + Result update2 = updateRef2.update(); + assertEquals(Result.FAST_FORWARD, update2); + assertEquals(pid, db.resolve("refs/heads/master")); + } + + /** + * Try modify a ref that is locked + * + * @throws IOException + */ + public void testUpdateRefLockFailureLocked() throws IOException { + ObjectId opid = db.resolve("refs/heads/master"); + ObjectId pid = db.resolve("refs/heads/master^"); + RefUpdate updateRef = db.updateRef("refs/heads/master"); + updateRef.setNewObjectId(pid); + LockFile lockFile1 = new LockFile(new File(db.getDirectory(),"refs/heads/master")); + try { + assertTrue(lockFile1.lock()); // precondition to test + Result update = updateRef.update(); + assertEquals(Result.LOCK_FAILURE, update); + assertEquals(opid, db.resolve("refs/heads/master")); + LockFile lockFile2 = new LockFile(new File(db.getDirectory(),"refs/heads/master")); + assertFalse(lockFile2.lock()); // was locked, still is + } finally { + lockFile1.unlock(); + } + } + + /** + * Try to delete a ref. Delete requires force. + * + * @throws IOException + */ + public void testDeleteLoosePackedRejected() throws IOException { + ObjectId pid = db.resolve("refs/heads/c^"); + ObjectId oldpid = db.resolve("refs/heads/c"); + RefUpdate updateRef = db.updateRef("refs/heads/c"); + updateRef.setNewObjectId(pid); + Result update = updateRef.update(); + assertEquals(Result.REJECTED, update); + assertEquals(oldpid, db.resolve("refs/heads/c")); + } + + public void testRenameBranchNoPreviousLog() throws IOException { + assertFalse("precondition, no log on old branchg", new File(db + .getDirectory(), "logs/refs/heads/b").exists()); + ObjectId rb = db.resolve("refs/heads/b"); + ObjectId oldHead = db.resolve(Constants.HEAD); + assertFalse(rb.equals(oldHead)); // assumption for this test + RefRename renameRef = db.renameRef("refs/heads/b", + "refs/heads/new/name"); + Result result = renameRef.rename(); + assertEquals(Result.RENAMED, result); + assertEquals(rb, db.resolve("refs/heads/new/name")); + assertNull(db.resolve("refs/heads/b")); + assertEquals(1, db.getReflogReader("new/name").getReverseEntries().size()); + assertEquals("Branch: renamed b to new/name", db.getReflogReader("new/name") + .getLastEntry().getComment()); + assertFalse(new File(db.getDirectory(), "logs/refs/heads/b").exists()); + assertEquals(oldHead, db.resolve(Constants.HEAD)); // unchanged + } + + public void testRenameBranchHasPreviousLog() throws IOException { + ObjectId rb = db.resolve("refs/heads/b"); + ObjectId oldHead = db.resolve(Constants.HEAD); + assertFalse("precondition for this test, branch b != HEAD", rb + .equals(oldHead)); + writeReflog(db, rb, rb, "Just a message", "refs/heads/b"); + assertTrue("log on old branch", new File(db.getDirectory(), + "logs/refs/heads/b").exists()); + RefRename renameRef = db.renameRef("refs/heads/b", + "refs/heads/new/name"); + Result result = renameRef.rename(); + assertEquals(Result.RENAMED, result); + assertEquals(rb, db.resolve("refs/heads/new/name")); + assertNull(db.resolve("refs/heads/b")); + assertEquals(2, db.getReflogReader("new/name").getReverseEntries().size()); + assertEquals("Branch: renamed b to new/name", db.getReflogReader("new/name") + .getLastEntry().getComment()); + assertEquals("Just a message", db.getReflogReader("new/name") + .getReverseEntries().get(1).getComment()); + assertFalse(new File(db.getDirectory(), "logs/refs/heads/b").exists()); + assertEquals(oldHead, db.resolve(Constants.HEAD)); // unchanged + } + + public void testRenameCurrentBranch() throws IOException { + ObjectId rb = db.resolve("refs/heads/b"); + writeSymref(Constants.HEAD, "refs/heads/b"); + ObjectId oldHead = db.resolve(Constants.HEAD); + assertTrue("internal test condition, b == HEAD", rb.equals(oldHead)); + writeReflog(db, rb, rb, "Just a message", "refs/heads/b"); + assertTrue("log on old branch", new File(db.getDirectory(), + "logs/refs/heads/b").exists()); + RefRename renameRef = db.renameRef("refs/heads/b", + "refs/heads/new/name"); + Result result = renameRef.rename(); + assertEquals(Result.RENAMED, result); + assertEquals(rb, db.resolve("refs/heads/new/name")); + assertNull(db.resolve("refs/heads/b")); + assertEquals("Branch: renamed b to new/name", db.getReflogReader( + "new/name").getLastEntry().getComment()); + assertFalse(new File(db.getDirectory(), "logs/refs/heads/b").exists()); + assertEquals(rb, db.resolve(Constants.HEAD)); + assertEquals(2, db.getReflogReader("new/name").getReverseEntries().size()); + assertEquals("Branch: renamed b to new/name", db.getReflogReader("new/name").getReverseEntries().get(0).getComment()); + assertEquals("Just a message", db.getReflogReader("new/name").getReverseEntries().get(1).getComment()); + } + + public void testRenameBranchAlsoInPack() throws IOException { + ObjectId rb = db.resolve("refs/heads/b"); + ObjectId rb2 = db.resolve("refs/heads/b~1"); + assertEquals(Ref.Storage.PACKED, db.getRef("refs/heads/b").getStorage()); + RefUpdate updateRef = db.updateRef("refs/heads/b"); + updateRef.setNewObjectId(rb2); + updateRef.setForceUpdate(true); + Result update = updateRef.update(); + assertEquals("internal check new ref is loose", Result.FORCED, update); + assertEquals(Ref.Storage.LOOSE, db.getRef("refs/heads/b").getStorage()); + writeReflog(db, rb, rb, "Just a message", "refs/heads/b"); + assertTrue("log on old branch", new File(db.getDirectory(), + "logs/refs/heads/b").exists()); + RefRename renameRef = db.renameRef("refs/heads/b", + "refs/heads/new/name"); + Result result = renameRef.rename(); + assertEquals(Result.RENAMED, result); + assertEquals(rb2, db.resolve("refs/heads/new/name")); + assertNull(db.resolve("refs/heads/b")); + assertEquals("Branch: renamed b to new/name", db.getReflogReader( + "new/name").getLastEntry().getComment()); + assertEquals(3, db.getReflogReader("refs/heads/new/name").getReverseEntries().size()); + assertEquals("Branch: renamed b to new/name", db.getReflogReader("refs/heads/new/name").getReverseEntries().get(0).getComment()); + assertEquals(0, db.getReflogReader("HEAD").getReverseEntries().size()); + // make sure b's log file is gone too. + assertFalse(new File(db.getDirectory(), "logs/refs/heads/b").exists()); + + // Create new Repository instance, to reread caches and make sure our + // assumptions are persistent. + Repository ndb = new FileRepository(db.getDirectory()); + assertEquals(rb2, ndb.resolve("refs/heads/new/name")); + assertNull(ndb.resolve("refs/heads/b")); + } + + public void tryRenameWhenLocked(String toLock, String fromName, + String toName, String headPointsTo) throws IOException { + // setup + writeSymref(Constants.HEAD, headPointsTo); + ObjectId oldfromId = db.resolve(fromName); + ObjectId oldHeadId = db.resolve(Constants.HEAD); + writeReflog(db, oldfromId, oldfromId, "Just a message", + fromName); + List oldFromLog = db + .getReflogReader(fromName).getReverseEntries(); + List oldHeadLog = oldHeadId != null ? db + .getReflogReader(Constants.HEAD).getReverseEntries() : null; + + assertTrue("internal check, we have a log", new File(db.getDirectory(), + "logs/" + fromName).exists()); + + // "someone" has branch X locked + LockFile lockFile = new LockFile(new File(db.getDirectory(), toLock)); + try { + assertTrue(lockFile.lock()); + + // Now this is our test + RefRename renameRef = db.renameRef(fromName, toName); + Result result = renameRef.rename(); + assertEquals(Result.LOCK_FAILURE, result); + + // Check that the involved refs are the same despite the failure + assertExists(false, toName); + if (!toLock.equals(toName)) + assertExists(false, toName + ".lock"); + assertExists(true, toLock + ".lock"); + if (!toLock.equals(fromName)) + assertExists(false, "logs/" + fromName + ".lock"); + assertExists(false, "logs/" + toName + ".lock"); + assertEquals(oldHeadId, db.resolve(Constants.HEAD)); + assertEquals(oldfromId, db.resolve(fromName)); + assertNull(db.resolve(toName)); + assertEquals(oldFromLog.toString(), db.getReflogReader(fromName) + .getReverseEntries().toString()); + if (oldHeadId != null) + assertEquals(oldHeadLog.toString(), db.getReflogReader( + Constants.HEAD).getReverseEntries().toString()); + } finally { + lockFile.unlock(); + } + } + + private void assertExists(boolean positive, String toName) { + assertEquals(toName + (positive ? " " : " does not ") + "exist", + positive, new File(db.getDirectory(), toName).exists()); + } + + public void testRenameBranchCannotLockAFileHEADisFromLockHEAD() + throws IOException { + tryRenameWhenLocked("HEAD", "refs/heads/b", "refs/heads/new/name", + "refs/heads/b"); + } + + public void testRenameBranchCannotLockAFileHEADisFromLockFrom() + throws IOException { + tryRenameWhenLocked("refs/heads/b", "refs/heads/b", + "refs/heads/new/name", "refs/heads/b"); + } + + public void testRenameBranchCannotLockAFileHEADisFromLockTo() + throws IOException { + tryRenameWhenLocked("refs/heads/new/name", "refs/heads/b", + "refs/heads/new/name", "refs/heads/b"); + } + + public void testRenameBranchCannotLockAFileHEADisToLockFrom() + throws IOException { + tryRenameWhenLocked("refs/heads/b", "refs/heads/b", + "refs/heads/new/name", "refs/heads/new/name"); + } + + public void testRenameBranchCannotLockAFileHEADisToLockTo() + throws IOException { + tryRenameWhenLocked("refs/heads/new/name", "refs/heads/b", + "refs/heads/new/name", "refs/heads/new/name"); + } + + public void testRenameBranchCannotLockAFileHEADisOtherLockFrom() + throws IOException { + tryRenameWhenLocked("refs/heads/b", "refs/heads/b", + "refs/heads/new/name", "refs/heads/a"); + } + + public void testRenameBranchCannotLockAFileHEADisOtherLockTo() + throws IOException { + tryRenameWhenLocked("refs/heads/new/name", "refs/heads/b", + "refs/heads/new/name", "refs/heads/a"); + } + + public void testRenameRefNameColission1avoided() throws IOException { + // setup + ObjectId rb = db.resolve("refs/heads/b"); + writeSymref(Constants.HEAD, "refs/heads/a"); + RefUpdate updateRef = db.updateRef("refs/heads/a"); + updateRef.setNewObjectId(rb); + updateRef.setRefLogMessage("Setup", false); + assertEquals(Result.FAST_FORWARD, updateRef.update()); + ObjectId oldHead = db.resolve(Constants.HEAD); + assertTrue(rb.equals(oldHead)); // assumption for this test + writeReflog(db, rb, rb, "Just a message", "refs/heads/a"); + assertTrue("internal check, we have a log", new File(db.getDirectory(), + "logs/refs/heads/a").exists()); + + // Now this is our test + RefRename renameRef = db.renameRef("refs/heads/a", "refs/heads/a/b"); + Result result = renameRef.rename(); + assertEquals(Result.RENAMED, result); + assertNull(db.resolve("refs/heads/a")); + assertEquals(rb, db.resolve("refs/heads/a/b")); + assertEquals(3, db.getReflogReader("a/b").getReverseEntries().size()); + assertEquals("Branch: renamed a to a/b", db.getReflogReader("a/b") + .getReverseEntries().get(0).getComment()); + assertEquals("Just a message", db.getReflogReader("a/b") + .getReverseEntries().get(1).getComment()); + assertEquals("Setup", db.getReflogReader("a/b").getReverseEntries() + .get(2).getComment()); + // same thing was logged to HEAD + assertEquals("Branch: renamed a to a/b", db.getReflogReader("HEAD") + .getReverseEntries().get(0).getComment()); + } + + public void testRenameRefNameColission2avoided() throws IOException { + // setup + ObjectId rb = db.resolve("refs/heads/b"); + writeSymref(Constants.HEAD, "refs/heads/prefix/a"); + RefUpdate updateRef = db.updateRef("refs/heads/prefix/a"); + updateRef.setNewObjectId(rb); + updateRef.setRefLogMessage("Setup", false); + updateRef.setForceUpdate(true); + assertEquals(Result.FORCED, updateRef.update()); + ObjectId oldHead = db.resolve(Constants.HEAD); + assertTrue(rb.equals(oldHead)); // assumption for this test + writeReflog(db, rb, rb, "Just a message", + "refs/heads/prefix/a"); + assertTrue("internal check, we have a log", new File(db.getDirectory(), + "logs/refs/heads/prefix/a").exists()); + + // Now this is our test + RefRename renameRef = db.renameRef("refs/heads/prefix/a", + "refs/heads/prefix"); + Result result = renameRef.rename(); + assertEquals(Result.RENAMED, result); + + assertNull(db.resolve("refs/heads/prefix/a")); + assertEquals(rb, db.resolve("refs/heads/prefix")); + assertEquals(3, db.getReflogReader("prefix").getReverseEntries().size()); + assertEquals("Branch: renamed prefix/a to prefix", db.getReflogReader( + "prefix").getReverseEntries().get(0).getComment()); + assertEquals("Just a message", db.getReflogReader("prefix") + .getReverseEntries().get(1).getComment()); + assertEquals("Setup", db.getReflogReader("prefix").getReverseEntries() + .get(2).getComment()); + assertEquals("Branch: renamed prefix/a to prefix", db.getReflogReader( + "HEAD").getReverseEntries().get(0).getComment()); + } + + private void writeReflog(Repository db, ObjectId oldId, ObjectId newId, + String msg, String refName) throws IOException { + RefDirectory refs = (RefDirectory) db.getRefDatabase(); + RefDirectoryUpdate update = refs.newUpdate(refName, true); + update.setNewObjectId(newId); + refs.log(update, msg, true); + } +} diff --git a/org.eclipse.jgit.test/tst/org/eclipse/jgit/storage/file/ReflogReaderTest.java b/org.eclipse.jgit.test/tst/org/eclipse/jgit/storage/file/ReflogReaderTest.java new file mode 100644 index 0000000000..1d268a4740 --- /dev/null +++ b/org.eclipse.jgit.test/tst/org/eclipse/jgit/storage/file/ReflogReaderTest.java @@ -0,0 +1,201 @@ +/* + * Copyright (C) 2009, Robin Rosenberg + * Copyright (C) 2009, Robin Rosenberg + * 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.FileNotFoundException; +import java.io.FileOutputStream; +import java.io.IOException; +import java.text.SimpleDateFormat; +import java.util.List; + +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 { + + static byte[] oneLine = "da85355dfc525c9f6f3927b876f379f46ccf826e 3e7549db262d1e836d9bf0af7e22355468f1717c A O Thor Too 1243028200 +0200\tcommit: Add a toString for debugging to RemoteRefUpdate\n" + .getBytes(); + + static byte[] twoLine = ("0000000000000000000000000000000000000000 c6734895958052a9dbc396cff4459dc1a25029ab A U Thor 1243028201 -0100\tbranch: Created from rr/renamebranchv4\n" + + "c6734895958052a9dbc396cff4459dc1a25029ab 54794942a18a237c57a80719afed44bb78172b10 Same A U Thor 1243028202 +0100\trebase finished: refs/heads/rr/renamebranch5 onto c6e3b9fe2da0293f11eae202ec35fb343191a82d\n") + .getBytes(); + + static byte[] twoLineWithAppendInProgress = ("0000000000000000000000000000000000000000 c6734895958052a9dbc396cff4459dc1a25029ab A U Thor 1243028201 -0100\tbranch: Created from rr/renamebranchv4\n" + + "c6734895958052a9dbc396cff4459dc1a25029ab 54794942a18a237c57a80719afed44bb78172b10 Same A U Thor 1243028202 +0100\trebase finished: refs/heads/rr/renamebranch5 onto c6e3b9fe2da0293f11eae202ec35fb343191a82d\n" + + "54794942a18a237c57a80719afed44bb78172b10 ") + .getBytes(); + + static byte[] aLine = "1111111111111111111111111111111111111111 3e7549db262d1e836d9bf0af7e22355468f1717c A U Thor 1243028201 -0100\tbranch: change to a\n" + .getBytes(); + + static byte[] masterLine = "2222222222222222222222222222222222222222 3e7549db262d1e836d9bf0af7e22355468f1717c A U Thor 1243028201 -0100\tbranch: change to master\n" + .getBytes(); + + static byte[] headLine = "3333333333333333333333333333333333333333 3e7549db262d1e836d9bf0af7e22355468f1717c A U Thor 1243028201 -0100\tbranch: change to HEAD\n" + .getBytes(); + + public void testReadOneLine() throws Exception { + setupReflog("logs/refs/heads/master", oneLine); + + ReflogReader reader = new ReflogReader(db, "refs/heads/master"); + Entry e = reader.getLastEntry(); + assertEquals(ObjectId + .fromString("da85355dfc525c9f6f3927b876f379f46ccf826e"), e + .getOldId()); + assertEquals(ObjectId + .fromString("3e7549db262d1e836d9bf0af7e22355468f1717c"), e + .getNewId()); + assertEquals("A O Thor Too", e.getWho().getName()); + assertEquals("authortoo@wri.tr", e.getWho().getEmailAddress()); + assertEquals(120, e.getWho().getTimeZoneOffset()); + assertEquals("2009-05-22T23:36:40", iso(e.getWho())); + assertEquals("commit: Add a toString for debugging to RemoteRefUpdate", + e.getComment()); + } + + private String iso(PersonIdent id) { + final SimpleDateFormat fmt; + fmt = new SimpleDateFormat("yyyy-MM-dd'T'HH:mm:ss"); + fmt.setTimeZone(id.getTimeZone()); + return fmt.format(id.getWhen()); + } + + public void testReadTwoLine() throws Exception { + setupReflog("logs/refs/heads/master", twoLine); + + ReflogReader reader = new ReflogReader(db, "refs/heads/master"); + List reverseEntries = reader.getReverseEntries(); + assertEquals(2, reverseEntries.size()); + Entry e = reverseEntries.get(0); + assertEquals(ObjectId + .fromString("c6734895958052a9dbc396cff4459dc1a25029ab"), e + .getOldId()); + assertEquals(ObjectId + .fromString("54794942a18a237c57a80719afed44bb78172b10"), e + .getNewId()); + assertEquals("Same A U Thor", e.getWho().getName()); + assertEquals("same.author@example.com", e.getWho().getEmailAddress()); + assertEquals(60, e.getWho().getTimeZoneOffset()); + assertEquals("2009-05-22T22:36:42", iso(e.getWho())); + assertEquals( + "rebase finished: refs/heads/rr/renamebranch5 onto c6e3b9fe2da0293f11eae202ec35fb343191a82d", + e.getComment()); + + e = reverseEntries.get(1); + assertEquals(ObjectId + .fromString("0000000000000000000000000000000000000000"), e + .getOldId()); + assertEquals(ObjectId + .fromString("c6734895958052a9dbc396cff4459dc1a25029ab"), e + .getNewId()); + assertEquals("A U Thor", e.getWho().getName()); + assertEquals("thor@committer.au", e.getWho().getEmailAddress()); + assertEquals(-60, e.getWho().getTimeZoneOffset()); + assertEquals("2009-05-22T20:36:41", iso(e.getWho())); + assertEquals("branch: Created from rr/renamebranchv4", e.getComment()); + } + + public void testReadWhileAppendIsInProgress() throws Exception { + setupReflog("logs/refs/heads/master", twoLineWithAppendInProgress); + ReflogReader reader = new ReflogReader(db, "refs/heads/master"); + List reverseEntries = reader.getReverseEntries(); + assertEquals(2, reverseEntries.size()); + Entry e = reverseEntries.get(0); + assertEquals(ObjectId + .fromString("c6734895958052a9dbc396cff4459dc1a25029ab"), e + .getOldId()); + assertEquals(ObjectId + .fromString("54794942a18a237c57a80719afed44bb78172b10"), e + .getNewId()); + assertEquals("Same A U Thor", e.getWho().getName()); + assertEquals("same.author@example.com", e.getWho().getEmailAddress()); + assertEquals(60, e.getWho().getTimeZoneOffset()); + assertEquals("2009-05-22T22:36:42", iso(e.getWho())); + assertEquals( + "rebase finished: refs/heads/rr/renamebranch5 onto c6e3b9fe2da0293f11eae202ec35fb343191a82d", + e.getComment()); + // while similar to testReadTwoLine, we can assume that if we get the last entry + // right, everything else is too + } + + + public void testReadRightLog() throws Exception { + setupReflog("logs/refs/heads/a", aLine); + setupReflog("logs/refs/heads/master", masterLine); + setupReflog("logs/HEAD", headLine); + assertEquals("branch: change to master", db.getReflogReader("master") + .getLastEntry().getComment()); + assertEquals("branch: change to a", db.getReflogReader("a") + .getLastEntry().getComment()); + assertEquals("branch: change to HEAD", db.getReflogReader("HEAD") + .getLastEntry().getComment()); + } + + public void testNoLog() throws Exception { + assertEquals(0, db.getReflogReader("master").getReverseEntries().size()); + assertNull(db.getReflogReader("master").getLastEntry()); + } + + private void setupReflog(String logName, byte[] data) + throws FileNotFoundException, IOException { + File logfile = new File(db.getDirectory(), logName); + if (!logfile.getParentFile().mkdirs() + && !logfile.getParentFile().isDirectory()) { + throw new IOException( + "oops, cannot create the directory for the test reflog file" + + logfile); + } + FileOutputStream fileOutputStream = new FileOutputStream(logfile); + try { + fileOutputStream.write(data); + } finally { + fileOutputStream.close(); + } + } + +} diff --git a/org.eclipse.jgit.test/tst/org/eclipse/jgit/storage/file/RepositorySetupWorkDirTest.java b/org.eclipse.jgit.test/tst/org/eclipse/jgit/storage/file/RepositorySetupWorkDirTest.java new file mode 100644 index 0000000000..caaeda2b03 --- /dev/null +++ b/org.eclipse.jgit.test/tst/org/eclipse/jgit/storage/file/RepositorySetupWorkDirTest.java @@ -0,0 +1,211 @@ +/* + * Copyright (C) 2010, Mathias Kinzler + * + * 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.errors.ConfigInvalidException; +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 + */ +public class RepositorySetupWorkDirTest extends LocalDiskRepositoryTestCase { + + public void testIsBare_CreateRepositoryFromArbitraryGitDir() + throws Exception { + File gitDir = getFile("workdir"); + assertTrue(new FileRepository(gitDir).isBare()); + } + + public void testNotBare_CreateRepositoryFromDotGitGitDir() throws Exception { + File gitDir = getFile("workdir", Constants.DOT_GIT); + Repository repo = new FileRepository(gitDir); + assertFalse(repo.isBare()); + assertWorkdirPath(repo, "workdir"); + assertGitdirPath(repo, "workdir", Constants.DOT_GIT); + } + + public void testWorkdirIsParentDir_CreateRepositoryFromDotGitGitDir() + throws Exception { + File gitDir = getFile("workdir", Constants.DOT_GIT); + Repository repo = new FileRepository(gitDir); + String workdir = repo.getWorkTree().getName(); + assertEquals(workdir, "workdir"); + } + + public void testNotBare_CreateRepositoryFromWorkDirOnly() throws Exception { + File workdir = getFile("workdir", "repo"); + FileRepository repo = new FileRepositoryBuilder().setWorkTree(workdir).build(); + assertFalse(repo.isBare()); + assertWorkdirPath(repo, "workdir", "repo"); + assertGitdirPath(repo, "workdir", "repo", Constants.DOT_GIT); + } + + public void testWorkdirIsDotGit_CreateRepositoryFromWorkDirOnly() + throws Exception { + File workdir = getFile("workdir", "repo"); + FileRepository repo = new FileRepositoryBuilder().setWorkTree(workdir).build(); + assertGitdirPath(repo, "workdir", "repo", Constants.DOT_GIT); + } + + public void testNotBare_CreateRepositoryFromGitDirOnlyWithWorktreeConfig() + throws Exception { + File gitDir = getFile("workdir", "repoWithConfig"); + File workTree = getFile("workdir", "treeRoot"); + setWorkTree(gitDir, workTree); + FileRepository repo = new FileRepositoryBuilder().setGitDir(gitDir).build(); + assertFalse(repo.isBare()); + assertWorkdirPath(repo, "workdir", "treeRoot"); + assertGitdirPath(repo, "workdir", "repoWithConfig"); + } + + public void testBare_CreateRepositoryFromGitDirOnlyWithBareConfigTrue() + throws Exception { + File gitDir = getFile("workdir", "repoWithConfig"); + setBare(gitDir, true); + FileRepository repo = new FileRepositoryBuilder().setGitDir(gitDir).build(); + assertTrue(repo.isBare()); + } + + public void testWorkdirIsParent_CreateRepositoryFromGitDirOnlyWithBareConfigFalse() + throws Exception { + File gitDir = getFile("workdir", "repoWithBareConfigTrue", "child"); + setBare(gitDir, false); + FileRepository repo = new FileRepositoryBuilder().setGitDir(gitDir).build(); + assertWorkdirPath(repo, "workdir", "repoWithBareConfigTrue"); + } + + public void testNotBare_CreateRepositoryFromGitDirOnlyWithBareConfigFalse() + throws Exception { + File gitDir = getFile("workdir", "repoWithBareConfigFalse", "child"); + setBare(gitDir, false); + FileRepository repo = new FileRepositoryBuilder().setGitDir(gitDir).build(); + assertFalse(repo.isBare()); + assertWorkdirPath(repo, "workdir", "repoWithBareConfigFalse"); + assertGitdirPath(repo, "workdir", "repoWithBareConfigFalse", "child"); + } + + public void testExceptionThrown_BareRepoGetWorkDir() throws Exception { + File gitDir = getFile("workdir"); + try { + new FileRepository(gitDir).getWorkTree(); + fail("Expected IllegalStateException missing"); + } catch (IllegalStateException e) { + // expected + } + } + + public void testExceptionThrown_BareRepoGetIndex() throws Exception { + File gitDir = getFile("workdir"); + try { + new FileRepository(gitDir).getIndex(); + fail("Expected IllegalStateException missing"); + } catch (IllegalStateException e) { + // expected + } + } + + public void testExceptionThrown_BareRepoGetIndexFile() throws Exception { + File gitDir = getFile("workdir"); + try { + new FileRepository(gitDir).getIndexFile(); + fail("Expected Exception missing"); + } catch (IllegalStateException e) { + // expected + } + } + + private File getFile(String... pathComponents) { + String rootPath = new File(new File("target"), "trash").getPath(); + for (String pathComponent : pathComponents) + rootPath = rootPath + File.separatorChar + pathComponent; + File result = new File(rootPath); + result.mkdir(); + return result; + } + + 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); + 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 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) + throws IOException { + File exp = getFile(expected).getCanonicalFile(); + File act = repo.getDirectory().getCanonicalFile(); + assertEquals("Wrong Git Directory", exp, act); + } + + private void assertWorkdirPath(Repository repo, String... expected) + throws IOException { + File exp = getFile(expected).getCanonicalFile(); + File act = repo.getWorkTree().getCanonicalFile(); + assertEquals("Wrong working Directory", exp, act); + } +} diff --git a/org.eclipse.jgit.test/tst/org/eclipse/jgit/storage/file/T0003_Basic.java b/org.eclipse.jgit.test/tst/org/eclipse/jgit/storage/file/T0003_Basic.java new file mode 100644 index 0000000000..c9013a6a01 --- /dev/null +++ b/org.eclipse.jgit.test/tst/org/eclipse/jgit/storage/file/T0003_Basic.java @@ -0,0 +1,769 @@ +/* + * Copyright (C) 2007, Dave Watson + * Copyright (C) 2007-2010, Robin Rosenberg + * Copyright (C) 2006-2008, Shawn O. Pearce + * 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.File; +import java.io.FileInputStream; +import java.io.FileReader; +import java.io.FileWriter; +import java.io.IOException; +import java.io.PrintWriter; + +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() { + final File gitdir = new File(trash, Constants.DOT_GIT); + final File objects = new File(gitdir, "objects"); + final File objects_pack = new File(objects, "pack"); + final File objects_info = new File(objects, "info"); + final File refs = new File(gitdir, "refs"); + final File refs_heads = new File(refs, "heads"); + final File refs_tags = new File(refs, "tags"); + final File HEAD = new File(gitdir, "HEAD"); + + assertTrue("Exists " + trash, trash.isDirectory()); + assertTrue("Exists " + objects, objects.isDirectory()); + assertTrue("Exists " + objects_pack, objects_pack.isDirectory()); + assertTrue("Exists " + objects_info, objects_info.isDirectory()); + assertEquals(2, objects.listFiles().length); + assertTrue("Exists " + refs, refs.isDirectory()); + assertTrue("Exists " + refs_heads, refs_heads.isDirectory()); + assertTrue("Exists " + refs_tags, refs_tags.isDirectory()); + assertTrue("Exists " + HEAD, HEAD.isFile()); + assertEquals(23, HEAD.length()); + } + + public void test000_openRepoBadArgs() throws IOException { + try { + 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", + e.getMessage()); + } + } + + /** + * Check the default rules for looking up directories and files within a + * repo when the gitDir is given. + * + * @throws IOException + */ + public void test000_openrepo_default_gitDirSet() throws IOException { + File repo1Parent = new File(trash.getParentFile(), "r1"); + Repository repo1initial = new FileRepository(new File(repo1Parent, Constants.DOT_GIT)); + repo1initial.create(); + repo1initial.close(); + + File theDir = new File(repo1Parent, Constants.DOT_GIT); + FileRepository r = new FileRepositoryBuilder().setGitDir(theDir).build(); + assertEqualsPath(theDir, r.getDirectory()); + assertEqualsPath(repo1Parent, r.getWorkTree()); + assertEqualsPath(new File(theDir, "index"), r.getIndexFile()); + assertEqualsPath(new File(theDir, "objects"), r.getObjectDatabase().getDirectory()); + } + + /** + * Check that we can pass both a git directory and a work tree + * repo when the gitDir is given. + * + * @throws IOException + */ + public void test000_openrepo_default_gitDirAndWorkTreeSet() throws IOException { + File repo1Parent = new File(trash.getParentFile(), "r1"); + Repository repo1initial = new FileRepository(new File(repo1Parent, Constants.DOT_GIT)); + repo1initial.create(); + repo1initial.close(); + + File theDir = new File(repo1Parent, Constants.DOT_GIT); + FileRepository r = new FileRepositoryBuilder().setGitDir(theDir) + .setWorkTree(repo1Parent.getParentFile()).build(); + assertEqualsPath(theDir, r.getDirectory()); + assertEqualsPath(repo1Parent.getParentFile(), r.getWorkTree()); + assertEqualsPath(new File(theDir, "index"), r.getIndexFile()); + assertEqualsPath(new File(theDir, "objects"), r.getObjectDatabase().getDirectory()); + } + + /** + * Check the default rules for looking up directories and files within a + * repo when the workTree is given. + * + * @throws IOException + */ + public void test000_openrepo_default_workDirSet() throws IOException { + File repo1Parent = new File(trash.getParentFile(), "r1"); + Repository repo1initial = new FileRepository(new File(repo1Parent, Constants.DOT_GIT)); + repo1initial.create(); + repo1initial.close(); + + File theDir = new File(repo1Parent, Constants.DOT_GIT); + FileRepository r = new FileRepositoryBuilder().setWorkTree(repo1Parent).build(); + assertEqualsPath(theDir, r.getDirectory()); + assertEqualsPath(repo1Parent, r.getWorkTree()); + assertEqualsPath(new File(theDir, "index"), r.getIndexFile()); + assertEqualsPath(new File(theDir, "objects"), r.getObjectDatabase().getDirectory()); + } + + /** + * Check that worktree config has an effect, given absolute path. + * + * @throws IOException + */ + public void test000_openrepo_default_absolute_workdirconfig() + throws IOException { + File repo1Parent = new File(trash.getParentFile(), "r1"); + File workdir = new File(trash.getParentFile(), "rw"); + workdir.mkdir(); + FileRepository repo1initial = new FileRepository(new File(repo1Parent, Constants.DOT_GIT)); + repo1initial.create(); + repo1initial.getConfig().setString("core", null, "worktree", + workdir.getAbsolutePath()); + repo1initial.getConfig().save(); + repo1initial.close(); + + File theDir = new File(repo1Parent, Constants.DOT_GIT); + FileRepository r = new FileRepositoryBuilder().setGitDir(theDir).build(); + assertEqualsPath(theDir, r.getDirectory()); + assertEqualsPath(workdir, r.getWorkTree()); + assertEqualsPath(new File(theDir, "index"), r.getIndexFile()); + assertEqualsPath(new File(theDir, "objects"), r.getObjectDatabase().getDirectory()); + } + + /** + * Check that worktree config has an effect, given a relative path. + * + * @throws IOException + */ + public void test000_openrepo_default_relative_workdirconfig() + throws IOException { + File repo1Parent = new File(trash.getParentFile(), "r1"); + File workdir = new File(trash.getParentFile(), "rw"); + workdir.mkdir(); + FileRepository repo1initial = new FileRepository(new File(repo1Parent, Constants.DOT_GIT)); + repo1initial.create(); + repo1initial.getConfig() + .setString("core", null, "worktree", "../../rw"); + repo1initial.getConfig().save(); + repo1initial.close(); + + File theDir = new File(repo1Parent, Constants.DOT_GIT); + FileRepository r = new FileRepositoryBuilder().setGitDir(theDir).build(); + assertEqualsPath(theDir, r.getDirectory()); + assertEqualsPath(workdir, r.getWorkTree()); + assertEqualsPath(new File(theDir, "index"), r.getIndexFile()); + assertEqualsPath(new File(theDir, "objects"), r.getObjectDatabase().getDirectory()); + } + + /** + * Check that the given index file is honored and the alternate object + * directories too + * + * @throws IOException + */ + public void test000_openrepo_alternate_index_file_and_objdirs() + throws IOException { + File repo1Parent = new File(trash.getParentFile(), "r1"); + File indexFile = new File(trash, "idx"); + File objDir = new File(trash, "../obj"); + 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); + FileRepository r = new FileRepositoryBuilder() // + .setGitDir(theDir).setObjectDirectory(objDir) // + .addAlternateObjectDirectory(altObjDir) // + .setIndexFile(indexFile) // + .build(); + assertEqualsPath(theDir, r.getDirectory()); + assertEqualsPath(theDir.getParentFile(), r.getWorkTree()); + assertEqualsPath(indexFile, r.getIndexFile()); + 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. + r.close(); + } + + protected void assertEqualsPath(File expected, File actual) + throws IOException { + assertEquals(expected.getCanonicalPath(), actual.getCanonicalPath()); + } + + public void test002_WriteEmptyTree() throws IOException { + // One of our test packs contains the empty tree object. If the pack is + // open when we create it we won't write the object file out as a loose + // object (as it already exists in the pack). + // + final Repository newdb = createBareRepository(); + final Tree t = new Tree(newdb); + t.accept(new WriteTree(trash, newdb), TreeEntry.MODIFIED_ONLY); + assertEquals("4b825dc642cb6eb9a060e54bf8d69288fbee4904", t.getId() + .name()); + final File o = new File(new File(new File(newdb.getDirectory(), + "objects"), "4b"), "825dc642cb6eb9a060e54bf8d69288fbee4904"); + assertTrue("Exists " + o, o.isFile()); + assertTrue("Read-only " + o, !o.canWrite()); + } + + public void test002_WriteEmptyTree2() throws IOException { + // File shouldn't exist as it is in a test pack. + // + final Tree t = new Tree(db); + t.accept(new WriteTree(trash, db), TreeEntry.MODIFIED_ONLY); + assertEquals("4b825dc642cb6eb9a060e54bf8d69288fbee4904", t.getId() + .name()); + final File o = new File(new File( + new File(db.getDirectory(), "objects"), "4b"), + "825dc642cb6eb9a060e54bf8d69288fbee4904"); + assertFalse("Exists " + o, o.isFile()); + } + + public void test003_WriteShouldBeEmptyTree() throws IOException { + final Tree t = new Tree(db); + final ObjectId emptyId = new ObjectWriter(db).writeBlob(new byte[0]); + t.addFile("should-be-empty").setId(emptyId); + t.accept(new WriteTree(trash, db), TreeEntry.MODIFIED_ONLY); + assertEquals("7bb943559a305bdd6bdee2cef6e5df2413c3d30a", t.getId() + .name()); + + File o; + o = new File(new File(new File(db.getDirectory(), "objects"), "7b"), + "b943559a305bdd6bdee2cef6e5df2413c3d30a"); + assertTrue("Exists " + o, o.isFile()); + assertTrue("Read-only " + o, !o.canWrite()); + + o = new File(new File(new File(db.getDirectory(), "objects"), "e6"), + "9de29bb2d1d6434b8b29ae775ad8c2e48c5391"); + assertTrue("Exists " + o, o.isFile()); + assertTrue("Read-only " + o, !o.canWrite()); + } + + public void test005_ReadSimpleConfig() { + final Config c = db.getConfig(); + assertNotNull(c); + assertEquals("0", c.getString("core", null, "repositoryformatversion")); + assertEquals("0", c.getString("CoRe", null, "REPOSITORYFoRmAtVeRsIoN")); + assertEquals("true", c.getString("core", null, "filemode")); + assertEquals("true", c.getString("cOrE", null, "fIlEModE")); + assertNull(c.getString("notavalue", null, "reallyNotAValue")); + } + + public void test006_ReadUglyConfig() throws IOException, + ConfigInvalidException { + 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" + + " email = A U Thor # Just an example...\n" + + " name = \"A Thor \\\\ \\\"\\t \"\n" + + " defaultCheckInComment = a many line\\n\\\ncomment\\n\\\n" + + " to test\n"; + pw.write(configStr); + pw.close(); + c.load(); + assertEquals("yes", c.getString("core", null, "filemode")); + assertEquals("A U Thor ", c + .getString("user", null, "email")); + assertEquals("A Thor \\ \"\t ", c.getString("user", null, "name")); + assertEquals("a many line\ncomment\n to test", c.getString("user", + null, "defaultCheckInComment")); + c.save(); + final FileReader fr = new FileReader(cfg); + final char[] cbuf = new char[configStr.length()]; + fr.read(cbuf); + fr.close(); + assertEquals(configStr, new String(cbuf)); + } + + public void test007_Open() throws IOException { + final FileRepository db2 = new FileRepository(db.getDirectory()); + assertEquals(db.getDirectory(), db2.getDirectory()); + assertEquals(db.getObjectDatabase().getDirectory(), db2.getObjectDatabase().getDirectory()); + assertNotSame(db.getConfig(), db2.getConfig()); + } + + public void test008_FailOnWrongVersion() throws IOException { + final File cfg = new File(db.getDirectory(), "config"); + final FileWriter pw = new FileWriter(cfg); + final String badvers = "ihopethisisneveraversion"; + final String configStr = "[core]\n" + "\trepositoryFormatVersion=" + + badvers + "\n"; + pw.write(configStr); + pw.close(); + + try { + new FileRepository(db.getDirectory()); + fail("incorrectly opened a bad repository"); + } catch (IOException ioe) { + assertTrue(ioe.getMessage().indexOf("format") > 0); + assertTrue(ioe.getMessage().indexOf(badvers) > 0); + } + } + + 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"); + t.accept(new WriteTree(trash, db), TreeEntry.MODIFIED_ONLY); + assertEquals(ObjectId.fromString("00b1f73724f493096d1ffa0b0f1f1482dbb8c936"), + t.getTreeId()); + + final Commit c = new Commit(db); + c.setAuthor(new PersonIdent(author, 1154236443000L, -4 * 60)); + c.setCommitter(new PersonIdent(committer, 1154236443000L, -4 * 60)); + c.setMessage("A Commit\n"); + c.setTree(t); + assertEquals(t.getTreeId(), c.getTreeId()); + c.commit(); + final ObjectId cmtid = ObjectId.fromString( + "803aec4aba175e8ab1d666873c984c0308179099"); + assertEquals(cmtid, c.getCommitId()); + + // Verify the commit we just wrote is in the correct format. + 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()); + assertTrue(0x789c % 31 == 0); + } finally { + xis.close(); + } + + // Verify we can read it. + final Commit c2 = db.mapCommit(cmtid); + assertNotNull(c2); + assertEquals(c.getMessage(), c2.getMessage()); + assertEquals(c.getTreeId(), c2.getTreeId()); + assertEquals(c.getAuthor(), c2.getAuthor()); + assertEquals(c.getCommitter(), c2.getCommitter()); + } + + public void test012_SubtreeExternalSorting() throws IOException { + final ObjectId emptyBlob = new ObjectWriter(db).writeBlob(new byte[0]); + final Tree t = new Tree(db); + final FileTreeEntry e0 = t.addFile("a-"); + final FileTreeEntry e1 = t.addFile("a-b"); + final FileTreeEntry e2 = t.addFile("a/b"); + final FileTreeEntry e3 = t.addFile("a="); + final FileTreeEntry e4 = t.addFile("a=b"); + + e0.setId(emptyBlob); + e1.setId(emptyBlob); + e2.setId(emptyBlob); + e3.setId(emptyBlob); + e4.setId(emptyBlob); + + t.accept(new WriteTree(trash, db), TreeEntry.MODIFIED_ONLY); + assertEquals(ObjectId.fromString("b47a8f0a4190f7572e11212769090523e23eb1ea"), + t.getId()); + } + + public void test020_createBlobTag() throws IOException { + final ObjectId emptyId = new ObjectWriter(db).writeBlob(new byte[0]); + final Tag t = new Tag(db); + t.setObjId(emptyId); + t.setType("blob"); + t.setTag("test020"); + t.setAuthor(new PersonIdent(author, 1154236443000L, -4 * 60)); + t.setMessage("test020 tagged\n"); + t.tag(); + assertEquals("6759556b09fbb4fd8ae5e315134481cc25d46954", t.getTagId().name()); + + Tag mapTag = db.mapTag("test020"); + assertEquals("blob", mapTag.getType()); + assertEquals("test020 tagged\n", mapTag.getMessage()); + assertEquals(new PersonIdent(author, 1154236443000L, -4 * 60), mapTag.getAuthor()); + assertEquals("e69de29bb2d1d6434b8b29ae775ad8c2e48c5391", mapTag.getObjId().name()); + } + + public void test020b_createBlobPlainTag() throws IOException { + test020_createBlobTag(); + Tag t = new Tag(db); + t.setTag("test020b"); + t.setObjId(ObjectId.fromString("e69de29bb2d1d6434b8b29ae775ad8c2e48c5391")); + t.tag(); + + Tag mapTag = db.mapTag("test020b"); + assertEquals("e69de29bb2d1d6434b8b29ae775ad8c2e48c5391", mapTag.getObjId().name()); + + // We do not repeat the plain tag test for other object types + } + + public void test021_createTreeTag() throws IOException { + final ObjectId emptyId = new ObjectWriter(db).writeBlob(new byte[0]); + final Tree almostEmptyTree = new Tree(db); + almostEmptyTree.addEntry(new FileTreeEntry(almostEmptyTree, emptyId, "empty".getBytes(), false)); + final ObjectId almostEmptyTreeId = new ObjectWriter(db).writeTree(almostEmptyTree); + final Tag t = new Tag(db); + t.setObjId(almostEmptyTreeId); + t.setType("tree"); + t.setTag("test021"); + t.setAuthor(new PersonIdent(author, 1154236443000L, -4 * 60)); + t.setMessage("test021 tagged\n"); + t.tag(); + assertEquals("b0517bc8dbe2096b419d42424cd7030733f4abe5", t.getTagId().name()); + + Tag mapTag = db.mapTag("test021"); + assertEquals("tree", mapTag.getType()); + assertEquals("test021 tagged\n", mapTag.getMessage()); + assertEquals(new PersonIdent(author, 1154236443000L, -4 * 60), mapTag.getAuthor()); + assertEquals("417c01c8795a35b8e835113a85a5c0c1c77f67fb", mapTag.getObjId().name()); + } + + public void test022_createCommitTag() throws IOException { + final ObjectId emptyId = new ObjectWriter(db).writeBlob(new byte[0]); + final Tree almostEmptyTree = new Tree(db); + almostEmptyTree.addEntry(new FileTreeEntry(almostEmptyTree, emptyId, "empty".getBytes(), false)); + final ObjectId almostEmptyTreeId = new ObjectWriter(db).writeTree(almostEmptyTree); + final Commit almostEmptyCommit = new Commit(db); + almostEmptyCommit.setAuthor(new PersonIdent(author, 1154236443000L, -2 * 60)); // not exactly the same + almostEmptyCommit.setCommitter(new PersonIdent(author, 1154236443000L, -2 * 60)); + almostEmptyCommit.setMessage("test022\n"); + almostEmptyCommit.setTreeId(almostEmptyTreeId); + ObjectId almostEmptyCommitId = new ObjectWriter(db).writeCommit(almostEmptyCommit); + final Tag t = new Tag(db); + t.setObjId(almostEmptyCommitId); + t.setType("commit"); + t.setTag("test022"); + t.setAuthor(new PersonIdent(author, 1154236443000L, -4 * 60)); + t.setMessage("test022 tagged\n"); + t.tag(); + assertEquals("0ce2ebdb36076ef0b38adbe077a07d43b43e3807", t.getTagId().name()); + + Tag mapTag = db.mapTag("test022"); + assertEquals("commit", mapTag.getType()); + assertEquals("test022 tagged\n", mapTag.getMessage()); + assertEquals(new PersonIdent(author, 1154236443000L, -4 * 60), mapTag.getAuthor()); + assertEquals("b5d3b45a96b340441f5abb9080411705c51cc86c", mapTag.getObjId().name()); + } + + public void test023_createCommitNonAnullii() throws IOException { + final ObjectId emptyId = new ObjectWriter(db).writeBlob(new byte[0]); + final Tree almostEmptyTree = new Tree(db); + almostEmptyTree.addEntry(new FileTreeEntry(almostEmptyTree, emptyId, "empty".getBytes(), false)); + final ObjectId almostEmptyTreeId = new ObjectWriter(db).writeTree(almostEmptyTree); + Commit commit = new Commit(db); + commit.setTreeId(almostEmptyTreeId); + commit.setAuthor(new PersonIdent("Joe H\u00e4cker","joe@example.com",4294967295000L,60)); + commit.setCommitter(new PersonIdent("Joe Hacker","joe2@example.com",4294967295000L,60)); + commit.setEncoding("UTF-8"); + commit.setMessage("\u00dcbergeeks"); + ObjectId cid = new ObjectWriter(db).writeCommit(commit); + assertEquals("4680908112778718f37e686cbebcc912730b3154", cid.name()); + Commit loadedCommit = db.mapCommit(cid); + assertNotSame(loadedCommit, commit); + assertEquals(commit.getMessage(), loadedCommit.getMessage()); + } + + public void test024_createCommitNonAscii() throws IOException { + final ObjectId emptyId = new ObjectWriter(db).writeBlob(new byte[0]); + final Tree almostEmptyTree = new Tree(db); + almostEmptyTree.addEntry(new FileTreeEntry(almostEmptyTree, emptyId, "empty".getBytes(), false)); + final ObjectId almostEmptyTreeId = new ObjectWriter(db).writeTree(almostEmptyTree); + Commit commit = new Commit(db); + commit.setTreeId(almostEmptyTreeId); + commit.setAuthor(new PersonIdent("Joe H\u00e4cker","joe@example.com",4294967295000L,60)); + commit.setCommitter(new PersonIdent("Joe Hacker","joe2@example.com",4294967295000L,60)); + commit.setEncoding("ISO-8859-1"); + commit.setMessage("\u00dcbergeeks"); + ObjectId cid = new ObjectWriter(db).writeCommit(commit); + assertEquals("2979b39d385014b33287054b87f77bcb3ecb5ebf", cid.name()); + } + + public void test025_packedRefs() throws IOException { + test020_createBlobTag(); + test021_createTreeTag(); + test022_createCommitTag(); + + if (!new File(db.getDirectory(),"refs/tags/test020").delete()) throw new Error("Cannot delete unpacked tag"); + if (!new File(db.getDirectory(),"refs/tags/test021").delete()) throw new Error("Cannot delete unpacked tag"); + if (!new File(db.getDirectory(),"refs/tags/test022").delete()) throw new Error("Cannot delete unpacked tag"); + + // We cannot resolve it now, since we have no ref + Tag mapTag20missing = db.mapTag("test020"); + assertNull(mapTag20missing); + + // Construct packed refs file + PrintWriter w = new PrintWriter(new FileWriter(new File(db.getDirectory(), "packed-refs"))); + w.println("# packed-refs with: peeled"); + w.println("6759556b09fbb4fd8ae5e315134481cc25d46954 refs/tags/test020"); + w.println("^e69de29bb2d1d6434b8b29ae775ad8c2e48c5391"); + w.println("b0517bc8dbe2096b419d42424cd7030733f4abe5 refs/tags/test021"); + w.println("^417c01c8795a35b8e835113a85a5c0c1c77f67fb"); + w.println("0ce2ebdb36076ef0b38adbe077a07d43b43e3807 refs/tags/test022"); + w.println("^b5d3b45a96b340441f5abb9080411705c51cc86c"); + w.close(); + ((RefDirectory)db.getRefDatabase()).rescan(); + + Tag mapTag20 = db.mapTag("test020"); + assertNotNull("have tag test020", mapTag20); + assertEquals("blob", mapTag20.getType()); + assertEquals("test020 tagged\n", mapTag20.getMessage()); + assertEquals(new PersonIdent(author, 1154236443000L, -4 * 60), mapTag20.getAuthor()); + assertEquals("e69de29bb2d1d6434b8b29ae775ad8c2e48c5391", mapTag20.getObjId().name()); + + Tag mapTag21 = db.mapTag("test021"); + assertEquals("tree", mapTag21.getType()); + assertEquals("test021 tagged\n", mapTag21.getMessage()); + assertEquals(new PersonIdent(author, 1154236443000L, -4 * 60), mapTag21.getAuthor()); + assertEquals("417c01c8795a35b8e835113a85a5c0c1c77f67fb", mapTag21.getObjId().name()); + + Tag mapTag22 = db.mapTag("test022"); + assertEquals("commit", mapTag22.getType()); + assertEquals("test022 tagged\n", mapTag22.getMessage()); + assertEquals(new PersonIdent(author, 1154236443000L, -4 * 60), mapTag22.getAuthor()); + assertEquals("b5d3b45a96b340441f5abb9080411705c51cc86c", mapTag22.getObjId().name()); + } + + public void test025_computeSha1NoStore() throws IOException { + byte[] data = "test025 some data, more than 16 bytes to get good coverage" + .getBytes("ISO-8859-1"); + // TODO: but we do not test legacy header writing + final ObjectId id = new ObjectWriter(db).computeBlobSha1(data.length, + new ByteArrayInputStream(data)); + assertEquals("4f561df5ecf0dfbd53a0dc0f37262fef075d9dde", id.name()); + } + + public void test026_CreateCommitMultipleparents() 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"); + t.accept(new WriteTree(trash, db), TreeEntry.MODIFIED_ONLY); + assertEquals(ObjectId.fromString("00b1f73724f493096d1ffa0b0f1f1482dbb8c936"), + t.getTreeId()); + + final Commit c1 = new Commit(db); + c1.setAuthor(new PersonIdent(author, 1154236443000L, -4 * 60)); + c1.setCommitter(new PersonIdent(committer, 1154236443000L, -4 * 60)); + c1.setMessage("A Commit\n"); + c1.setTree(t); + assertEquals(t.getTreeId(), c1.getTreeId()); + c1.commit(); + final ObjectId cmtid1 = ObjectId.fromString( + "803aec4aba175e8ab1d666873c984c0308179099"); + assertEquals(cmtid1, c1.getCommitId()); + + final Commit c2 = new Commit(db); + c2.setAuthor(new PersonIdent(author, 1154236443000L, -4 * 60)); + c2.setCommitter(new PersonIdent(committer, 1154236443000L, -4 * 60)); + c2.setMessage("A Commit 2\n"); + c2.setTree(t); + assertEquals(t.getTreeId(), c2.getTreeId()); + c2.setParentIds(new ObjectId[] { c1.getCommitId() } ); + c2.commit(); + final ObjectId cmtid2 = ObjectId.fromString( + "95d068687c91c5c044fb8c77c5154d5247901553"); + assertEquals(cmtid2, c2.getCommitId()); + + Commit rm2 = db.mapCommit(cmtid2); + assertNotSame(c2, rm2); // assert the parsed objects is not from the cache + assertEquals(c2.getAuthor(), rm2.getAuthor()); + assertEquals(c2.getCommitId(), rm2.getCommitId()); + assertEquals(c2.getMessage(), rm2.getMessage()); + assertEquals(c2.getTree().getTreeId(), rm2.getTree().getTreeId()); + assertEquals(1, rm2.getParentIds().length); + assertEquals(c1.getCommitId(), rm2.getParentIds()[0]); + + final Commit c3 = new Commit(db); + c3.setAuthor(new PersonIdent(author, 1154236443000L, -4 * 60)); + c3.setCommitter(new PersonIdent(committer, 1154236443000L, -4 * 60)); + c3.setMessage("A Commit 3\n"); + c3.setTree(t); + assertEquals(t.getTreeId(), c3.getTreeId()); + c3.setParentIds(new ObjectId[] { c1.getCommitId(), c2.getCommitId() }); + c3.commit(); + final ObjectId cmtid3 = ObjectId.fromString( + "ce6e1ce48fbeeb15a83f628dc8dc2debefa066f4"); + assertEquals(cmtid3, c3.getCommitId()); + + Commit rm3 = db.mapCommit(cmtid3); + assertNotSame(c3, rm3); // assert the parsed objects is not from the cache + assertEquals(c3.getAuthor(), rm3.getAuthor()); + assertEquals(c3.getCommitId(), rm3.getCommitId()); + assertEquals(c3.getMessage(), rm3.getMessage()); + assertEquals(c3.getTree().getTreeId(), rm3.getTree().getTreeId()); + assertEquals(2, rm3.getParentIds().length); + assertEquals(c1.getCommitId(), rm3.getParentIds()[0]); + assertEquals(c2.getCommitId(), rm3.getParentIds()[1]); + + final Commit c4 = new Commit(db); + c4.setAuthor(new PersonIdent(author, 1154236443000L, -4 * 60)); + c4.setCommitter(new PersonIdent(committer, 1154236443000L, -4 * 60)); + c4.setMessage("A Commit 4\n"); + c4.setTree(t); + assertEquals(t.getTreeId(), c3.getTreeId()); + c4.setParentIds(new ObjectId[] { c1.getCommitId(), c2.getCommitId(), c3.getCommitId() }); + c4.commit(); + final ObjectId cmtid4 = ObjectId.fromString( + "d1fca9fe3fef54e5212eb67902c8ed3e79736e27"); + assertEquals(cmtid4, c4.getCommitId()); + + Commit rm4 = db.mapCommit(cmtid4); + assertNotSame(c4, rm3); // assert the parsed objects is not from the cache + assertEquals(c4.getAuthor(), rm4.getAuthor()); + assertEquals(c4.getCommitId(), rm4.getCommitId()); + assertEquals(c4.getMessage(), rm4.getMessage()); + assertEquals(c4.getTree().getTreeId(), rm4.getTree().getTreeId()); + assertEquals(3, rm4.getParentIds().length); + assertEquals(c1.getCommitId(), rm4.getParentIds()[0]); + assertEquals(c2.getCommitId(), rm4.getParentIds()[1]); + assertEquals(c3.getCommitId(), rm4.getParentIds()[2]); + } + + public void test027_UnpackedRefHigherPriorityThanPacked() throws IOException { + PrintWriter writer = new PrintWriter(new FileWriter(new File(db.getDirectory(), "refs/heads/a"))); + String unpackedId = "7f822839a2fe9760f386cbbbcb3f92c5fe81def7"; + writer.print(unpackedId); + writer.print('\n'); + writer.close(); + + ObjectId resolved = db.resolve("refs/heads/a"); + assertEquals(unpackedId, resolved.name()); + } + + public void test028_LockPackedRef() throws IOException { + writeTrashFile(".git/packed-refs", "7f822839a2fe9760f386cbbbcb3f92c5fe81def7 refs/heads/foobar"); + writeTrashFile(".git/HEAD", "ref: refs/heads/foobar\n"); + BUG_WorkAroundRacyGitIssues("packed-refs"); + BUG_WorkAroundRacyGitIssues("HEAD"); + + ObjectId resolve = db.resolve("HEAD"); + assertEquals("7f822839a2fe9760f386cbbbcb3f92c5fe81def7", resolve.name()); + + RefUpdate lockRef = db.updateRef("HEAD"); + ObjectId newId = ObjectId.fromString("07f822839a2fe9760f386cbbbcb3f92c5fe81def"); + lockRef.setNewObjectId(newId); + assertEquals(RefUpdate.Result.FORCED, lockRef.forceUpdate()); + + assertTrue(new File(db.getDirectory(), "refs/heads/foobar").exists()); + assertEquals(newId, db.resolve("refs/heads/foobar")); + + // Again. The ref already exists + RefUpdate lockRef2 = db.updateRef("HEAD"); + ObjectId newId2 = ObjectId.fromString("7f822839a2fe9760f386cbbbcb3f92c5fe81def7"); + lockRef2.setNewObjectId(newId2); + assertEquals(RefUpdate.Result.FORCED, lockRef2.forceUpdate()); + + assertTrue(new File(db.getDirectory(), "refs/heads/foobar").exists()); + assertEquals(newId2, db.resolve("refs/heads/foobar")); + } + + public void test029_mapObject() throws IOException { + assertEquals(new byte[0].getClass(), db.mapObject(ObjectId.fromString("5b6e7c66c276e7610d4a73c70ec1a1f7c1003259"), null).getClass()); + assertEquals(Commit.class, db.mapObject(ObjectId.fromString("540a36d136cf413e4b064c2b0e0a4db60f77feab"), null).getClass()); + assertEquals(Tree.class, db.mapObject(ObjectId.fromString("aabf2ffaec9b497f0950352b3e582d73035c2035"), null).getClass()); + assertEquals(Tag.class, db.mapObject(ObjectId.fromString("17768080a2318cd89bba4c8b87834401e2095703"), null).getClass()); + } + + public void test30_stripWorkDir() { + File relCwd = new File("."); + File absCwd = relCwd.getAbsoluteFile(); + File absBase = new File(new File(absCwd, "repo"), "workdir"); + File relBase = new File(new File(relCwd, "repo"), "workdir"); + assertEquals(absBase.getAbsolutePath(), relBase.getAbsolutePath()); + + File relBaseFile = new File(new File(relBase, "other"), "module.c"); + File absBaseFile = new File(new File(absBase, "other"), "module.c"); + assertEquals("other/module.c", Repository.stripWorkDir(relBase, relBaseFile)); + assertEquals("other/module.c", Repository.stripWorkDir(relBase, absBaseFile)); + assertEquals("other/module.c", Repository.stripWorkDir(absBase, relBaseFile)); + assertEquals("other/module.c", Repository.stripWorkDir(absBase, absBaseFile)); + + File relNonFile = new File(new File(relCwd, "not-repo"), ".gitignore"); + File absNonFile = new File(new File(absCwd, "not-repo"), ".gitignore"); + assertEquals("", Repository.stripWorkDir(relBase, relNonFile)); + assertEquals("", Repository.stripWorkDir(absBase, absNonFile)); + + assertEquals("", Repository.stripWorkDir(db.getWorkTree(), db.getWorkTree())); + + File file = new File(new File(db.getWorkTree(), "subdir"), "File.java"); + assertEquals("subdir/File.java", Repository.stripWorkDir(db.getWorkTree(), file)); + + } + + /** + * Kick the timestamp of a local file. + *

+ * We shouldn't have to make these method calls. The cache is using file + * system timestamps, and on many systems unit tests run faster than the + * modification clock. Dumping the cache after we make an edit behind + * RefDirectory's back allows the tests to pass. + * + * @param name + * the file in the repository to force a time change on. + */ + private void BUG_WorkAroundRacyGitIssues(String name) { + File path = new File(db.getDirectory(), name); + long old = path.lastModified(); + long set = 1250379778668L; // Sat Aug 15 20:12:58 GMT-03:30 2009 + path.setLastModified(set); + assertTrue("time changed", old != path.lastModified()); + } +} diff --git a/org.eclipse.jgit.test/tst/org/eclipse/jgit/storage/file/T0004_PackReader.java b/org.eclipse.jgit.test/tst/org/eclipse/jgit/storage/file/T0004_PackReader.java new file mode 100644 index 0000000000..38b532726e --- /dev/null +++ b/org.eclipse.jgit.test/tst/org/eclipse/jgit/storage/file/T0004_PackReader.java @@ -0,0 +1,90 @@ +/* + * Copyright (C) 2008-2009, Google Inc. + * Copyright (C) 2008, Imran M Yousuf + * Copyright (C) 2007, Robin Rosenberg + * Copyright (C) 2006-2008, Shawn O. Pearce + * 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.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 { + private static final String PACK_NAME = "pack-34be9032ac282b11fa9babdc2b2a93ca996c9c2f"; + private static final File TEST_PACK = JGitTestUtil.getTestResourceFile(PACK_NAME + ".pack"); + private static final File TEST_IDX = JGitTestUtil.getTestResourceFile(PACK_NAME + ".idx"); + + public void test003_lookupCompressedObject() throws IOException { + final PackFile pr; + final ObjectId id; + final PackedObjectLoader or; + + id = ObjectId.fromString("902d5476fa249b7abc9d84c611577a81381f0327"); + pr = new PackFile(TEST_IDX, TEST_PACK); + 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(); + } + + public void test004_lookupDeltifiedObject() throws IOException { + final ObjectId id; + final ObjectLoader or; + + id = ObjectId.fromString("5b6e7c66c276e7610d4a73c70ec1a1f7c1003259"); + or = db.openObject(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/WindowCacheGetTest.java b/org.eclipse.jgit.test/tst/org/eclipse/jgit/storage/file/WindowCacheGetTest.java new file mode 100644 index 0000000000..ec512cf710 --- /dev/null +++ b/org.eclipse.jgit.test/tst/org/eclipse/jgit/storage/file/WindowCacheGetTest.java @@ -0,0 +1,153 @@ +/* + * 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.BufferedReader; +import java.io.FileInputStream; +import java.io.IOException; +import java.io.InputStreamReader; +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; + +public class WindowCacheGetTest extends SampleDataRepositoryTestCase { + private List toLoad; + + @Override + public void setUp() throws Exception { + super.setUp(); + + toLoad = new ArrayList(); + final BufferedReader br = new BufferedReader(new InputStreamReader( + new FileInputStream(JGitTestUtil + .getTestResourceFile("all_packed_objects.txt")), + Constants.CHARSET)); + try { + String line; + while ((line = br.readLine()) != null) { + final String[] parts = line.split(" {1,}"); + final TestObject o = new TestObject(); + o.id = ObjectId.fromString(parts[0]); + o.setType(parts[1]); + o.rawSize = Integer.parseInt(parts[2]); + // parts[3] is the size-in-pack + o.offset = Long.parseLong(parts[4]); + toLoad.add(o); + } + } finally { + br.close(); + } + assertEquals(96, toLoad.size()); + } + + public void testCache_Defaults() throws IOException { + final WindowCacheConfig cfg = new WindowCacheConfig(); + WindowCache.reconfigure(cfg); + doCacheTests(); + checkLimits(cfg); + + final WindowCache cache = WindowCache.getInstance(); + assertEquals(6, cache.getOpenFiles()); + assertEquals(17346, cache.getOpenBytes()); + } + + public void testCache_TooFewFiles() throws IOException { + final WindowCacheConfig cfg = new WindowCacheConfig(); + cfg.setPackedGitOpenFiles(2); + WindowCache.reconfigure(cfg); + doCacheTests(); + checkLimits(cfg); + } + + public void testCache_TooSmallLimit() throws IOException { + final WindowCacheConfig cfg = new WindowCacheConfig(); + cfg.setPackedGitWindowSize(4096); + cfg.setPackedGitLimit(4096); + WindowCache.reconfigure(cfg); + doCacheTests(); + checkLimits(cfg); + } + + private void checkLimits(final WindowCacheConfig cfg) { + final WindowCache cache = WindowCache.getInstance(); + assertTrue(cache.getOpenFiles() <= cfg.getPackedGitOpenFiles()); + assertTrue(cache.getOpenBytes() <= cfg.getPackedGitLimit()); + assertTrue(0 < cache.getOpenFiles()); + assertTrue(0 < cache.getOpenBytes()); + } + + private void doCacheTests() throws IOException { + for (final TestObject o : toLoad) { + final ObjectLoader or = db.openObject(o.id); + assertNotNull(or); + assertTrue(or instanceof PackedObjectLoader); + assertEquals(o.type, or.getType()); + assertEquals(o.rawSize, or.getRawSize()); + assertEquals(o.offset, ((PackedObjectLoader) or).getObjectOffset()); + } + } + + private class TestObject { + ObjectId id; + + int type; + + int rawSize; + + long offset; + + void setType(final String typeStr) throws CorruptObjectException { + final byte[] typeRaw = Constants.encode(typeStr + " "); + final MutableInteger ptr = new MutableInteger(); + type = Constants.decodeTypeString(id, typeRaw, (byte) ' ', ptr); + } + } +} diff --git a/org.eclipse.jgit.test/tst/org/eclipse/jgit/storage/file/WindowCacheReconfigureTest.java b/org.eclipse.jgit.test/tst/org/eclipse/jgit/storage/file/WindowCacheReconfigureTest.java new file mode 100644 index 0000000000..e52b19d925 --- /dev/null +++ b/org.eclipse.jgit.test/tst/org/eclipse/jgit/storage/file/WindowCacheReconfigureTest.java @@ -0,0 +1,127 @@ +/* + * 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 org.eclipse.jgit.lib.RepositoryTestCase; + +public class WindowCacheReconfigureTest extends RepositoryTestCase { + public void testConfigureCache_PackedGitLimit_0() { + try { + final WindowCacheConfig cfg = new WindowCacheConfig(); + cfg.setPackedGitLimit(0); + WindowCache.reconfigure(cfg); + fail("incorrectly permitted PackedGitLimit = 0"); + } catch (IllegalArgumentException e) { + // + } + } + + public void testConfigureCache_PackedGitWindowSize_0() { + try { + final WindowCacheConfig cfg = new WindowCacheConfig(); + cfg.setPackedGitWindowSize(0); + WindowCache.reconfigure(cfg); + fail("incorrectly permitted PackedGitWindowSize = 0"); + } catch (IllegalArgumentException e) { + assertEquals("Invalid window size", e.getMessage()); + } + } + + public void testConfigureCache_PackedGitWindowSize_512() { + try { + final WindowCacheConfig cfg = new WindowCacheConfig(); + cfg.setPackedGitWindowSize(512); + WindowCache.reconfigure(cfg); + fail("incorrectly permitted PackedGitWindowSize = 512"); + } catch (IllegalArgumentException e) { + assertEquals("Invalid window size", e.getMessage()); + } + } + + public void testConfigureCache_PackedGitWindowSize_4097() { + try { + final WindowCacheConfig cfg = new WindowCacheConfig(); + cfg.setPackedGitWindowSize(4097); + WindowCache.reconfigure(cfg); + fail("incorrectly permitted PackedGitWindowSize = 4097"); + } catch (IllegalArgumentException e) { + assertEquals("Window size must be power of 2", e.getMessage()); + } + } + + public void testConfigureCache_PackedGitOpenFiles_0() { + try { + final WindowCacheConfig cfg = new WindowCacheConfig(); + cfg.setPackedGitOpenFiles(0); + WindowCache.reconfigure(cfg); + fail("incorrectly permitted PackedGitOpenFiles = 0"); + } catch (IllegalArgumentException e) { + assertEquals("Open files must be >= 1", e.getMessage()); + } + } + + public void testConfigureCache_PackedGitWindowSizeAbovePackedGitLimit() { + try { + final WindowCacheConfig cfg = new WindowCacheConfig(); + cfg.setPackedGitLimit(1024); + cfg.setPackedGitWindowSize(8192); + WindowCache.reconfigure(cfg); + fail("incorrectly permitted PackedGitWindowSize > PackedGitLimit"); + } catch (IllegalArgumentException e) { + assertEquals("Window size must be < limit", e.getMessage()); + } + } + + public void testConfigureCache_Limits1() { + // This test is just to force coverage over some lower bounds for + // the table. We don't want the table to wind up with too small + // of a size. This is highly dependent upon the table allocation + // algorithm actually implemented in WindowCache. + // + final WindowCacheConfig cfg = new WindowCacheConfig(); + cfg.setPackedGitLimit(6 * 4096 / 5); + cfg.setPackedGitWindowSize(4096); + WindowCache.reconfigure(cfg); + } +} diff --git a/org.eclipse.jgit.test/tst/org/eclipse/jgit/storage/file/XInputStream.java b/org.eclipse.jgit.test/tst/org/eclipse/jgit/storage/file/XInputStream.java new file mode 100644 index 0000000000..9978c8e13c --- /dev/null +++ b/org.eclipse.jgit.test/tst/org/eclipse/jgit/storage/file/XInputStream.java @@ -0,0 +1,88 @@ +/* + * Copyright (C) 2006-2008, Shawn O. Pearce + * 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.EOFException; +import java.io.IOException; +import java.io.InputStream; + +import org.eclipse.jgit.util.NB; + +class XInputStream extends BufferedInputStream { + private final byte[] intbuf = new byte[8]; + + XInputStream(final InputStream s) { + super(s); + } + + synchronized byte[] readFully(final int len) throws IOException { + final byte[] b = new byte[len]; + readFully(b, 0, len); + return b; + } + + synchronized void readFully(final byte[] b, int o, int len) + throws IOException { + int r; + while (len > 0 && (r = read(b, o, len)) > 0) { + o += r; + len -= r; + } + if (len > 0) + throw new EOFException(); + } + + int readUInt8() throws IOException { + final int r = read(); + if (r < 0) + throw new EOFException(); + return r; + } + + long readUInt32() throws IOException { + readFully(intbuf, 0, 4); + return NB.decodeUInt32(intbuf, 0); + } +} 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 03726cba09..cb1ce6f382 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; diff --git a/org.eclipse.jgit/META-INF/MANIFEST.MF b/org.eclipse.jgit/META-INF/MANIFEST.MF index 81441dbdbd..e96ba038d0 100644 --- a/org.eclipse.jgit/META-INF/MANIFEST.MF +++ b/org.eclipse.jgit/META-INF/MANIFEST.MF @@ -19,6 +19,7 @@ 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.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/src/org/eclipse/jgit/dircache/DirCache.java b/org.eclipse.jgit/src/org/eclipse/jgit/dircache/DirCache.java index 9ced005a45..6b1349adef 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,10 @@ 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.ObjectInserter; import org.eclipse.jgit.lib.Repository; +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; diff --git a/org.eclipse.jgit/src/org/eclipse/jgit/lib/BaseRepositoryBuilder.java b/org.eclipse.jgit/src/org/eclipse/jgit/lib/BaseRepositoryBuilder.java index f5dd7eec72..90929e721c 100644 --- a/org.eclipse.jgit/src/org/eclipse/jgit/lib/BaseRepositoryBuilder.java +++ b/org.eclipse.jgit/src/org/eclipse/jgit/lib/BaseRepositoryBuilder.java @@ -21,6 +21,9 @@ 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; diff --git a/org.eclipse.jgit/src/org/eclipse/jgit/lib/ByteArrayWindow.java b/org.eclipse.jgit/src/org/eclipse/jgit/lib/ByteArrayWindow.java deleted file mode 100644 index 0c5c818993..0000000000 --- a/org.eclipse.jgit/src/org/eclipse/jgit/lib/ByteArrayWindow.java +++ /dev/null @@ -1,101 +0,0 @@ -/* - * Copyright (C) 2008-2009, Google Inc. - * Copyright (C) 2007, Robin Rosenberg - * Copyright (C) 2006-2008, Shawn O. Pearce - * 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.util.zip.CRC32; -import java.util.zip.DataFormatException; -import java.util.zip.Inflater; - -/** - * A {@link ByteWindow} with an underlying byte array for storage. - */ -final class ByteArrayWindow extends ByteWindow { - private final byte[] array; - - ByteArrayWindow(final PackFile pack, final long o, final byte[] b) { - super(pack, o, b.length); - array = b; - } - - @Override - protected int copy(final int p, final byte[] b, final int o, int n) { - n = Math.min(array.length - p, n); - System.arraycopy(array, p, b, o, n); - return n; - } - - @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; - } - - 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 { - 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/lib/ByteBufferWindow.java deleted file mode 100644 index 794d7428e6..0000000000 --- a/org.eclipse.jgit/src/org/eclipse/jgit/lib/ByteBufferWindow.java +++ /dev/null @@ -1,92 +0,0 @@ -/* - * Copyright (C) 2008-2009, Google Inc. - * Copyright (C) 2007, Robin Rosenberg - * Copyright (C) 2006-2008, Shawn O. Pearce - * 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.nio.ByteBuffer; -import java.util.zip.DataFormatException; -import java.util.zip.Inflater; - -/** - * A window for accessing git packs using a {@link ByteBuffer} for storage. - * - * @see ByteWindow - */ -final class ByteBufferWindow extends ByteWindow { - private final ByteBuffer buffer; - - ByteBufferWindow(final PackFile pack, final long o, final ByteBuffer b) { - super(pack, o, b.capacity()); - buffer = b; - } - - @Override - protected int copy(final int p, final byte[] b, final int o, int n) { - final ByteBuffer s = buffer.slice(); - s.position(p); - n = Math.min(s.remaining(), n); - s.get(b, o, n); - return n; - } - - @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; - } -} diff --git a/org.eclipse.jgit/src/org/eclipse/jgit/lib/ByteWindow.java b/org.eclipse.jgit/src/org/eclipse/jgit/lib/ByteWindow.java deleted file mode 100644 index 69d255c781..0000000000 --- a/org.eclipse.jgit/src/org/eclipse/jgit/lib/ByteWindow.java +++ /dev/null @@ -1,175 +0,0 @@ -/* - * Copyright (C) 2007, Robin Rosenberg - * Copyright (C) 2006-2008, Shawn O. Pearce - * 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.util.zip.DataFormatException; -import java.util.zip.Inflater; - -/** - * A window of data currently stored within a cache. - *

- * All bytes in the window can be assumed to be "immediately available", that is - * they are very likely already in memory, unless the operating system's memory - * is very low and has paged part of this process out to disk. Therefore copying - * bytes from a window is very inexpensive. - *

- */ -abstract class ByteWindow { - protected final PackFile pack; - - protected final long start; - - protected final long end; - - protected ByteWindow(final PackFile p, final long s, final int n) { - pack = p; - start = s; - end = start + n; - } - - final int size() { - return (int) (end - start); - } - - final boolean contains(final PackFile neededFile, final long neededPos) { - return pack == neededFile && start <= neededPos && neededPos < end; - } - - /** - * Copy bytes from the window to a caller supplied buffer. - * - * @param pos - * offset within the file to start copying from. - * @param dstbuf - * destination buffer to copy into. - * @param dstoff - * offset within dstbuf to start copying into. - * @param cnt - * number of bytes to copy. This value may exceed the number of - * bytes remaining in the window starting at offset - * pos. - * @return number of bytes actually copied; this may be less than - * cnt if cnt exceeded the number of - * bytes available. - */ - final int copy(long pos, byte[] dstbuf, int dstoff, int cnt) { - return copy((int) (pos - start), dstbuf, dstoff, cnt); - } - - /** - * Copy bytes from the window to a caller supplied buffer. - * - * @param pos - * offset within the window to start copying from. - * @param dstbuf - * destination buffer to copy into. - * @param dstoff - * offset within dstbuf to start copying into. - * @param cnt - * number of bytes to copy. This value may exceed the number of - * bytes remaining in the window starting at offset - * pos. - * @return number of bytes actually copied; this may be less than - * cnt if cnt exceeded the number of - * bytes available. - */ - 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 dstbuf 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 dstoff based on the number of bytes - * successfully copied into dstbuf by - * inf. 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 dstbuf 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 dstoff based on the number of bytes - * successfully copied into dstbuf by - * inf. 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; -} diff --git a/org.eclipse.jgit/src/org/eclipse/jgit/lib/CachedObjectDirectory.java b/org.eclipse.jgit/src/org/eclipse/jgit/lib/CachedObjectDirectory.java deleted file mode 100644 index a32571e606..0000000000 --- a/org.eclipse.jgit/src/org/eclipse/jgit/lib/CachedObjectDirectory.java +++ /dev/null @@ -1,184 +0,0 @@ -/* - * Copyright (C) 2010, Constantine Plotnikov - * 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.File; -import java.io.IOException; - -/** - * The cached instance of an {@link ObjectDirectory}. - *

- * This class caches the list of loose objects in memory, so the file system is - * not queried with stat calls. - */ -class CachedObjectDirectory extends FileObjectDatabase { - /** - * The set that contains unpacked objects identifiers, it is created when - * the cached instance is created. - */ - private final ObjectIdSubclassMap unpackedObjects = new ObjectIdSubclassMap(); - - private final ObjectDirectory wrapped; - - private AlternateHandle[] alts; - - /** - * The constructor - * - * @param wrapped - * the wrapped database - */ - CachedObjectDirectory(ObjectDirectory wrapped) { - this.wrapped = wrapped; - - File objects = wrapped.getDirectory(); - String[] fanout = objects.list(); - if (fanout == null) - fanout = new String[0]; - for (String d : fanout) { - if (d.length() != 2) - continue; - String[] entries = new File(objects, d).list(); - if (entries == null) - continue; - for (String e : entries) { - if (e.length() != Constants.OBJECT_ID_STRING_LENGTH - 2) - continue; - try { - unpackedObjects.add(ObjectId.fromString(d + e)); - } catch (IllegalArgumentException notAnObject) { - // ignoring the file that does not represent loose object - } - } - } - } - - @Override - 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 hasObject(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 { - // This method should never be invoked. - throw new UnsupportedOperationException(); - } - - @Override - void selectObjectRepresentation(PackWriter packer, ObjectToPack otp, - WindowCursor curs) throws IOException { - wrapped.selectObjectRepresentation(packer, otp, curs); - } -} diff --git a/org.eclipse.jgit/src/org/eclipse/jgit/lib/DeltaOfsPackedObjectLoader.java b/org.eclipse.jgit/src/org/eclipse/jgit/lib/DeltaOfsPackedObjectLoader.java deleted file mode 100644 index d0e98a2a92..0000000000 --- a/org.eclipse.jgit/src/org/eclipse/jgit/lib/DeltaOfsPackedObjectLoader.java +++ /dev/null @@ -1,82 +0,0 @@ -/* - * Copyright (C) 2009, Google Inc. - * Copyright (C) 2008, Marek Zawirski - * Copyright (C) 2007, Robin Rosenberg - * Copyright (C) 2006-2008, Shawn O. Pearce - * 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.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; - } - - @Override - public ObjectId getDeltaBase() throws IOException { - final ObjectId id = pack.findObjectForOffset(deltaBase); - if (id == null) - throw new CorruptObjectException( - JGitText.get().offsetWrittenDeltaBaseForObjectNotFoundInAPack); - return id; - } -} 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 - * Copyright (C) 2007, Robin Rosenberg - * Copyright (C) 2006-2008, Shawn O. Pearce - * 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/DeltaRefPackedObjectLoader.java b/org.eclipse.jgit/src/org/eclipse/jgit/lib/DeltaRefPackedObjectLoader.java deleted file mode 100644 index 9f7589c291..0000000000 --- a/org.eclipse.jgit/src/org/eclipse/jgit/lib/DeltaRefPackedObjectLoader.java +++ /dev/null @@ -1,80 +0,0 @@ -/* - * Copyright (C) 2008-2009, Google Inc. - * Copyright (C) 2008, Marek Zawirski - * Copyright (C) 2007, Robin Rosenberg - * Copyright (C) 2006-2008, Shawn O. Pearce - * 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.MissingObjectException; - -/** Reads a deltified object which uses an {@link ObjectId} to find its base. */ -class DeltaRefPackedObjectLoader extends DeltaPackedObjectLoader { - private final ObjectId deltaBase; - - DeltaRefPackedObjectLoader(final PackFile pr, final long objectOffset, - final int headerSz, final int deltaSz, final ObjectId base) { - super(pr, objectOffset, headerSz, deltaSz); - deltaBase = base; - } - - 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; - } - - @Override - public int getRawType() { - return Constants.OBJ_REF_DELTA; - } - - @Override - public ObjectId getDeltaBase() throws IOException { - return deltaBase; - } -} diff --git a/org.eclipse.jgit/src/org/eclipse/jgit/lib/FileBasedConfig.java b/org.eclipse.jgit/src/org/eclipse/jgit/lib/FileBasedConfig.java deleted file mode 100644 index eb00917917..0000000000 --- a/org.eclipse.jgit/src/org/eclipse/jgit/lib/FileBasedConfig.java +++ /dev/null @@ -1,163 +0,0 @@ -/* - * Copyright (C) 2009, Constantine Plotnikov - * Copyright (C) 2007, Dave Watson - * Copyright (C) 2009, Google Inc. - * Copyright (C) 2009, JetBrains s.r.o. - * Copyright (C) 2008-2009, Robin Rosenberg - * Copyright (C) 2008, Shawn O. Pearce - * Copyright (C) 2008, Thad Hughes - * 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.text.MessageFormat; - -import org.eclipse.jgit.JGitText; -import org.eclipse.jgit.errors.ConfigInvalidException; -import org.eclipse.jgit.util.IO; -import org.eclipse.jgit.util.RawParseUtils; - -/** - * The configuration file that is stored in the file of the file system. - */ -public class FileBasedConfig extends Config { - private final File configFile; - private volatile long lastModified; - - /** - * Create a configuration with no default fallback. - * - * @param cfgLocation - * the location of the configuration file on the file system - */ - public FileBasedConfig(File cfgLocation) { - this(null, cfgLocation); - } - - /** - * The constructor - * - * @param base - * the base configuration file - * @param cfgLocation - * the location of the configuration file on the file system - */ - public FileBasedConfig(Config base, File cfgLocation) { - super(base); - configFile = cfgLocation; - } - - /** @return location of the configuration file on disk */ - public final File getFile() { - return configFile; - } - - /** - * Load the configuration as a Git text style configuration file. - *

- * If the file does not exist, this configuration is cleared, and thus - * behaves the same as though the file exists, but is empty. - * - * @throws IOException - * the file could not be read (but does exist). - * @throws ConfigInvalidException - * the file is not a properly formatted configuration file. - */ - public void load() throws IOException, ConfigInvalidException { - lastModified = getFile().lastModified(); - try { - fromText(RawParseUtils.decode(IO.readFully(getFile()))); - } catch (FileNotFoundException noFile) { - clear(); - } catch (IOException e) { - final IOException e2 = new IOException(MessageFormat.format(JGitText.get().cannotReadFile, getFile())); - e2.initCause(e); - throw e2; - } catch (ConfigInvalidException e) { - throw new ConfigInvalidException(MessageFormat.format(JGitText.get().cannotReadFile, getFile()), e); - } - } - - /** - * Save the configuration as a Git text style configuration file. - *

- * Warning: Although this method uses the traditional Git file - * locking approach to protect against concurrent writes of the - * configuration file, it does not ensure that the file has not been - * modified since the last read, which means updates performed by other - * objects accessing the same backing file may be lost. - * - * @throws IOException - * the file could not be written. - */ - public void save() throws IOException { - final byte[] out = Constants.encode(toText()); - final LockFile lf = new LockFile(getFile()); - if (!lf.lock()) - throw new IOException(MessageFormat.format(JGitText.get().cannotLockFile, getFile())); - try { - lf.setNeedStatInformation(true); - lf.write(out); - if (!lf.commit()) - throw new IOException(MessageFormat.format(JGitText.get().cannotCommitWriteTo, getFile())); - } finally { - lf.unlock(); - } - lastModified = lf.getCommitLastModified(); - } - - @Override - public String toString() { - return getClass().getSimpleName() + "[" + getFile().getPath() + "]"; - } - - /** - * @return returns true if the currently loaded configuration file is older - * than the file on disk - */ - public boolean isOutdated() { - return getFile().lastModified() != lastModified; - } -} diff --git a/org.eclipse.jgit/src/org/eclipse/jgit/lib/FileObjectDatabase.java b/org.eclipse.jgit/src/org/eclipse/jgit/lib/FileObjectDatabase.java deleted file mode 100644 index 5328b327ec..0000000000 --- a/org.eclipse.jgit/src/org/eclipse/jgit/lib/FileObjectDatabase.java +++ /dev/null @@ -1,207 +0,0 @@ -/* - * 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 java.io.IOException; - -abstract class FileObjectDatabase extends ObjectDatabase { - @Override - public ObjectReader newReader() { - return new WindowCursor(this); - } - - /** - * Does the requested object exist in this database? - *

- * 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 hasObject(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. - *

- * 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; - } - - 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 FileObjectDatabase newCachedFileObjectDatabase(); - - 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/lib/FileRepository.java b/org.eclipse.jgit/src/org/eclipse/jgit/lib/FileRepository.java deleted file mode 100644 index f45ffdf149..0000000000 --- a/org.eclipse.jgit/src/org/eclipse/jgit/lib/FileRepository.java +++ /dev/null @@ -1,310 +0,0 @@ -/* - * Copyright (C) 2007, Dave Watson - * Copyright (C) 2008-2010, Google Inc. - * Copyright (C) 2006-2010, Robin Rosenberg - * Copyright (C) 2006-2008, Shawn O. Pearce - * 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.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.FileObjectDatabase.AlternateHandle; -import org.eclipse.jgit.lib.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. - * - *

    - *
  • GIT_DIR - *
      - *
    • objects/ - objects
    • - *
    • refs/ - tags and heads
    • - *
    • config - configuration
    • - *
    • info/ - more configurations
    • - *
    - *
  • - *
- *

- * This class is thread-safe. - *

- * 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. - *

- * 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: - * - *

-	 * new FileRepositoryBuilder().setGitDir(gitDir).build()
-	 * 
- * - * @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()); - } - - 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()); - - 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()}. - *

- * 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 getAdditionalHaves() { - HashSet r = new HashSet(); - 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/FileRepositoryBuilder.java b/org.eclipse.jgit/src/org/eclipse/jgit/lib/FileRepositoryBuilder.java deleted file mode 100644 index c0220d979e..0000000000 --- a/org.eclipse.jgit/src/org/eclipse/jgit/lib/FileRepositoryBuilder.java +++ /dev/null @@ -1,88 +0,0 @@ -/* - * 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 java.io.IOException; - -/** - * Constructs a {@link FileRepository}. - *

- * 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. - *

- * Single repository applications trying to be compatible with other Git - * implementations are encouraged to use a model such as: - * - *

- * 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()
- * 
- */ -public class FileRepositoryBuilder extends - BaseRepositoryBuilder { - /** - * Create a repository matching the configuration in this builder. - *

- * 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 FileRepository build() throws IOException { - return new FileRepository(setup()); - } -} diff --git a/org.eclipse.jgit/src/org/eclipse/jgit/lib/LocalObjectRepresentation.java b/org.eclipse.jgit/src/org/eclipse/jgit/lib/LocalObjectRepresentation.java deleted file mode 100644 index f8535bdf2d..0000000000 --- a/org.eclipse.jgit/src/org/eclipse/jgit/lib/LocalObjectRepresentation.java +++ /dev/null @@ -1,80 +0,0 @@ -/* - * 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; - -class LocalObjectRepresentation extends StoredObjectRepresentation { - final PackedObjectLoader ldr; - - LocalObjectRepresentation(PackedObjectLoader ldr) { - this.ldr = ldr; - } - - @Override - public int getFormat() { - if (ldr instanceof DeltaPackedObjectLoader) - return PACK_DELTA; - if (ldr instanceof WholePackedObjectLoader) - return PACK_WHOLE; - return FORMAT_OTHER; - } - - @Override - public int getWeight() { - long sz = ldr.getRawSize(); - if (Integer.MAX_VALUE < sz) - return WEIGHT_UNKNOWN; - return (int) sz; - } - - @Override - public ObjectId getDeltaBase() { - try { - return ldr.getDeltaBase(); - } catch (IOException e) { - return null; - } - } -} diff --git a/org.eclipse.jgit/src/org/eclipse/jgit/lib/LocalObjectToPack.java b/org.eclipse.jgit/src/org/eclipse/jgit/lib/LocalObjectToPack.java deleted file mode 100644 index 8db58707e8..0000000000 --- a/org.eclipse.jgit/src/org/eclipse/jgit/lib/LocalObjectToPack.java +++ /dev/null @@ -1,66 +0,0 @@ -/* - * 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 org.eclipse.jgit.revwalk.RevObject; - -/** {@link ObjectToPack} for {@link ObjectDirectory}. */ -class LocalObjectToPack extends ObjectToPack { - /** Pack to reuse compressed data from, otherwise null. */ - PackFile copyFromPack; - - /** Offset of the object's header in {@link #copyFromPack}. */ - long copyOffset; - - LocalObjectToPack(RevObject obj) { - super(obj); - } - - @Override - public void select(StoredObjectRepresentation ref) { - LocalObjectRepresentation ptr = (LocalObjectRepresentation)ref; - this.copyFromPack = ptr.ldr.pack; - this.copyOffset = ptr.ldr.objectOffset; - } -} diff --git a/org.eclipse.jgit/src/org/eclipse/jgit/lib/LockFile.java b/org.eclipse.jgit/src/org/eclipse/jgit/lib/LockFile.java deleted file mode 100644 index 13f158dedf..0000000000 --- a/org.eclipse.jgit/src/org/eclipse/jgit/lib/LockFile.java +++ /dev/null @@ -1,448 +0,0 @@ -/* - * Copyright (C) 2007, Robin Rosenberg - * Copyright (C) 2006-2008, Shawn O. Pearce - * 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.BufferedOutputStream; -import java.io.File; -import java.io.FileInputStream; -import java.io.FileNotFoundException; -import java.io.FileOutputStream; -import java.io.FilenameFilter; -import java.io.IOException; -import java.io.OutputStream; -import java.nio.channels.FileLock; -import java.nio.channels.OverlappingFileLockException; -import java.text.MessageFormat; - -import org.eclipse.jgit.JGitText; - -/** - * Git style file locking and replacement. - *

- * To modify a ref file Git tries to use an atomic update approach: we write the - * new data into a brand new file, then rename it in place over the old name. - * This way we can just delete the temporary file if anything goes wrong, and - * nothing has been damaged. To coordinate access from multiple processes at - * once Git tries to atomically create the new temporary file under a well-known - * name. - */ -public class LockFile { - static final String SUFFIX = ".lock"; //$NON-NLS-1$ - - /** Filter to skip over active lock files when listing a directory. */ - static final FilenameFilter FILTER = new FilenameFilter() { - public boolean accept(File dir, String name) { - return !name.endsWith(SUFFIX); - } - }; - - private final File ref; - - private final File lck; - - private FileLock fLck; - - private boolean haveLck; - - private FileOutputStream os; - - private boolean needStatInformation; - - private long commitLastModified; - - /** - * Create a new lock for any file. - * - * @param f - * the file that will be locked. - */ - public LockFile(final File f) { - ref = f; - lck = new File(ref.getParentFile(), ref.getName() + SUFFIX); - } - - /** - * Try to establish the lock. - * - * @return true if the lock is now held by the caller; false if it is held - * by someone else. - * @throws IOException - * the temporary output file could not be created. The caller - * does not hold the lock. - */ - public boolean lock() throws IOException { - lck.getParentFile().mkdirs(); - if (lck.createNewFile()) { - haveLck = true; - try { - os = new FileOutputStream(lck); - try { - fLck = os.getChannel().tryLock(); - if (fLck == null) - throw new OverlappingFileLockException(); - } catch (OverlappingFileLockException ofle) { - // We cannot use unlock() here as this file is not - // held by us, but we thought we created it. We must - // not delete it, as it belongs to some other process. - // - haveLck = false; - try { - os.close(); - } catch (IOException ioe) { - // Fail by returning haveLck = false. - } - os = null; - } - } catch (IOException ioe) { - unlock(); - throw ioe; - } - } - return haveLck; - } - - /** - * Try to establish the lock for appending. - * - * @return true if the lock is now held by the caller; false if it is held - * by someone else. - * @throws IOException - * the temporary output file could not be created. The caller - * does not hold the lock. - */ - public boolean lockForAppend() throws IOException { - if (!lock()) - return false; - copyCurrentContent(); - return true; - } - - /** - * Copy the current file content into the temporary file. - *

- * This method saves the current file content by inserting it into the - * temporary file, so that the caller can safely append rather than replace - * the primary file. - *

- * This method does nothing if the current file does not exist, or exists - * but is empty. - * - * @throws IOException - * the temporary file could not be written, or a read error - * occurred while reading from the current file. The lock is - * released before throwing the underlying IO exception to the - * caller. - * @throws RuntimeException - * the temporary file could not be written. The lock is released - * before throwing the underlying exception to the caller. - */ - public void copyCurrentContent() throws IOException { - requireLock(); - try { - final FileInputStream fis = new FileInputStream(ref); - try { - final byte[] buf = new byte[2048]; - int r; - while ((r = fis.read(buf)) >= 0) - os.write(buf, 0, r); - } finally { - fis.close(); - } - } catch (FileNotFoundException fnfe) { - // Don't worry about a file that doesn't exist yet, it - // conceptually has no current content to copy. - // - } catch (IOException ioe) { - unlock(); - throw ioe; - } catch (RuntimeException ioe) { - unlock(); - throw ioe; - } catch (Error ioe) { - unlock(); - throw ioe; - } - } - - /** - * Write an ObjectId and LF to the temporary file. - * - * @param id - * the id to store in the file. The id will be written in hex, - * followed by a sole LF. - * @throws IOException - * the temporary file could not be written. The lock is released - * before throwing the underlying IO exception to the caller. - * @throws RuntimeException - * the temporary file could not be written. The lock is released - * before throwing the underlying exception to the caller. - */ - public void write(final ObjectId id) throws IOException { - requireLock(); - try { - final BufferedOutputStream b; - b = new BufferedOutputStream(os, Constants.OBJECT_ID_STRING_LENGTH + 1); - id.copyTo(b); - b.write('\n'); - b.flush(); - fLck.release(); - b.close(); - os = null; - } catch (IOException ioe) { - unlock(); - throw ioe; - } catch (RuntimeException ioe) { - unlock(); - throw ioe; - } catch (Error ioe) { - unlock(); - throw ioe; - } - } - - /** - * Write arbitrary data to the temporary file. - * - * @param content - * the bytes to store in the temporary file. No additional bytes - * are added, so if the file must end with an LF it must appear - * at the end of the byte array. - * @throws IOException - * the temporary file could not be written. The lock is released - * before throwing the underlying IO exception to the caller. - * @throws RuntimeException - * the temporary file could not be written. The lock is released - * before throwing the underlying exception to the caller. - */ - public void write(final byte[] content) throws IOException { - requireLock(); - try { - os.write(content); - os.flush(); - fLck.release(); - os.close(); - os = null; - } catch (IOException ioe) { - unlock(); - throw ioe; - } catch (RuntimeException ioe) { - unlock(); - throw ioe; - } catch (Error ioe) { - unlock(); - throw ioe; - } - } - - /** - * Obtain the direct output stream for this lock. - *

- * The stream may only be accessed once, and only after {@link #lock()} has - * been successfully invoked and returned true. Callers must close the - * stream prior to calling {@link #commit()} to commit the change. - * - * @return a stream to write to the new file. The stream is unbuffered. - */ - public OutputStream getOutputStream() { - requireLock(); - return new OutputStream() { - @Override - public void write(final byte[] b, final int o, final int n) - throws IOException { - os.write(b, o, n); - } - - @Override - public void write(final byte[] b) throws IOException { - os.write(b); - } - - @Override - public void write(final int b) throws IOException { - os.write(b); - } - - @Override - public void flush() throws IOException { - os.flush(); - } - - @Override - public void close() throws IOException { - try { - os.flush(); - fLck.release(); - os.close(); - os = null; - } catch (IOException ioe) { - unlock(); - throw ioe; - } catch (RuntimeException ioe) { - unlock(); - throw ioe; - } catch (Error ioe) { - unlock(); - throw ioe; - } - } - }; - } - - private void requireLock() { - if (os == null) { - unlock(); - throw new IllegalStateException(MessageFormat.format(JGitText.get().lockOnNotHeld, ref)); - } - } - - /** - * Request that {@link #commit()} remember modification time. - * - * @param on - * true if the commit method must remember the modification time. - */ - public void setNeedStatInformation(final boolean on) { - needStatInformation = on; - } - - /** - * Wait until the lock file information differs from the old file. - *

- * This method tests both the length and the last modification date. If both - * are the same, this method sleeps until it can force the new lock file's - * modification date to be later than the target file. - * - * @throws InterruptedException - * the thread was interrupted before the last modified date of - * the lock file was different from the last modified date of - * the target file. - */ - public void waitForStatChange() throws InterruptedException { - if (ref.length() == lck.length()) { - long otime = ref.lastModified(); - long ntime = lck.lastModified(); - while (otime == ntime) { - Thread.sleep(25 /* milliseconds */); - lck.setLastModified(System.currentTimeMillis()); - ntime = lck.lastModified(); - } - } - } - - /** - * Commit this change and release the lock. - *

- * If this method fails (returns false) the lock is still released. - * - * @return true if the commit was successful and the file contains the new - * data; false if the commit failed and the file remains with the - * old data. - * @throws IllegalStateException - * the lock is not held. - */ - public boolean commit() { - if (os != null) { - unlock(); - throw new IllegalStateException(MessageFormat.format(JGitText.get().lockOnNotClosed, ref)); - } - - saveStatInformation(); - if (lck.renameTo(ref)) - return true; - if (!ref.exists() || ref.delete()) - if (lck.renameTo(ref)) - return true; - unlock(); - return false; - } - - private void saveStatInformation() { - if (needStatInformation) - commitLastModified = lck.lastModified(); - } - - /** - * Get the modification time of the output file when it was committed. - * - * @return modification time of the lock file right before we committed it. - */ - public long getCommitLastModified() { - return commitLastModified; - } - - /** - * Unlock this file and abort this change. - *

- * The temporary file (if created) is deleted before returning. - */ - public void unlock() { - if (os != null) { - if (fLck != null) { - try { - fLck.release(); - } catch (IOException ioe) { - // Huh? - } - fLck = null; - } - try { - os.close(); - } catch (IOException ioe) { - // Ignore this - } - os = null; - } - - if (haveLck) { - haveLck = false; - lck.delete(); - } - } - - @Override - public String toString() { - return "LockFile[" + lck + ", haveLck=" + haveLck + "]"; - } -} diff --git a/org.eclipse.jgit/src/org/eclipse/jgit/lib/ObjectDirectory.java b/org.eclipse.jgit/src/org/eclipse/jgit/lib/ObjectDirectory.java deleted file mode 100644 index bf8f323341..0000000000 --- a/org.eclipse.jgit/src/org/eclipse/jgit/lib/ObjectDirectory.java +++ /dev/null @@ -1,621 +0,0 @@ -/* - * 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.BufferedReader; -import java.io.File; -import java.io.FileNotFoundException; -import java.io.FileReader; -import java.io.IOException; -import java.text.MessageFormat; -import java.util.ArrayList; -import java.util.Arrays; -import java.util.Collection; -import java.util.Collections; -import java.util.HashMap; -import java.util.HashSet; -import java.util.List; -import java.util.Map; -import java.util.Set; -import java.util.concurrent.atomic.AtomicReference; - -import org.eclipse.jgit.JGitText; -import org.eclipse.jgit.errors.PackMismatchException; -import org.eclipse.jgit.lib.RepositoryCache.FileKey; -import org.eclipse.jgit.util.FS; - -/** - * Traditional file system based {@link ObjectDatabase}. - *

- * This is the classical object database representation for a Git repository, - * 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. - *

- * Optionally an object database can reference one or more alternates; other - * ObjectDatabase instances that are searched in addition to the current - * database. - *

- * 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 FileObjectDatabase { - 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; - - private final File packDirectory; - - private final File alternatesFile; - - private final AtomicReference packList; - - private final FS fs; - - private final AtomicReference alternates; - - /** - * Initialize a reference to an on-disk object directory. - * - * @param cfg - * configuration this directory consults for write settings. - * @param dir - * the location of the objects directory. - * @param alternatePaths - * a list of alternate object directories - * @param fs - * 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 Config cfg, final File dir, - File[] alternatePaths, FS fs) throws IOException { - config = cfg; - objects = dir; - infoDirectory = new File(objects, "info"); - packDirectory = new File(objects, "pack"); - alternatesFile = new File(infoDirectory, "alternates"); - packList = new AtomicReference(NO_PACKS); - this.fs = fs; - - alternates = new AtomicReference(); - 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); - } - } - - /** - * @return the location of the objects directory. - */ - public final File getDirectory() { - return objects; - } - - @Override - public boolean exists() { - return objects.exists(); - } - - @Override - public void create() throws IOException { - objects.mkdirs(); - infoDirectory.mkdir(); - packDirectory.mkdir(); - } - - @Override - 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(); - } - } - - /** - * Compute the location of a loose object file. - * - * @param objectId - * identity of the loose object to map to the directory. - * @return location of the object, if it were to exist as a loose object. - */ - public File fileFor(final AnyObjectId objectId) { - return fileFor(objectId.name()); - } - - private File fileFor(final String objectName) { - final String d = objectName.substring(0, 2); - final String f = objectName.substring(2); - return new File(new File(objects, d), f); - } - - /** - * @return unmodifiable collection of all known pack files local to this - * directory. Most recent packs are presented first. Packs most - * likely to contain more recent objects appear before packs - * containing objects referenced by commits further back in the - * history of the repository. - */ - public Collection getPacks() { - final PackFile[] packs = packList.get().packs; - return Collections.unmodifiableCollection(Arrays.asList(packs)); - } - - /** - * 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 { - final String p = pack.getName(); - final String i = idx.getName(); - - if (p.length() != 50 || !p.startsWith("pack-") || !p.endsWith(".pack")) - throw new IOException(MessageFormat.format(JGitText.get().notAValidPack, pack)); - - if (i.length() != 49 || !i.startsWith("pack-") || !i.endsWith(".idx")) - throw new IOException(MessageFormat.format(JGitText.get().notAValidPack, idx)); - - if (!p.substring(0, 45).equals(i.substring(0, 45))) - throw new IOException(MessageFormat.format(JGitText.get().packDoesNotMatchIndex, pack)); - - insertPack(new PackFile(idx, pack)); - } - - @Override - public String toString() { - return "ObjectDirectory[" + getDirectory() + "]"; - } - - boolean hasObject1(final AnyObjectId objectId) { - for (final PackFile p : packList.get().packs) { - try { - if (p.hasObject(objectId)) { - return true; - } - } catch (IOException e) { - // The hasObject call should have only touched the index, - // so any failure here indicates the index is unreadable - // by this process, and the pack is likewise not readable. - // - removePack(p); - continue; - } - } - return false; - } - - 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); - return ldr; - } - } 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); - } - } - return null; - } - } - - @Override - void selectObjectRepresentation(PackWriter packer, ObjectToPack otp, - WindowCursor curs) throws IOException { - PackList pList = packList.get(); - SEARCH: for (;;) { - for (final PackFile p : pList.packs) { - try { - PackedObjectLoader ldr = p.get(curs, otp); - if (ldr != null) - packer.select(otp, new LocalObjectRepresentation(ldr)); - } 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); - } catch (FileNotFoundException noFile) { - return null; - } - } - - boolean tryAgain1() { - final PackList old = packList.get(); - if (old.tryAgain(packDirectory.lastModified())) - return old != scanPacks(old); - return false; - } - - private void insertPack(final PackFile pf) { - PackList o, n; - do { - o = packList.get(); - - // If the pack in question is already present in the list - // (picked up by a concurrent thread that did a scan?) we - // do not want to insert it a second time. - // - final PackFile[] oldList = o.packs; - final String name = pf.getPackFile().getName(); - for (PackFile p : oldList) { - if (PackFile.SORT.compare(pf, p) < 0) - break; - if (name.equals(p.getPackFile().getName())) - return; - } - - final PackFile[] newList = new PackFile[1 + oldList.length]; - newList[0] = pf; - System.arraycopy(oldList, 0, newList, 1, oldList.length); - n = new PackList(o.lastRead, o.lastModified, newList); - } while (!packList.compareAndSet(o, n)); - } - - private void removePack(final PackFile deadPack) { - PackList o, n; - do { - o = packList.get(); - - final PackFile[] oldList = o.packs; - final int j = indexOf(oldList, deadPack); - if (j < 0) - break; - - final PackFile[] newList = new PackFile[oldList.length - 1]; - System.arraycopy(oldList, 0, newList, 0, j); - System.arraycopy(oldList, j + 1, newList, j, newList.length - j); - n = new PackList(o.lastRead, o.lastModified, newList); - } while (!packList.compareAndSet(o, n)); - deadPack.close(); - } - - private static int indexOf(final PackFile[] list, final PackFile pack) { - for (int i = 0; i < list.length; i++) { - if (list[i] == pack) - return i; - } - return -1; - } - - private PackList scanPacks(final PackList original) { - synchronized (packList) { - PackList o, n; - do { - o = packList.get(); - if (o != original) { - // Another thread did the scan for us, while we - // were blocked on the monitor above. - // - return o; - } - n = scanPacksImpl(o); - if (n == o) - return n; - } while (!packList.compareAndSet(o, n)); - return n; - } - } - - private PackList scanPacksImpl(final PackList old) { - final Map forReuse = reuseMap(old); - final long lastRead = System.currentTimeMillis(); - final long lastModified = packDirectory.lastModified(); - final Set names = listPackDirectory(); - final List list = new ArrayList(names.size() >> 2); - boolean foundNew = false; - for (final String indexName : names) { - // Must match "pack-[0-9a-f]{40}.idx" to be an index. - // - if (indexName.length() != 49 || !indexName.endsWith(".idx")) - continue; - - final String base = indexName.substring(0, indexName.length() - 4); - final String packName = base + ".pack"; - if (!names.contains(packName)) { - // Sometimes C Git's HTTP fetch transport leaves a - // .idx file behind and does not download the .pack. - // We have to skip over such useless indexes. - // - continue; - } - - final PackFile oldPack = forReuse.remove(packName); - if (oldPack != null) { - list.add(oldPack); - continue; - } - - final File packFile = new File(packDirectory, packName); - final File idxFile = new File(packDirectory, indexName); - list.add(new PackFile(idxFile, packFile)); - foundNew = true; - } - - // If we did not discover any new files, the modification time was not - // changed, and we did not remove any files, then the set of files is - // the same as the set we were given. Instead of building a new object - // return the same collection. - // - if (!foundNew && lastModified == old.lastModified && forReuse.isEmpty()) - return old.updateLastRead(lastRead); - - for (final PackFile p : forReuse.values()) { - p.close(); - } - - if (list.isEmpty()) - return new PackList(lastRead, lastModified, NO_PACKS.packs); - - final PackFile[] r = list.toArray(new PackFile[list.size()]); - Arrays.sort(r, PackFile.SORT); - return new PackList(lastRead, lastModified, r); - } - - private static Map reuseMap(final PackList old) { - final Map forReuse = new HashMap(); - for (final PackFile p : old.packs) { - if (p.invalid()) { - // The pack instance is corrupted, and cannot be safely used - // again. Do not include it in our reuse map. - // - p.close(); - continue; - } - - final PackFile prior = forReuse.put(p.getPackFile().getName(), p); - if (prior != null) { - // This should never occur. It should be impossible for us - // to have two pack files with the same name, as all of them - // came out of the same directory. If it does, we promised to - // close any PackFiles we did not reuse, so close the second, - // readers are likely to be actively using the first. - // - forReuse.put(prior.getPackFile().getName(), prior); - p.close(); - } - } - return forReuse; - } - - private Set listPackDirectory() { - final String[] nameList = packDirectory.list(); - if (nameList == null) - return Collections.emptySet(); - final Set nameSet = new HashSet(nameList.length << 1); - for (final String name : nameList) { - if (name.startsWith("pack-")) - nameSet.add(name); - } - return nameSet; - } - - 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); - } - } - } - return alt; - } - - private AlternateHandle[] loadAlternates() throws IOException { - final List l = new ArrayList(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 AlternateHandle[l.size()]); - } - - private static BufferedReader open(final File f) - throws FileNotFoundException { - return new BufferedReader(new FileReader(f)); - } - - private AlternateHandle openAlternate(final String location) - throws IOException { - final File objdir = fs.resolve(objects, location); - return openAlternate(objdir); - } - - private AlternateHandle openAlternate(File objdir) throws IOException { - final File parent = objdir.getParentFile(); - if (FileKey.isGitRepository(parent, fs)) { - FileKey key = FileKey.exact(parent, fs); - FileRepository db = (FileRepository) RepositoryCache.open(key); - return new AlternateRepository(db); - } - - ObjectDirectory db = new ObjectDirectory(config, objdir, null, fs); - return new AlternateHandle(db); - } - - private static final class PackList { - /** Last wall-clock time the directory was read. */ - volatile long lastRead; - - /** Last modification time of {@link ObjectDirectory#packDirectory}. */ - final long lastModified; - - /** All known packs, sorted by {@link PackFile#SORT}. */ - final PackFile[] packs; - - private boolean cannotBeRacilyClean; - - PackList(final long lastRead, final long lastModified, - final PackFile[] packs) { - this.lastRead = lastRead; - this.lastModified = lastModified; - this.packs = packs; - this.cannotBeRacilyClean = notRacyClean(lastRead); - } - - private boolean notRacyClean(final long read) { - return read - lastModified > 2 * 60 * 1000L; - } - - PackList updateLastRead(final long now) { - if (notRacyClean(now)) - cannotBeRacilyClean = true; - lastRead = now; - return this; - } - - boolean tryAgain(final long currLastModified) { - // Any difference indicates the directory was modified. - // - if (lastModified != currLastModified) - return true; - - // We have already determined the last read was far enough - // after the last modification that any new modifications - // are certain to change the last modified time. - // - if (cannotBeRacilyClean) - return false; - - if (notRacyClean(lastRead)) { - // Our last read should have marked cannotBeRacilyClean, - // but this thread may not have seen the change. The read - // of the volatile field lastRead should have fixed that. - // - return false; - } - - // We last read this directory too close to its last observed - // modification time. We may have missed a modification. Scan - // the directory again, to ensure we still see the same state. - // - return true; - } - } - - @Override - public ObjectDatabase newCachedDatabase() { - return newCachedFileObjectDatabase(); - } - - FileObjectDatabase newCachedFileObjectDatabase() { - return new CachedObjectDirectory(this); - } -} diff --git a/org.eclipse.jgit/src/org/eclipse/jgit/lib/ObjectDirectoryInserter.java b/org.eclipse.jgit/src/org/eclipse/jgit/lib/ObjectDirectoryInserter.java deleted file mode 100644 index 146d2d6250..0000000000 --- a/org.eclipse.jgit/src/org/eclipse/jgit/lib/ObjectDirectoryInserter.java +++ /dev/null @@ -1,177 +0,0 @@ -/* - * Copyright (C) 2007, Robin Rosenberg - * Copyright (C) 2008, Shawn O. Pearce - * 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.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; - -/** 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.hasObject(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.hasObject(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/lib/PackFile.java b/org.eclipse.jgit/src/org/eclipse/jgit/lib/PackFile.java deleted file mode 100644 index ab25f61889..0000000000 --- a/org.eclipse.jgit/src/org/eclipse/jgit/lib/PackFile.java +++ /dev/null @@ -1,695 +0,0 @@ -/* - * Copyright (C) 2008-2009, Google Inc. - * Copyright (C) 2007, Robin Rosenberg - * Copyright (C) 2006-2008, Shawn O. Pearce - * 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.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.PackInvalidException; -import org.eclipse.jgit.errors.PackMismatchException; -import org.eclipse.jgit.errors.StoredObjectRepresentationNotAvailableException; -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 { - /** Sorts PackFiles to be most recently created to least recently created. */ - public static Comparator SORT = new Comparator() { - 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. - *

- * 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 .idx file listing the contents. - * @param packFile - * path of the .pack 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 { - if (isCorrupt(ofs)) { - throw new CorruptObjectException(MessageFormat.format(JGitText - .get().objectAtHasBadZlibStream, ofs, getPackFile())); - } - 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. - *

- * For performance reasons only the index file is searched; the main pack - * content is ignored entirely. - *

- * - * @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. - */ - public PackedObjectLoader get(final WindowCursor curs, final AnyObjectId id) - throws IOException { - final long offset = idx().findOffset(id); - return 0 < offset && !isCorrupt(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. - *

- * Iterator returns objects in SHA-1 lexicographical order. - *

- * - * @return iterator over entries of associated pack index - * - * @see PackIndex#iterator() - */ - public Iterator iterator() { - try { - return idx().iterator(); - } catch (IOException e) { - return Collections. 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 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.copyOffset, 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.copyOffset + 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.copyOffset + headerCnt; - final long dataLength; - final long expectedCRC; - final ByteArrayWindow quickCopy; - - // Verify the object isn't corrupt before sending. If it is, - // we report it missing instead. - // - try { - dataLength = findEndOffset(src.copyOffset) - dataOffset; - 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.copyOffset); - throw new CorruptObjectException(MessageFormat.format( - JGitText.get().objectAtHasBadZlibStream, - src.copyOffset, 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.copyOffset); - throw new EOFException(MessageFormat.format( - JGitText.get().shortCompressedStreamAt, - src.copyOffset)); - } - expectedCRC = crc1.getValue(); - } - } catch (DataFormatException dataFormat) { - setCorrupt(src.copyOffset); - - CorruptObjectException corruptObject = new CorruptObjectException( - MessageFormat.format( - JGitText.get().objectAtHasBadZlibStream, - src.copyOffset, 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.copyOffset, - 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())); - } - - 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; - } - - 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/lib/PackIndex.java deleted file mode 100644 index 13985e78e9..0000000000 --- a/org.eclipse.jgit/src/org/eclipse/jgit/lib/PackIndex.java +++ /dev/null @@ -1,315 +0,0 @@ -/* - * Copyright (C) 2008, Marek Zawirski - * Copyright (C) 2008, Shawn O. Pearce - * 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.FileInputStream; -import java.io.FileNotFoundException; -import java.io.IOException; -import java.text.MessageFormat; -import java.util.Iterator; - -import org.eclipse.jgit.JGitText; -import org.eclipse.jgit.errors.MissingObjectException; -import org.eclipse.jgit.util.IO; -import org.eclipse.jgit.util.NB; - -/** - * Access path to locate objects by {@link ObjectId} in a {@link PackFile}. - *

- * Indexes are strictly redundant information in that we can rebuild all of the - * data held in the index file from the on disk representation of the pack file - * itself, but it is faster to access for random requests because data is stored - * by ObjectId. - *

- */ -public abstract class PackIndex implements Iterable { - /** - * Open an existing pack .idx file for reading. - *

- * The format of the file will be automatically detected and a proper access - * implementation for that format will be constructed and returned to the - * caller. The file may or may not be held open by the returned instance. - *

- * - * @param idxFile - * existing pack .idx to read. - * @return access implementation for the requested file. - * @throws FileNotFoundException - * the file does not exist. - * @throws IOException - * the file exists but could not be read due to security errors, - * unrecognized data version, or unexpected data corruption. - */ - public static PackIndex open(final File idxFile) throws IOException { - final FileInputStream fd = new FileInputStream(idxFile); - try { - final byte[] hdr = new byte[8]; - IO.readFully(fd, hdr, 0, hdr.length); - if (isTOC(hdr)) { - final int v = NB.decodeInt32(hdr, 4); - switch (v) { - case 2: - return new PackIndexV2(fd); - default: - throw new IOException(MessageFormat.format(JGitText.get().unsupportedPackIndexVersion, v)); - } - } - return new PackIndexV1(fd, hdr); - } catch (IOException ioe) { - final String path = idxFile.getAbsolutePath(); - final IOException err; - err = new IOException(MessageFormat.format(JGitText.get().unreadablePackIndex, path)); - err.initCause(ioe); - throw err; - } finally { - try { - fd.close(); - } catch (IOException err2) { - // ignore - } - } - } - - private static boolean isTOC(final byte[] h) { - final byte[] toc = PackIndexWriter.TOC; - for (int i = 0; i < toc.length; i++) - if (h[i] != toc[i]) - return false; - return true; - } - - /** Footer checksum applied on the bottom of the pack file. */ - protected byte[] packChecksum; - - /** - * Determine if an object is contained within the pack file. - * - * @param id - * the object to look for. Must not be null. - * @return true if the object is listed in this index; false otherwise. - */ - public boolean hasObject(final AnyObjectId id) { - return findOffset(id) != -1; - } - - /** - * Provide iterator that gives access to index entries. Note, that iterator - * returns reference to mutable object, the same reference in each call - - * for performance reason. If client needs immutable objects, it must copy - * returned object on its own. - *

- * Iterator returns objects in SHA-1 lexicographical order. - *

- * - * @return iterator over pack index entries - */ - public abstract Iterator iterator(); - - /** - * Obtain the total number of objects described by this index. - * - * @return number of objects in this index, and likewise in the associated - * pack that this index was generated from. - */ - abstract long getObjectCount(); - - /** - * Obtain the total number of objects needing 64 bit offsets. - * - * @return number of objects in this index using a 64 bit offset; that is an - * object positioned after the 2 GB position within the file. - */ - abstract long getOffset64Count(); - - /** - * Get ObjectId for the n-th object entry returned by {@link #iterator()}. - *

- * This method is a constant-time replacement for the following loop: - * - *

-	 * Iterator<MutableEntry> eItr = index.iterator();
-	 * int curPosition = 0;
-	 * while (eItr.hasNext() && curPosition++ < nthPosition)
-	 * 	eItr.next();
-	 * ObjectId result = eItr.next().toObjectId();
-	 * 
- * - * @param nthPosition - * position within the traversal of {@link #iterator()} that the - * caller needs the object for. The first returned - * {@link MutableEntry} is 0, the second is 1, etc. - * @return the ObjectId for the corresponding entry. - */ - abstract ObjectId getObjectId(long nthPosition); - - /** - * Get ObjectId for the n-th object entry returned by {@link #iterator()}. - *

- * This method is a constant-time replacement for the following loop: - * - *

-	 * Iterator<MutableEntry> eItr = index.iterator();
-	 * int curPosition = 0;
-	 * while (eItr.hasNext() && curPosition++ < nthPosition)
-	 * 	eItr.next();
-	 * ObjectId result = eItr.next().toObjectId();
-	 * 
- * - * @param nthPosition - * unsigned 32 bit position within the traversal of - * {@link #iterator()} that the caller needs the object for. The - * first returned {@link MutableEntry} is 0, the second is 1, - * etc. Positions past 2**31-1 are negative, but still valid. - * @return the ObjectId for the corresponding entry. - */ - final ObjectId getObjectId(final int nthPosition) { - if (nthPosition >= 0) - return getObjectId((long) nthPosition); - final int u31 = nthPosition >>> 1; - final int one = nthPosition & 1; - return getObjectId(((long) u31) << 1 | one); - } - - /** - * Locate the file offset position for the requested object. - * - * @param objId - * name of the object to locate within the pack. - * @return offset of the object's header and compressed content; -1 if the - * object does not exist in this index and is thus not stored in the - * associated pack. - */ - abstract long findOffset(AnyObjectId objId); - - /** - * Retrieve stored CRC32 checksum of the requested object raw-data - * (including header). - * - * @param objId - * id of object to look for - * @return CRC32 checksum of specified object (at 32 less significant bits) - * @throws MissingObjectException - * when requested ObjectId was not found in this index - * @throws UnsupportedOperationException - * when this index doesn't support CRC32 checksum - */ - abstract long findCRC32(AnyObjectId objId) throws MissingObjectException, - UnsupportedOperationException; - - /** - * Check whether this index supports (has) CRC32 checksums for objects. - * - * @return true if CRC32 is stored, false otherwise - */ - abstract boolean hasCRC32Support(); - - /** - * Represent mutable entry of pack index consisting of object id and offset - * in pack (both mutable). - * - */ - public static class MutableEntry { - final MutableObjectId idBuffer = new MutableObjectId(); - - long offset; - - /** - * Returns offset for this index object entry - * - * @return offset of this object in a pack file - */ - public long getOffset() { - return offset; - } - - /** @return hex string describing the object id of this entry. */ - public String name() { - ensureId(); - return idBuffer.name(); - } - - /** @return a copy of the object id. */ - public ObjectId toObjectId() { - ensureId(); - return idBuffer.toObjectId(); - } - - /** @return a complete copy of this entry, that won't modify */ - public MutableEntry cloneEntry() { - final MutableEntry r = new MutableEntry(); - ensureId(); - r.idBuffer.fromObjectId(idBuffer); - r.offset = offset; - return r; - } - - void ensureId() { - // Override in implementations. - } - } - - abstract class EntriesIterator implements Iterator { - protected final MutableEntry entry = initEntry(); - - protected long returnedNumber = 0; - - protected abstract MutableEntry initEntry(); - - public boolean hasNext() { - return returnedNumber < getObjectCount(); - } - - /** - * Implementation must update {@link #returnedNumber} before returning - * element. - */ - public abstract MutableEntry next(); - - public void remove() { - throw new UnsupportedOperationException(); - } - } -} diff --git a/org.eclipse.jgit/src/org/eclipse/jgit/lib/PackIndexV1.java b/org.eclipse.jgit/src/org/eclipse/jgit/lib/PackIndexV1.java deleted file mode 100644 index bb7cd8b53c..0000000000 --- a/org.eclipse.jgit/src/org/eclipse/jgit/lib/PackIndexV1.java +++ /dev/null @@ -1,202 +0,0 @@ -/* - * Copyright (C) 2008-2009, Google Inc. - * Copyright (C) 2008, Marek Zawirski - * Copyright (C) 2007-2009, Robin Rosenberg - * Copyright (C) 2006-2008, Shawn O. Pearce - * 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; -import java.util.Arrays; -import java.util.Iterator; -import java.util.NoSuchElementException; - -import org.eclipse.jgit.errors.CorruptObjectException; -import org.eclipse.jgit.util.IO; -import org.eclipse.jgit.util.NB; - -class PackIndexV1 extends PackIndex { - private static final int IDX_HDR_LEN = 256 * 4; - - private final long[] idxHeader; - - private byte[][] idxdata; - - private long objectCnt; - - PackIndexV1(final InputStream fd, final byte[] hdr) - throws CorruptObjectException, IOException { - final byte[] fanoutTable = new byte[IDX_HDR_LEN]; - System.arraycopy(hdr, 0, fanoutTable, 0, hdr.length); - IO.readFully(fd, fanoutTable, hdr.length, IDX_HDR_LEN - hdr.length); - - idxHeader = new long[256]; // really unsigned 32-bit... - for (int k = 0; k < idxHeader.length; k++) - idxHeader[k] = NB.decodeUInt32(fanoutTable, k * 4); - idxdata = new byte[idxHeader.length][]; - for (int k = 0; k < idxHeader.length; k++) { - int n; - if (k == 0) { - n = (int) (idxHeader[k]); - } else { - n = (int) (idxHeader[k] - idxHeader[k - 1]); - } - if (n > 0) { - idxdata[k] = new byte[n * (Constants.OBJECT_ID_LENGTH + 4)]; - IO.readFully(fd, idxdata[k], 0, idxdata[k].length); - } - } - objectCnt = idxHeader[255]; - - packChecksum = new byte[20]; - IO.readFully(fd, packChecksum, 0, packChecksum.length); - } - - long getObjectCount() { - return objectCnt; - } - - @Override - long getOffset64Count() { - long n64 = 0; - for (final MutableEntry e : this) { - if (e.getOffset() >= Integer.MAX_VALUE) - n64++; - } - return n64; - } - - @Override - ObjectId getObjectId(final long nthPosition) { - int levelOne = Arrays.binarySearch(idxHeader, nthPosition + 1); - long base; - if (levelOne >= 0) { - // If we hit the bucket exactly the item is in the bucket, or - // any bucket before it which has the same object count. - // - base = idxHeader[levelOne]; - while (levelOne > 0 && base == idxHeader[levelOne - 1]) - levelOne--; - } else { - // The item is in the bucket we would insert it into. - // - levelOne = -(levelOne + 1); - } - - base = levelOne > 0 ? idxHeader[levelOne - 1] : 0; - final int p = (int) (nthPosition - base); - final int dataIdx = ((4 + Constants.OBJECT_ID_LENGTH) * p) + 4; - return ObjectId.fromRaw(idxdata[levelOne], dataIdx); - } - - long findOffset(final AnyObjectId objId) { - final int levelOne = objId.getFirstByte(); - byte[] data = idxdata[levelOne]; - if (data == null) - return -1; - int high = data.length / (4 + Constants.OBJECT_ID_LENGTH); - int low = 0; - do { - final int mid = (low + high) >>> 1; - final int pos = ((4 + Constants.OBJECT_ID_LENGTH) * mid) + 4; - final int cmp = objId.compareTo(data, pos); - if (cmp < 0) - high = mid; - else if (cmp == 0) { - int b0 = data[pos - 4] & 0xff; - int b1 = data[pos - 3] & 0xff; - int b2 = data[pos - 2] & 0xff; - int b3 = data[pos - 1] & 0xff; - return (((long) b0) << 24) | (b1 << 16) | (b2 << 8) | (b3); - } else - low = mid + 1; - } while (low < high); - return -1; - } - - @Override - long findCRC32(AnyObjectId objId) { - throw new UnsupportedOperationException(); - } - - @Override - boolean hasCRC32Support() { - return false; - } - - public Iterator iterator() { - return new IndexV1Iterator(); - } - - private class IndexV1Iterator extends EntriesIterator { - private int levelOne; - - private int levelTwo; - - @Override - protected MutableEntry initEntry() { - return new MutableEntry() { - protected void ensureId() { - idBuffer.fromRaw(idxdata[levelOne], levelTwo - - Constants.OBJECT_ID_LENGTH); - } - }; - } - - public MutableEntry next() { - for (; levelOne < idxdata.length; levelOne++) { - if (idxdata[levelOne] == null) - continue; - if (levelTwo < idxdata[levelOne].length) { - entry.offset = NB.decodeUInt32(idxdata[levelOne], levelTwo); - levelTwo += Constants.OBJECT_ID_LENGTH + 4; - returnedNumber++; - return entry; - } - levelTwo = 0; - } - throw new NoSuchElementException(); - } - } -} diff --git a/org.eclipse.jgit/src/org/eclipse/jgit/lib/PackIndexV2.java b/org.eclipse.jgit/src/org/eclipse/jgit/lib/PackIndexV2.java deleted file mode 100644 index 128b2df8cb..0000000000 --- a/org.eclipse.jgit/src/org/eclipse/jgit/lib/PackIndexV2.java +++ /dev/null @@ -1,279 +0,0 @@ -/* - * Copyright (C) 2008, Shawn O. Pearce - * 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; -import java.util.Arrays; -import java.util.Iterator; -import java.util.NoSuchElementException; - -import org.eclipse.jgit.JGitText; -import org.eclipse.jgit.errors.MissingObjectException; -import org.eclipse.jgit.util.IO; -import org.eclipse.jgit.util.NB; - -/** Support for the pack index v2 format. */ -class PackIndexV2 extends PackIndex { - private static final long IS_O64 = 1L << 31; - - private static final int FANOUT = 256; - - private static final int[] NO_INTS = {}; - - private static final byte[] NO_BYTES = {}; - - private long objectCnt; - - private final long[] fanoutTable; - - /** 256 arrays of contiguous object names. */ - private int[][] names; - - /** 256 arrays of the 32 bit offset data, matching {@link #names}. */ - private byte[][] offset32; - - /** 256 arrays of the CRC-32 of objects, matching {@link #names}. */ - private byte[][] crc32; - - /** 64 bit offset table. */ - private byte[] offset64; - - PackIndexV2(final InputStream fd) throws IOException { - final byte[] fanoutRaw = new byte[4 * FANOUT]; - IO.readFully(fd, fanoutRaw, 0, fanoutRaw.length); - fanoutTable = new long[FANOUT]; - for (int k = 0; k < FANOUT; k++) - fanoutTable[k] = NB.decodeUInt32(fanoutRaw, k * 4); - objectCnt = fanoutTable[FANOUT - 1]; - - names = new int[FANOUT][]; - offset32 = new byte[FANOUT][]; - crc32 = new byte[FANOUT][]; - - // Object name table. The size we can permit per fan-out bucket - // is limited to Java's 2 GB per byte array limitation. That is - // no more than 107,374,182 objects per fan-out. - // - for (int k = 0; k < FANOUT; k++) { - final long bucketCnt; - if (k == 0) - bucketCnt = fanoutTable[k]; - else - bucketCnt = fanoutTable[k] - fanoutTable[k - 1]; - - if (bucketCnt == 0) { - names[k] = NO_INTS; - offset32[k] = NO_BYTES; - crc32[k] = NO_BYTES; - continue; - } - - final long nameLen = bucketCnt * Constants.OBJECT_ID_LENGTH; - if (nameLen > Integer.MAX_VALUE) - throw new IOException(JGitText.get().indexFileIsTooLargeForJgit); - - final int intNameLen = (int) nameLen; - final byte[] raw = new byte[intNameLen]; - final int[] bin = new int[intNameLen >>> 2]; - IO.readFully(fd, raw, 0, raw.length); - for (int i = 0; i < bin.length; i++) - bin[i] = NB.decodeInt32(raw, i << 2); - - names[k] = bin; - offset32[k] = new byte[(int) (bucketCnt * 4)]; - crc32[k] = new byte[(int) (bucketCnt * 4)]; - } - - // CRC32 table. - for (int k = 0; k < FANOUT; k++) - IO.readFully(fd, crc32[k], 0, crc32[k].length); - - // 32 bit offset table. Any entries with the most significant bit - // set require a 64 bit offset entry in another table. - // - int o64cnt = 0; - for (int k = 0; k < FANOUT; k++) { - final byte[] ofs = offset32[k]; - IO.readFully(fd, ofs, 0, ofs.length); - for (int p = 0; p < ofs.length; p += 4) - if (ofs[p] < 0) - o64cnt++; - } - - // 64 bit offset table. Most objects should not require an entry. - // - if (o64cnt > 0) { - offset64 = new byte[o64cnt * 8]; - IO.readFully(fd, offset64, 0, offset64.length); - } else { - offset64 = NO_BYTES; - } - - packChecksum = new byte[20]; - IO.readFully(fd, packChecksum, 0, packChecksum.length); - } - - @Override - long getObjectCount() { - return objectCnt; - } - - @Override - long getOffset64Count() { - return offset64.length / 8; - } - - @Override - ObjectId getObjectId(final long nthPosition) { - int levelOne = Arrays.binarySearch(fanoutTable, nthPosition + 1); - long base; - if (levelOne >= 0) { - // If we hit the bucket exactly the item is in the bucket, or - // any bucket before it which has the same object count. - // - base = fanoutTable[levelOne]; - while (levelOne > 0 && base == fanoutTable[levelOne - 1]) - levelOne--; - } else { - // The item is in the bucket we would insert it into. - // - levelOne = -(levelOne + 1); - } - - base = levelOne > 0 ? fanoutTable[levelOne - 1] : 0; - final int p = (int) (nthPosition - base); - final int p4 = p << 2; - return ObjectId.fromRaw(names[levelOne], p4 + p); // p * 5 - } - - @Override - long findOffset(final AnyObjectId objId) { - final int levelOne = objId.getFirstByte(); - final int levelTwo = binarySearchLevelTwo(objId, levelOne); - if (levelTwo == -1) - return -1; - final long p = NB.decodeUInt32(offset32[levelOne], levelTwo << 2); - if ((p & IS_O64) != 0) - return NB.decodeUInt64(offset64, (8 * (int) (p & ~IS_O64))); - return p; - } - - @Override - long findCRC32(AnyObjectId objId) throws MissingObjectException { - final int levelOne = objId.getFirstByte(); - final int levelTwo = binarySearchLevelTwo(objId, levelOne); - if (levelTwo == -1) - throw new MissingObjectException(objId.copy(), "unknown"); - return NB.decodeUInt32(crc32[levelOne], levelTwo << 2); - } - - @Override - boolean hasCRC32Support() { - return true; - } - - public Iterator iterator() { - return new EntriesIteratorV2(); - } - - private int binarySearchLevelTwo(final AnyObjectId objId, final int levelOne) { - final int[] data = names[levelOne]; - int high = offset32[levelOne].length >>> 2; - if (high == 0) - return -1; - int low = 0; - do { - final int mid = (low + high) >>> 1; - final int mid4 = mid << 2; - final int cmp; - - cmp = objId.compareTo(data, mid4 + mid); // mid * 5 - if (cmp < 0) - high = mid; - else if (cmp == 0) { - return mid; - } else - low = mid + 1; - } while (low < high); - return -1; - } - - private class EntriesIteratorV2 extends EntriesIterator { - private int levelOne; - - private int levelTwo; - - @Override - protected MutableEntry initEntry() { - return new MutableEntry() { - protected void ensureId() { - idBuffer.fromRaw(names[levelOne], levelTwo - - Constants.OBJECT_ID_LENGTH / 4); - } - }; - } - - public MutableEntry next() { - for (; levelOne < names.length; levelOne++) { - if (levelTwo < names[levelOne].length) { - int idx = levelTwo / (Constants.OBJECT_ID_LENGTH / 4) * 4; - long offset = NB.decodeUInt32(offset32[levelOne], idx); - if ((offset & IS_O64) != 0) { - idx = (8 * (int) (offset & ~IS_O64)); - offset = NB.decodeUInt64(offset64, idx); - } - entry.offset = offset; - - levelTwo += Constants.OBJECT_ID_LENGTH / 4; - returnedNumber++; - return entry; - } - levelTwo = 0; - } - throw new NoSuchElementException(); - } - } - -} diff --git a/org.eclipse.jgit/src/org/eclipse/jgit/lib/PackIndexWriter.java b/org.eclipse.jgit/src/org/eclipse/jgit/lib/PackIndexWriter.java deleted file mode 100644 index 4d2714bc55..0000000000 --- a/org.eclipse.jgit/src/org/eclipse/jgit/lib/PackIndexWriter.java +++ /dev/null @@ -1,275 +0,0 @@ -/* - * Copyright (C) 2008, Robin Rosenberg - * Copyright (C) 2008, Shawn O. Pearce - * 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.BufferedOutputStream; -import java.io.IOException; -import java.io.OutputStream; -import java.security.DigestOutputStream; -import java.text.MessageFormat; -import java.util.List; - -import org.eclipse.jgit.JGitText; -import org.eclipse.jgit.transport.PackedObjectInfo; -import org.eclipse.jgit.util.NB; - -/** - * Creates a table of contents to support random access by {@link PackFile}. - *

- * Pack index files (the .idx suffix in a pack file pair) - * provides random access to any object in the pack by associating an ObjectId - * to the byte offset within the pack where the object's data can be read. - */ -public abstract class PackIndexWriter { - /** Magic constant indicating post-version 1 format. */ - protected static final byte[] TOC = { -1, 't', 'O', 'c' }; - - /** - * Create a new writer for the oldest (most widely understood) format. - *

- * This method selects an index format that can accurate describe the - * supplied objects and that will be the most compatible format with older - * Git implementations. - *

- * Index version 1 is widely recognized by all Git implementations, but - * index version 2 (and later) is not as well recognized as it was - * introduced more than a year later. Index version 1 can only be used if - * the resulting pack file is under 4 gigabytes in size; packs larger than - * that limit must use index version 2. - * - * @param dst - * the stream the index data will be written to. If not already - * buffered it will be automatically wrapped in a buffered - * stream. Callers are always responsible for closing the stream. - * @param objs - * the objects the caller needs to store in the index. Entries - * will be examined until a format can be conclusively selected. - * @return a new writer to output an index file of the requested format to - * the supplied stream. - * @throws IllegalArgumentException - * no recognized pack index version can support the supplied - * objects. This is likely a bug in the implementation. - */ - @SuppressWarnings("fallthrough") - public static PackIndexWriter createOldestPossible(final OutputStream dst, - final List objs) { - int version = 1; - LOOP: for (final PackedObjectInfo oe : objs) { - switch (version) { - case 1: - if (PackIndexWriterV1.canStore(oe)) - continue; - version = 2; - case 2: - break LOOP; - } - } - return createVersion(dst, version); - } - - /** - * Create a new writer instance for a specific index format version. - * - * @param dst - * the stream the index data will be written to. If not already - * buffered it will be automatically wrapped in a buffered - * stream. Callers are always responsible for closing the stream. - * @param version - * index format version number required by the caller. Exactly - * this formatted version will be written. - * @return a new writer to output an index file of the requested format to - * the supplied stream. - * @throws IllegalArgumentException - * the version requested is not supported by this - * implementation. - */ - public static PackIndexWriter createVersion(final OutputStream dst, - final int version) { - switch (version) { - case 1: - return new PackIndexWriterV1(dst); - case 2: - return new PackIndexWriterV2(dst); - default: - throw new IllegalArgumentException(MessageFormat.format( - JGitText.get().unsupportedPackIndexVersion, version)); - } - } - - /** The index data stream we are responsible for creating. */ - protected final DigestOutputStream out; - - /** A temporary buffer for use during IO to {link #out}. */ - protected final byte[] tmp; - - /** The entries this writer must pack. */ - protected List entries; - - /** SHA-1 checksum for the entire pack data. */ - protected byte[] packChecksum; - - /** - * Create a new writer instance. - * - * @param dst - * the stream this instance outputs to. If not already buffered - * it will be automatically wrapped in a buffered stream. - */ - protected PackIndexWriter(final OutputStream dst) { - out = new DigestOutputStream(dst instanceof BufferedOutputStream ? dst - : new BufferedOutputStream(dst), Constants.newMessageDigest()); - tmp = new byte[4 + Constants.OBJECT_ID_LENGTH]; - } - - /** - * Write all object entries to the index stream. - *

- * After writing the stream passed to the factory is flushed but remains - * open. Callers are always responsible for closing the output stream. - * - * @param toStore - * sorted list of objects to store in the index. The caller must - * have previously sorted the list using {@link PackedObjectInfo}'s - * native {@link Comparable} implementation. - * @param packDataChecksum - * checksum signature of the entire pack data content. This is - * traditionally the last 20 bytes of the pack file's own stream. - * @throws IOException - * an error occurred while writing to the output stream, or this - * index format cannot store the object data supplied. - */ - public void write(final List toStore, - final byte[] packDataChecksum) throws IOException { - entries = toStore; - packChecksum = packDataChecksum; - writeImpl(); - out.flush(); - } - - /** - * Writes the index file to {@link #out}. - *

- * Implementations should go something like: - * - *

-	 * writeFanOutTable();
-	 * for (final PackedObjectInfo po : entries)
-	 * 	writeOneEntry(po);
-	 * writeChecksumFooter();
-	 * 
- * - *

- * Where the logic for writeOneEntry is specific to the index - * format in use. Additional headers/footers may be used if necessary and - * the {@link #entries} collection may be iterated over more than once if - * necessary. Implementors therefore have complete control over the data. - * - * @throws IOException - * an error occurred while writing to the output stream, or this - * index format cannot store the object data supplied. - */ - protected abstract void writeImpl() throws IOException; - - /** - * Output the version 2 (and later) TOC header, with version number. - *

- * Post version 1 all index files start with a TOC header that makes the - * file an invalid version 1 file, and then includes the version number. - * This header is necessary to recognize a version 1 from a version 2 - * formatted index. - * - * @param version - * version number of this index format being written. - * @throws IOException - * an error occurred while writing to the output stream. - */ - protected void writeTOC(final int version) throws IOException { - out.write(TOC); - NB.encodeInt32(tmp, 0, version); - out.write(tmp, 0, 4); - } - - /** - * Output the standard 256 entry first-level fan-out table. - *

- * The fan-out table is 4 KB in size, holding 256 32-bit unsigned integer - * counts. Each count represents the number of objects within this index - * whose {@link ObjectId#getFirstByte()} matches the count's position in the - * fan-out table. - * - * @throws IOException - * an error occurred while writing to the output stream. - */ - protected void writeFanOutTable() throws IOException { - final int[] fanout = new int[256]; - for (final PackedObjectInfo po : entries) - fanout[po.getFirstByte() & 0xff]++; - for (int i = 1; i < 256; i++) - fanout[i] += fanout[i - 1]; - for (final int n : fanout) { - NB.encodeInt32(tmp, 0, n); - out.write(tmp, 0, 4); - } - } - - /** - * Output the standard two-checksum index footer. - *

- * The standard footer contains two checksums (20 byte SHA-1 values): - *

    - *
  1. Pack data checksum - taken from the last 20 bytes of the pack file.
  2. - *
  3. Index data checksum - checksum of all index bytes written, including - * the pack data checksum above.
  4. - *
- * - * @throws IOException - * an error occurred while writing to the output stream. - */ - protected void writeChecksumFooter() throws IOException { - out.write(packChecksum); - out.on(false); - out.write(out.getMessageDigest().digest()); - } -} diff --git a/org.eclipse.jgit/src/org/eclipse/jgit/lib/PackIndexWriterV1.java b/org.eclipse.jgit/src/org/eclipse/jgit/lib/PackIndexWriterV1.java deleted file mode 100644 index eb44b3a8c7..0000000000 --- a/org.eclipse.jgit/src/org/eclipse/jgit/lib/PackIndexWriterV1.java +++ /dev/null @@ -1,85 +0,0 @@ -/* - * Copyright (C) 2008, Robin Rosenberg - * Copyright (C) 2008, Shawn O. Pearce - * 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 org.eclipse.jgit.JGitText; -import org.eclipse.jgit.transport.PackedObjectInfo; -import org.eclipse.jgit.util.NB; - -/** - * Creates the version 1 (old style) pack table of contents files. - * - * @see PackIndexWriter - * @see PackIndexV1 - */ -class PackIndexWriterV1 extends PackIndexWriter { - static boolean canStore(final PackedObjectInfo oe) { - // We are limited to 4 GB per pack as offset is 32 bit unsigned int. - // - return oe.getOffset() >>> 1 < Integer.MAX_VALUE; - } - - PackIndexWriterV1(final OutputStream dst) { - super(dst); - } - - @Override - protected void writeImpl() throws IOException { - writeFanOutTable(); - - for (final PackedObjectInfo oe : entries) { - if (!canStore(oe)) - throw new IOException(JGitText.get().packTooLargeForIndexVersion1); - NB.encodeInt32(tmp, 0, (int) oe.getOffset()); - oe.copyRawTo(tmp, 4); - out.write(tmp); - } - - writeChecksumFooter(); - } -} diff --git a/org.eclipse.jgit/src/org/eclipse/jgit/lib/PackIndexWriterV2.java b/org.eclipse.jgit/src/org/eclipse/jgit/lib/PackIndexWriterV2.java deleted file mode 100644 index b6ac7b89e3..0000000000 --- a/org.eclipse.jgit/src/org/eclipse/jgit/lib/PackIndexWriterV2.java +++ /dev/null @@ -1,107 +0,0 @@ -/* - * Copyright (C) 2008, Shawn O. Pearce - * 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 org.eclipse.jgit.transport.PackedObjectInfo; -import org.eclipse.jgit.util.NB; - -/** - * Creates the version 2 pack table of contents files. - * - * @see PackIndexWriter - * @see PackIndexV2 - */ -class PackIndexWriterV2 extends PackIndexWriter { - PackIndexWriterV2(final OutputStream dst) { - super(dst); - } - - @Override - protected void writeImpl() throws IOException { - writeTOC(2); - writeFanOutTable(); - writeObjectNames(); - writeCRCs(); - writeOffset32(); - writeOffset64(); - writeChecksumFooter(); - } - - private void writeObjectNames() throws IOException { - for (final PackedObjectInfo oe : entries) - oe.copyRawTo(out); - } - - private void writeCRCs() throws IOException { - for (final PackedObjectInfo oe : entries) { - NB.encodeInt32(tmp, 0, oe.getCRC()); - out.write(tmp, 0, 4); - } - } - - private void writeOffset32() throws IOException { - int o64 = 0; - for (final PackedObjectInfo oe : entries) { - final long o = oe.getOffset(); - if (o < Integer.MAX_VALUE) - NB.encodeInt32(tmp, 0, (int) o); - else - NB.encodeInt32(tmp, 0, (1 << 31) | o64++); - out.write(tmp, 0, 4); - } - } - - private void writeOffset64() throws IOException { - for (final PackedObjectInfo oe : entries) { - final long o = oe.getOffset(); - if (o > Integer.MAX_VALUE) { - NB.encodeInt64(tmp, 0, o); - out.write(tmp, 0, 8); - } - } - } -} diff --git a/org.eclipse.jgit/src/org/eclipse/jgit/lib/PackLock.java b/org.eclipse.jgit/src/org/eclipse/jgit/lib/PackLock.java deleted file mode 100644 index de8e3fa637..0000000000 --- a/org.eclipse.jgit/src/org/eclipse/jgit/lib/PackLock.java +++ /dev/null @@ -1,90 +0,0 @@ -/* - * 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.File; -import java.io.IOException; - -/** Keeps track of a {@link PackFile}'s associated .keep file. */ -public class PackLock { - private final File keepFile; - - /** - * Create a new lock for a pack file. - * - * @param packFile - * location of the pack-*.pack file. - */ - public PackLock(final File packFile) { - final File p = packFile.getParentFile(); - final String n = packFile.getName(); - keepFile = new File(p, n.substring(0, n.length() - 5) + ".keep"); - } - - /** - * Create the pack-*.keep file, with the given message. - * - * @param msg - * message to store in the file. - * @return true if the keep file was successfully written; false otherwise. - * @throws IOException - * the keep file could not be written. - */ - public boolean lock(String msg) throws IOException { - if (msg == null) - return false; - if (!msg.endsWith("\n")) - msg += "\n"; - final LockFile lf = new LockFile(keepFile); - if (!lf.lock()) - return false; - lf.write(Constants.encode(msg)); - return lf.commit(); - } - - /** Remove the .keep file that holds this pack in place. */ - public void unlock() { - keepFile.delete(); - } -} diff --git a/org.eclipse.jgit/src/org/eclipse/jgit/lib/PackReverseIndex.java b/org.eclipse.jgit/src/org/eclipse/jgit/lib/PackReverseIndex.java deleted file mode 100644 index f4f57aed43..0000000000 --- a/org.eclipse.jgit/src/org/eclipse/jgit/lib/PackReverseIndex.java +++ /dev/null @@ -1,193 +0,0 @@ -/* - * Copyright (C) 2008, Marek Zawirski - * 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.text.MessageFormat; -import java.util.Arrays; - -import org.eclipse.jgit.JGitText; -import org.eclipse.jgit.errors.CorruptObjectException; -import org.eclipse.jgit.lib.PackIndex.MutableEntry; - -/** - *

- * Reverse index for forward pack index. Provides operations based on offset - * instead of object id. Such offset-based reverse lookups are performed in - * O(log n) time. - *

- * - * @see PackIndex - * @see PackFile - */ -class PackReverseIndex { - /** Index we were created from, and that has our ObjectId data. */ - private final PackIndex index; - - /** - * (offset31, truly) Offsets accommodating in 31 bits. - */ - private final int offsets32[]; - - /** - * Offsets not accommodating in 31 bits. - */ - private final long offsets64[]; - - /** Position of the corresponding {@link #offsets32} in {@link #index}. */ - private final int nth32[]; - - /** Position of the corresponding {@link #offsets64} in {@link #index}. */ - private final int nth64[]; - - /** - * Create reverse index from straight/forward pack index, by indexing all - * its entries. - * - * @param packIndex - * forward index - entries to (reverse) index. - */ - PackReverseIndex(final PackIndex packIndex) { - index = packIndex; - - final long cnt = index.getObjectCount(); - final long n64 = index.getOffset64Count(); - final long n32 = cnt - n64; - if (n32 > Integer.MAX_VALUE || n64 > Integer.MAX_VALUE - || cnt > 0xffffffffL) - throw new IllegalArgumentException( - JGitText.get().hugeIndexesAreNotSupportedByJgitYet); - - offsets32 = new int[(int) n32]; - offsets64 = new long[(int) n64]; - nth32 = new int[offsets32.length]; - nth64 = new int[offsets64.length]; - - int i32 = 0; - int i64 = 0; - for (final MutableEntry me : index) { - final long o = me.getOffset(); - if (o < Integer.MAX_VALUE) - offsets32[i32++] = (int) o; - else - offsets64[i64++] = o; - } - - Arrays.sort(offsets32); - Arrays.sort(offsets64); - - int nth = 0; - for (final MutableEntry me : index) { - final long o = me.getOffset(); - if (o < Integer.MAX_VALUE) - nth32[Arrays.binarySearch(offsets32, (int) o)] = nth++; - else - nth64[Arrays.binarySearch(offsets64, o)] = nth++; - } - } - - /** - * Search for object id with the specified start offset in this pack - * (reverse) index. - * - * @param offset - * start offset of object to find. - * @return object id for this offset, or null if no object was found. - */ - ObjectId findObject(final long offset) { - if (offset <= Integer.MAX_VALUE) { - final int i32 = Arrays.binarySearch(offsets32, (int) offset); - if (i32 < 0) - return null; - return index.getObjectId(nth32[i32]); - } else { - final int i64 = Arrays.binarySearch(offsets64, offset); - if (i64 < 0) - return null; - return index.getObjectId(nth64[i64]); - } - } - - /** - * Search for the next offset to the specified offset in this pack (reverse) - * index. - * - * @param offset - * start offset of previous object (must be valid-existing - * offset). - * @param maxOffset - * maximum offset in a pack (returned when there is no next - * offset). - * @return offset of the next object in a pack or maxOffset if provided - * offset was the last one. - * @throws CorruptObjectException - * when there is no object with the provided offset. - */ - long findNextOffset(final long offset, final long maxOffset) - throws CorruptObjectException { - if (offset <= Integer.MAX_VALUE) { - final int i32 = Arrays.binarySearch(offsets32, (int) offset); - if (i32 < 0) - throw new CorruptObjectException(MessageFormat.format( - JGitText.get().cantFindObjectInReversePackIndexForTheSpecifiedOffset - , offset)); - - if (i32 + 1 == offsets32.length) { - if (offsets64.length > 0) - return offsets64[0]; - return maxOffset; - } - return offsets32[i32 + 1]; - } else { - final int i64 = Arrays.binarySearch(offsets64, offset); - if (i64 < 0) - throw new CorruptObjectException(MessageFormat.format( - JGitText.get().cantFindObjectInReversePackIndexForTheSpecifiedOffset - , offset)); - - if (i64 + 1 == offsets64.length) - return maxOffset; - return offsets64[i64 + 1]; - } - } -} diff --git a/org.eclipse.jgit/src/org/eclipse/jgit/lib/PackWriter.java b/org.eclipse.jgit/src/org/eclipse/jgit/lib/PackWriter.java index 80d8fff536..a6f6b25078 100644 --- a/org.eclipse.jgit/src/org/eclipse/jgit/lib/PackWriter.java +++ b/org.eclipse.jgit/src/org/eclipse/jgit/lib/PackWriter.java @@ -66,6 +66,7 @@ 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; /** *

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 47f5e67a73..0000000000 --- a/org.eclipse.jgit/src/org/eclipse/jgit/lib/PackedObjectLoader.java +++ /dev/null @@ -1,124 +0,0 @@ -/* - * Copyright (C) 2009, Google Inc. - * Copyright (C) 2008, Marek Zawirski - * Copyright (C) 2007, Robin Rosenberg - * Copyright (C) 2006-2008, Shawn O. Pearce - * 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; - -/** - * 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. - *

- * 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. - *

    - *
  • {@link #getType()}
  • - *
  • {@link #getSize()}
  • - *
  • {@link #getBytes()}, {@link #getCachedBytes}
  • - *
  • {@link #getRawSize()}
  • - *
  • {@link #getRawType()}
  • - *
- * - * @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; - } - - /** - * @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/RefDirectory.java b/org.eclipse.jgit/src/org/eclipse/jgit/lib/RefDirectory.java deleted file mode 100644 index 13e9c22d98..0000000000 --- a/org.eclipse.jgit/src/org/eclipse/jgit/lib/RefDirectory.java +++ /dev/null @@ -1,1006 +0,0 @@ -/* - * Copyright (C) 2007, Dave Watson - * Copyright (C) 2009-2010, Google Inc. - * Copyright (C) 2007, Robin Rosenberg - * Copyright (C) 2006, Shawn O. Pearce - * 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 static org.eclipse.jgit.lib.Constants.CHARSET; -import static org.eclipse.jgit.lib.Constants.HEAD; -import static org.eclipse.jgit.lib.Constants.LOGS; -import static org.eclipse.jgit.lib.Constants.OBJECT_ID_STRING_LENGTH; -import static org.eclipse.jgit.lib.Constants.PACKED_REFS; -import static org.eclipse.jgit.lib.Constants.R_HEADS; -import static org.eclipse.jgit.lib.Constants.R_REFS; -import static org.eclipse.jgit.lib.Constants.R_REMOTES; -import static org.eclipse.jgit.lib.Constants.R_TAGS; -import static org.eclipse.jgit.lib.Constants.encode; -import static org.eclipse.jgit.lib.Ref.Storage.LOOSE; -import static org.eclipse.jgit.lib.Ref.Storage.NEW; -import static org.eclipse.jgit.lib.Ref.Storage.PACKED; - -import java.io.BufferedReader; -import java.io.File; -import java.io.FileInputStream; -import java.io.FileNotFoundException; -import java.io.FileOutputStream; -import java.io.IOException; -import java.io.InputStreamReader; -import java.text.MessageFormat; -import java.util.Arrays; -import java.util.Map; -import java.util.concurrent.atomic.AtomicInteger; -import java.util.concurrent.atomic.AtomicReference; - -import org.eclipse.jgit.JGitText; -import org.eclipse.jgit.errors.ObjectWritingException; -import org.eclipse.jgit.events.RefsChangedEvent; -import org.eclipse.jgit.revwalk.RevObject; -import org.eclipse.jgit.revwalk.RevTag; -import org.eclipse.jgit.revwalk.RevWalk; -import org.eclipse.jgit.util.FS; -import org.eclipse.jgit.util.IO; -import org.eclipse.jgit.util.RawParseUtils; -import org.eclipse.jgit.util.RefList; -import org.eclipse.jgit.util.RefMap; - -/** - * Traditional file system based {@link RefDatabase}. - *

- * This is the classical reference database representation for a Git repository. - * References are stored in two formats: loose, and packed. - *

- * Loose references are stored as individual files within the {@code refs/} - * directory. The file name matches the reference name and the file contents is - * the current {@link ObjectId} in string form. - *

- * Packed references are stored in a single text file named {@code packed-refs}. - * In the packed format, each reference is stored on its own line. This file - * reduces the number of files needed for large reference spaces, reducing the - * overall size of a Git repository on disk. - */ -public class RefDirectory extends RefDatabase { - /** Magic string denoting the start of a symbolic reference file. */ - public static final String SYMREF = "ref: "; //$NON-NLS-1$ - - /** Magic string denoting the header of a packed-refs file. */ - public static final String PACKED_REFS_HEADER = "# pack-refs with:"; //$NON-NLS-1$ - - /** If in the header, denotes the file has peeled data. */ - public static final String PACKED_REFS_PEELED = " peeled"; //$NON-NLS-1$ - - private final FileRepository parent; - - private final File gitDir; - - private final File refsDir; - - private final File logsDir; - - private final File logsRefsDir; - - private final File packedRefsFile; - - /** - * Immutable sorted list of loose references. - *

- * Symbolic references in this collection are stored unresolved, that is - * their target appears to be a new reference with no ObjectId. These are - * converted into resolved references during a get operation, ensuring the - * live value is always returned. - */ - private final AtomicReference> looseRefs = new AtomicReference>(); - - /** Immutable sorted list of packed references. */ - private final AtomicReference packedRefs = new AtomicReference(); - - /** - * Number of modifications made to this database. - *

- * This counter is incremented when a change is made, or detected from the - * filesystem during a read operation. - */ - private final AtomicInteger modCnt = new AtomicInteger(); - - /** - * Last {@link #modCnt} that we sent to listeners. - *

- * This value is compared to {@link #modCnt}, and a notification is sent to - * the listeners only when it differs. - */ - private final AtomicInteger lastNotifiedModCnt = new AtomicInteger(); - - RefDirectory(final FileRepository db) { - final FS fs = db.getFS(); - parent = db; - gitDir = db.getDirectory(); - refsDir = fs.resolve(gitDir, R_REFS); - logsDir = fs.resolve(gitDir, LOGS); - logsRefsDir = fs.resolve(gitDir, LOGS + '/' + R_REFS); - packedRefsFile = fs.resolve(gitDir, PACKED_REFS); - - looseRefs.set(RefList. emptyList()); - packedRefs.set(PackedRefList.NO_PACKED_REFS); - } - - Repository getRepository() { - return parent; - } - - public void create() throws IOException { - refsDir.mkdir(); - logsDir.mkdir(); - logsRefsDir.mkdir(); - - new File(refsDir, R_HEADS.substring(R_REFS.length())).mkdir(); - new File(refsDir, R_TAGS.substring(R_REFS.length())).mkdir(); - new File(logsRefsDir, R_HEADS.substring(R_REFS.length())).mkdir(); - } - - @Override - public void close() { - // We have no resources to close. - } - - void rescan() { - looseRefs.set(RefList. emptyList()); - packedRefs.set(PackedRefList.NO_PACKED_REFS); - } - - @Override - public boolean isNameConflicting(String name) throws IOException { - RefList packed = getPackedRefs(); - RefList loose = getLooseRefs(); - - // Cannot be nested within an existing reference. - int lastSlash = name.lastIndexOf('/'); - while (0 < lastSlash) { - String needle = name.substring(0, lastSlash); - if (loose.contains(needle) || packed.contains(needle)) - return true; - lastSlash = name.lastIndexOf('/', lastSlash - 1); - } - - // Cannot be the container of an existing reference. - String prefix = name + '/'; - int idx; - - idx = -(packed.find(prefix) + 1); - if (idx < packed.size() && packed.get(idx).getName().startsWith(prefix)) - return true; - - idx = -(loose.find(prefix) + 1); - if (idx < loose.size() && loose.get(idx).getName().startsWith(prefix)) - return true; - - return false; - } - - private RefList getLooseRefs() { - final RefList oldLoose = looseRefs.get(); - - LooseScanner scan = new LooseScanner(oldLoose); - scan.scan(ALL); - - RefList loose; - if (scan.newLoose != null) { - loose = scan.newLoose.toRefList(); - if (looseRefs.compareAndSet(oldLoose, loose)) - modCnt.incrementAndGet(); - } else - loose = oldLoose; - return loose; - } - - @Override - public Ref getRef(final String needle) throws IOException { - final RefList packed = getPackedRefs(); - Ref ref = null; - for (String prefix : SEARCH_PATH) { - ref = readRef(prefix + needle, packed); - if (ref != null) { - ref = resolve(ref, 0, null, null, packed); - break; - } - } - fireRefsChanged(); - return ref; - } - - @Override - public Map getRefs(String prefix) throws IOException { - final RefList packed = getPackedRefs(); - final RefList oldLoose = looseRefs.get(); - - LooseScanner scan = new LooseScanner(oldLoose); - scan.scan(prefix); - - RefList loose; - if (scan.newLoose != null) { - loose = scan.newLoose.toRefList(); - if (looseRefs.compareAndSet(oldLoose, loose)) - modCnt.incrementAndGet(); - } else - loose = oldLoose; - fireRefsChanged(); - - RefList.Builder symbolic = scan.symbolic; - for (int idx = 0; idx < symbolic.size();) { - Ref ref = symbolic.get(idx); - ref = resolve(ref, 0, prefix, loose, packed); - if (ref != null && ref.getObjectId() != null) { - symbolic.set(idx, ref); - idx++; - } else { - // A broken symbolic reference, we have to drop it from the - // collections the client is about to receive. Should be a - // rare occurrence so pay a copy penalty. - loose = loose.remove(idx); - symbolic.remove(idx); - } - } - - return new RefMap(prefix, packed, upcast(loose), symbolic.toRefList()); - } - - @SuppressWarnings("unchecked") - private RefList upcast(RefList loose) { - return (RefList) loose; - } - - private class LooseScanner { - private final RefList curLoose; - - private int curIdx; - - final RefList.Builder symbolic = new RefList.Builder(4); - - RefList.Builder newLoose; - - LooseScanner(final RefList curLoose) { - this.curLoose = curLoose; - } - - void scan(String prefix) { - if (ALL.equals(prefix)) { - scanOne(HEAD); - scanTree(R_REFS, refsDir); - - // If any entries remain, they are deleted, drop them. - if (newLoose == null && curIdx < curLoose.size()) - newLoose = curLoose.copy(curIdx); - - } else if (prefix.startsWith(R_REFS) && prefix.endsWith("/")) { - curIdx = -(curLoose.find(prefix) + 1); - File dir = new File(refsDir, prefix.substring(R_REFS.length())); - scanTree(prefix, dir); - - // Skip over entries still within the prefix; these have - // been removed from the directory. - while (curIdx < curLoose.size()) { - if (!curLoose.get(curIdx).getName().startsWith(prefix)) - break; - if (newLoose == null) - newLoose = curLoose.copy(curIdx); - curIdx++; - } - - // Keep any entries outside of the prefix space, we - // do not know anything about their status. - if (newLoose != null) { - while (curIdx < curLoose.size()) - newLoose.add(curLoose.get(curIdx++)); - } - } - } - - private boolean scanTree(String prefix, File dir) { - final String[] entries = dir.list(LockFile.FILTER); - if (entries == null) // not a directory or an I/O error - return false; - if (0 < entries.length) { - Arrays.sort(entries); - for (String name : entries) { - File e = new File(dir, name); - if (!scanTree(prefix + name + '/', e)) - scanOne(prefix + name); - } - } - return true; - } - - private void scanOne(String name) { - LooseRef cur; - - if (curIdx < curLoose.size()) { - do { - cur = curLoose.get(curIdx); - int cmp = RefComparator.compareTo(cur, name); - if (cmp < 0) { - // Reference is not loose anymore, its been deleted. - // Skip the name in the new result list. - if (newLoose == null) - newLoose = curLoose.copy(curIdx); - curIdx++; - cur = null; - continue; - } - - if (cmp > 0) // Newly discovered loose reference. - cur = null; - break; - } while (curIdx < curLoose.size()); - } else - cur = null; // Newly discovered loose reference. - - LooseRef n; - try { - n = scanRef(cur, name); - } catch (IOException notValid) { - n = null; - } - - if (n != null) { - if (cur != n && newLoose == null) - newLoose = curLoose.copy(curIdx); - if (newLoose != null) - newLoose.add(n); - if (n.isSymbolic()) - symbolic.add(n); - } else if (cur != null) { - // Tragically, this file is no longer a loose reference. - // Kill our cached entry of it. - if (newLoose == null) - newLoose = curLoose.copy(curIdx); - } - - if (cur != null) - curIdx++; - } - } - - @Override - public Ref peel(final Ref ref) throws IOException { - final Ref leaf = ref.getLeaf(); - 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) { - newLeaf = new ObjectIdRef.PeeledTag(leaf.getStorage(), leaf - .getName(), leaf.getObjectId(), rw.peel(obj).copy()); - } else { - newLeaf = new ObjectIdRef.PeeledNonTag(leaf.getStorage(), leaf - .getName(), leaf.getObjectId()); - } - - // 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. - if (leaf.getStorage().isLoose()) { - RefList curList = looseRefs.get(); - int idx = curList.find(leaf.getName()); - if (0 <= idx && curList.get(idx) == leaf) { - LooseRef asPeeled = ((LooseRef) leaf).peel(newLeaf); - RefList newList = curList.set(idx, asPeeled); - looseRefs.compareAndSet(curList, newList); - } - } - - return recreate(ref, newLeaf); - } - - private static Ref recreate(final Ref old, final ObjectIdRef leaf) { - if (old.isSymbolic()) { - Ref dst = recreate(old.getTarget(), leaf); - return new SymbolicRef(old.getName(), dst); - } - return leaf; - } - - void storedSymbolicRef(RefDirectoryUpdate u, long modified, String target) { - putLooseRef(newSymbolicRef(modified, u.getRef().getName(), target)); - fireRefsChanged(); - } - - public RefDirectoryUpdate newUpdate(String name, boolean detach) - throws IOException { - final RefList packed = getPackedRefs(); - Ref ref = readRef(name, packed); - if (ref != null) - ref = resolve(ref, 0, null, null, packed); - if (ref == null) - ref = new ObjectIdRef.Unpeeled(NEW, name, null); - else if (detach && ref.isSymbolic()) - ref = new ObjectIdRef.Unpeeled(LOOSE, name, ref.getObjectId()); - return new RefDirectoryUpdate(this, ref); - } - - @Override - public RefDirectoryRename newRename(String fromName, String toName) - throws IOException { - RefDirectoryUpdate from = newUpdate(fromName, false); - RefDirectoryUpdate to = newUpdate(toName, false); - return new RefDirectoryRename(from, to); - } - - void stored(RefDirectoryUpdate update, long modified) { - final ObjectId target = update.getNewObjectId().copy(); - final Ref leaf = update.getRef().getLeaf(); - putLooseRef(new LooseUnpeeled(modified, leaf.getName(), target)); - } - - private void putLooseRef(LooseRef ref) { - RefList cList, nList; - do { - cList = looseRefs.get(); - nList = cList.put(ref); - } while (!looseRefs.compareAndSet(cList, nList)); - modCnt.incrementAndGet(); - fireRefsChanged(); - } - - void delete(RefDirectoryUpdate update) throws IOException { - Ref dst = update.getRef().getLeaf(); - String name = dst.getName(); - - // Write the packed-refs file using an atomic update. We might - // wind up reading it twice, before and after the lock, to ensure - // we don't miss an edit made externally. - final PackedRefList packed = getPackedRefs(); - if (packed.contains(name)) { - LockFile lck = new LockFile(packedRefsFile); - if (!lck.lock()) - throw new IOException(MessageFormat.format( - JGitText.get().cannotLockFile, packedRefsFile)); - try { - PackedRefList cur = readPackedRefs(0, 0); - int idx = cur.find(name); - if (0 <= idx) - commitPackedRefs(lck, cur.remove(idx), packed); - } finally { - lck.unlock(); - } - } - - RefList curLoose, newLoose; - do { - curLoose = looseRefs.get(); - int idx = curLoose.find(name); - if (idx < 0) - break; - newLoose = curLoose.remove(idx); - } while (!looseRefs.compareAndSet(curLoose, newLoose)); - - int levels = levelsIn(name) - 2; - delete(logFor(name), levels); - if (dst.getStorage().isLoose()) { - update.unlock(); - delete(fileFor(name), levels); - } - - modCnt.incrementAndGet(); - fireRefsChanged(); - } - - void log(final RefUpdate update, final String msg, final boolean deref) - throws IOException { - final ObjectId oldId = update.getOldObjectId(); - final ObjectId newId = update.getNewObjectId(); - final Ref ref = update.getRef(); - - PersonIdent ident = update.getRefLogIdent(); - if (ident == null) - ident = new PersonIdent(parent); - else - ident = new PersonIdent(ident); - - final StringBuilder r = new StringBuilder(); - r.append(ObjectId.toString(oldId)); - r.append(' '); - r.append(ObjectId.toString(newId)); - r.append(' '); - r.append(ident.toExternalString()); - r.append('\t'); - r.append(msg); - r.append('\n'); - final byte[] rec = encode(r.toString()); - - if (deref && ref.isSymbolic()) { - log(ref.getName(), rec); - log(ref.getLeaf().getName(), rec); - } else { - log(ref.getName(), rec); - } - } - - private void log(final String refName, final byte[] rec) throws IOException { - final File log = logFor(refName); - final boolean write; - if (isLogAllRefUpdates() && shouldAutoCreateLog(refName)) - write = true; - else if (log.isFile()) - write = true; - else - write = false; - - if (write) { - FileOutputStream out; - try { - out = new FileOutputStream(log, true); - } catch (FileNotFoundException err) { - final File dir = log.getParentFile(); - if (dir.exists()) - throw err; - if (!dir.mkdirs() && !dir.isDirectory()) - throw new IOException(MessageFormat.format(JGitText.get().cannotCreateDirectory, dir)); - out = new FileOutputStream(log, true); - } - try { - out.write(rec); - } finally { - out.close(); - } - } - } - - private boolean isLogAllRefUpdates() { - return parent.getConfig().get(CoreConfig.KEY).isLogAllRefUpdates(); - } - - private boolean shouldAutoCreateLog(final String refName) { - return refName.equals(HEAD) // - || refName.startsWith(R_HEADS) // - || refName.startsWith(R_REMOTES); - } - - private Ref resolve(final Ref ref, int depth, String prefix, - RefList loose, RefList packed) throws IOException { - if (ref.isSymbolic()) { - Ref dst = ref.getTarget(); - - if (MAX_SYMBOLIC_REF_DEPTH <= depth) - return null; // claim it doesn't exist - - // If the cached value can be assumed to be current due to a - // recent scan of the loose directory, use it. - if (loose != null && dst.getName().startsWith(prefix)) { - int idx; - if (0 <= (idx = loose.find(dst.getName()))) - dst = loose.get(idx); - else if (0 <= (idx = packed.find(dst.getName()))) - dst = packed.get(idx); - else - return ref; - } else { - dst = readRef(dst.getName(), packed); - if (dst == null) - return ref; - } - - dst = resolve(dst, depth + 1, prefix, loose, packed); - if (dst == null) - return null; - return new SymbolicRef(ref.getName(), dst); - } - return ref; - } - - private PackedRefList getPackedRefs() throws IOException { - long size = packedRefsFile.length(); - long mtime = size != 0 ? packedRefsFile.lastModified() : 0; - - final PackedRefList curList = packedRefs.get(); - if (size == curList.lastSize && mtime == curList.lastModified) - return curList; - - final PackedRefList newList = readPackedRefs(size, mtime); - if (packedRefs.compareAndSet(curList, newList)) - modCnt.incrementAndGet(); - return newList; - } - - private PackedRefList readPackedRefs(long size, long mtime) - throws IOException { - final BufferedReader br; - try { - br = new BufferedReader(new InputStreamReader(new FileInputStream( - packedRefsFile), CHARSET)); - } catch (FileNotFoundException noPackedRefs) { - // Ignore it and leave the new list empty. - return PackedRefList.NO_PACKED_REFS; - } - try { - return new PackedRefList(parsePackedRefs(br), size, mtime); - } finally { - br.close(); - } - } - - private RefList parsePackedRefs(final BufferedReader br) - throws IOException { - RefList.Builder all = new RefList.Builder(); - Ref last = null; - boolean peeled = false; - boolean needSort = false; - - String p; - while ((p = br.readLine()) != null) { - if (p.charAt(0) == '#') { - if (p.startsWith(PACKED_REFS_HEADER)) { - p = p.substring(PACKED_REFS_HEADER.length()); - peeled = p.contains(PACKED_REFS_PEELED); - } - continue; - } - - if (p.charAt(0) == '^') { - if (last == null) - throw new IOException(JGitText.get().peeledLineBeforeRef); - - ObjectId id = ObjectId.fromString(p.substring(1)); - last = new ObjectIdRef.PeeledTag(PACKED, last.getName(), last - .getObjectId(), id); - all.set(all.size() - 1, last); - continue; - } - - int sp = p.indexOf(' '); - ObjectId id = ObjectId.fromString(p.substring(0, sp)); - String name = copy(p, sp + 1, p.length()); - ObjectIdRef cur; - if (peeled) - cur = new ObjectIdRef.PeeledNonTag(PACKED, name, id); - else - cur = new ObjectIdRef.Unpeeled(PACKED, name, id); - if (last != null && RefComparator.compareTo(last, cur) > 0) - needSort = true; - all.add(cur); - last = cur; - } - - if (needSort) - all.sort(); - return all.toRefList(); - } - - private static String copy(final String src, final int off, final int end) { - // Don't use substring since it could leave a reference to the much - // larger existing string. Force construction of a full new object. - return new StringBuilder(end - off).append(src, off, end).toString(); - } - - private void commitPackedRefs(final LockFile lck, final RefList refs, - final PackedRefList oldPackedList) throws IOException { - new RefWriter(refs) { - @Override - protected void writeFile(String name, byte[] content) - throws IOException { - lck.setNeedStatInformation(true); - try { - lck.write(content); - } catch (IOException ioe) { - throw new ObjectWritingException(MessageFormat.format(JGitText.get().unableToWrite, name), ioe); - } - try { - lck.waitForStatChange(); - } catch (InterruptedException e) { - lck.unlock(); - throw new ObjectWritingException(MessageFormat.format(JGitText.get().interruptedWriting, name)); - } - if (!lck.commit()) - throw new ObjectWritingException(MessageFormat.format(JGitText.get().unableToWrite, name)); - - packedRefs.compareAndSet(oldPackedList, new PackedRefList(refs, - content.length, lck.getCommitLastModified())); - } - }.writePackedRefs(); - } - - private Ref readRef(String name, RefList packed) throws IOException { - final RefList curList = looseRefs.get(); - final int idx = curList.find(name); - if (0 <= idx) { - final LooseRef o = curList.get(idx); - final LooseRef n = scanRef(o, name); - if (n == null) { - if (looseRefs.compareAndSet(curList, curList.remove(idx))) - modCnt.incrementAndGet(); - return packed.get(name); - } - - if (o == n) - return n; - if (looseRefs.compareAndSet(curList, curList.set(idx, n))) - modCnt.incrementAndGet(); - return n; - } - - final LooseRef n = scanRef(null, name); - if (n == null) - return packed.get(name); - if (looseRefs.compareAndSet(curList, curList.add(idx, n))) - modCnt.incrementAndGet(); - return n; - } - - private LooseRef scanRef(LooseRef ref, String name) throws IOException { - final File path = fileFor(name); - final long modified = path.lastModified(); - - if (ref != null) { - if (ref.getLastModified() == modified) - return ref; - name = ref.getName(); - } else if (modified == 0) - return null; - - final byte[] buf; - try { - buf = IO.readFully(path, 4096); - } catch (FileNotFoundException noFile) { - return null; // doesn't exist; not a reference. - } - - int n = buf.length; - if (n == 0) - return null; // empty file; not a reference. - - if (isSymRef(buf, n)) { - // trim trailing whitespace - while (0 < n && Character.isWhitespace(buf[n - 1])) - n--; - if (n < 6) { - String content = RawParseUtils.decode(buf, 0, n); - throw new IOException(MessageFormat.format(JGitText.get().notARef, name, content)); - } - final String target = RawParseUtils.decode(buf, 5, n); - return newSymbolicRef(modified, name, target); - } - - if (n < OBJECT_ID_STRING_LENGTH) - return null; // impossibly short object identifier; not a reference. - - final ObjectId id; - try { - id = ObjectId.fromString(buf, 0); - } catch (IllegalArgumentException notRef) { - while (0 < n && Character.isWhitespace(buf[n - 1])) - n--; - String content = RawParseUtils.decode(buf, 0, n); - throw new IOException(MessageFormat.format(JGitText.get().notARef, name, content)); - } - return new LooseUnpeeled(modified, name, id); - } - - private static boolean isSymRef(final byte[] buf, int n) { - if (n < 6) - return false; - return /**/buf[0] == 'r' // - && buf[1] == 'e' // - && buf[2] == 'f' // - && buf[3] == ':' // - && buf[4] == ' '; - } - - /** If the parent should fire listeners, fires them. */ - private void fireRefsChanged() { - final int last = lastNotifiedModCnt.get(); - final int curr = modCnt.get(); - if (last != curr && lastNotifiedModCnt.compareAndSet(last, curr)) - parent.fireEvent(new RefsChangedEvent()); - } - - /** - * Create a reference update to write a temporary reference. - * - * @return an update for a new temporary reference. - * @throws IOException - * a temporary name cannot be allocated. - */ - RefDirectoryUpdate newTemporaryUpdate() throws IOException { - File tmp = File.createTempFile("renamed_", "_ref", refsDir); - String name = Constants.R_REFS + tmp.getName(); - Ref ref = new ObjectIdRef.Unpeeled(NEW, name, null); - return new RefDirectoryUpdate(this, ref); - } - - /** - * Locate the file on disk for a single reference name. - * - * @param name - * name of the ref, relative to the Git repository top level - * directory (so typically starts with refs/). - * @return the loose file location. - */ - File fileFor(String name) { - if (name.startsWith(R_REFS)) { - name = name.substring(R_REFS.length()); - return new File(refsDir, name); - } - return new File(gitDir, name); - } - - /** - * Locate the log file on disk for a single reference name. - * - * @param name - * name of the ref, relative to the Git repository top level - * directory (so typically starts with refs/). - * @return the log file location. - */ - File logFor(String name) { - if (name.startsWith(R_REFS)) { - name = name.substring(R_REFS.length()); - return new File(logsRefsDir, name); - } - return new File(logsDir, name); - } - - static int levelsIn(final String name) { - int count = 0; - for (int p = name.indexOf('/'); p >= 0; p = name.indexOf('/', p + 1)) - count++; - return count; - } - - static void delete(final File file, final int depth) throws IOException { - if (!file.delete() && file.isFile()) - throw new IOException(MessageFormat.format(JGitText.get().fileCannotBeDeleted, file)); - - File dir = file.getParentFile(); - for (int i = 0; i < depth; ++i) { - if (!dir.delete()) - break; // ignore problem here - dir = dir.getParentFile(); - } - } - - private static class PackedRefList extends RefList { - static final PackedRefList NO_PACKED_REFS = new PackedRefList(RefList - .emptyList(), 0, 0); - - /** Last length of the packed-refs file when we read it. */ - final long lastSize; - - /** Last modified time of the packed-refs file when we read it. */ - final long lastModified; - - PackedRefList(RefList src, long size, long mtime) { - super(src); - lastSize = size; - lastModified = mtime; - } - } - - private static LooseSymbolicRef newSymbolicRef(long lastModified, - String name, String target) { - Ref dst = new ObjectIdRef.Unpeeled(NEW, target, null); - return new LooseSymbolicRef(lastModified, name, dst); - } - - private static interface LooseRef extends Ref { - long getLastModified(); - - LooseRef peel(ObjectIdRef newLeaf); - } - - private final static class LoosePeeledTag extends ObjectIdRef.PeeledTag - implements LooseRef { - private final long lastModified; - - LoosePeeledTag(long mtime, String refName, ObjectId id, ObjectId p) { - super(LOOSE, refName, id, p); - this.lastModified = mtime; - } - - public long getLastModified() { - return lastModified; - } - - public LooseRef peel(ObjectIdRef newLeaf) { - return this; - } - } - - private final static class LooseNonTag extends ObjectIdRef.PeeledNonTag - implements LooseRef { - private final long lastModified; - - LooseNonTag(long mtime, String refName, ObjectId id) { - super(LOOSE, refName, id); - this.lastModified = mtime; - } - - public long getLastModified() { - return lastModified; - } - - public LooseRef peel(ObjectIdRef newLeaf) { - return this; - } - } - - private final static class LooseUnpeeled extends ObjectIdRef.Unpeeled - implements LooseRef { - private final long lastModified; - - LooseUnpeeled(long mtime, String refName, ObjectId id) { - super(LOOSE, refName, id); - this.lastModified = mtime; - } - - public long getLastModified() { - return lastModified; - } - - public LooseRef peel(ObjectIdRef newLeaf) { - if (newLeaf.getPeeledObjectId() != null) - return new LoosePeeledTag(lastModified, getName(), - getObjectId(), newLeaf.getPeeledObjectId()); - else - return new LooseNonTag(lastModified, getName(), getObjectId()); - } - } - - private final static class LooseSymbolicRef extends SymbolicRef implements - LooseRef { - private final long lastModified; - - LooseSymbolicRef(long mtime, String refName, Ref target) { - super(refName, target); - this.lastModified = mtime; - } - - public long getLastModified() { - return lastModified; - } - - public LooseRef peel(ObjectIdRef newLeaf) { - // We should never try to peel the symbolic references. - throw new UnsupportedOperationException(); - } - } -} diff --git a/org.eclipse.jgit/src/org/eclipse/jgit/lib/RefDirectoryRename.java b/org.eclipse.jgit/src/org/eclipse/jgit/lib/RefDirectoryRename.java deleted file mode 100644 index fec00d9325..0000000000 --- a/org.eclipse.jgit/src/org/eclipse/jgit/lib/RefDirectoryRename.java +++ /dev/null @@ -1,225 +0,0 @@ -/* - * Copyright (C) 2010, Google Inc. - * Copyright (C) 2009, Robin Rosenberg - * 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.IOException; - -import org.eclipse.jgit.lib.RefUpdate.Result; -import org.eclipse.jgit.revwalk.RevWalk; - -/** - * Rename any reference stored by {@link RefDirectory}. - *

- * This class works by first renaming the source reference to a temporary name, - * then renaming the temporary name to the final destination reference. - *

- * This strategy permits switching a reference like {@code refs/heads/foo}, - * which is a file, to {@code refs/heads/foo/bar}, which is stored inside a - * directory that happens to match the source name. - */ -class RefDirectoryRename extends RefRename { - private final RefDirectory refdb; - - /** - * The value of the source reference at the start of the rename. - *

- * At the end of the rename the destination reference must have this same - * value, otherwise we have a concurrent update and the rename must fail - * without making any changes. - */ - private ObjectId objId; - - /** True if HEAD must be moved to the destination reference. */ - private boolean updateHEAD; - - /** A reference we backup {@link #objId} into during the rename. */ - private RefDirectoryUpdate tmp; - - RefDirectoryRename(RefDirectoryUpdate src, RefDirectoryUpdate dst) { - super(src, dst); - refdb = src.getRefDatabase(); - } - - @Override - protected Result doRename() throws IOException { - 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(); - try { - // First backup the source so its never unreachable. - tmp.setNewObjectId(objId); - tmp.setForceUpdate(true); - tmp.disableRefLog(); - switch (tmp.update(rw)) { - case NEW: - case FORCED: - case NO_CHANGE: - break; - default: - return tmp.getResult(); - } - - // Save the source's log under the temporary name, we must do - // this before we delete the source, otherwise we lose the log. - if (!renameLog(source, tmp)) - return Result.IO_FAILURE; - - // If HEAD has to be updated, link it now to destination. - // We have to link before we delete, otherwise the delete - // fails because its the current branch. - RefUpdate dst = destination; - if (updateHEAD) { - if (!linkHEAD(destination)) { - renameLog(tmp, source); - return Result.LOCK_FAILURE; - } - - // Replace the update operation so HEAD will log the rename. - dst = refdb.newUpdate(Constants.HEAD, false); - dst.setRefLogIdent(destination.getRefLogIdent()); - dst.setRefLogMessage(destination.getRefLogMessage(), false); - } - - // Delete the source name so its path is free for replacement. - source.setExpectedOldObjectId(objId); - source.setForceUpdate(true); - source.disableRefLog(); - if (source.delete(rw) != Result.FORCED) { - renameLog(tmp, source); - if (updateHEAD) - linkHEAD(source); - return source.getResult(); - } - - // Move the log to the destination. - if (!renameLog(tmp, destination)) { - renameLog(tmp, source); - source.setExpectedOldObjectId(ObjectId.zeroId()); - source.setNewObjectId(objId); - source.update(rw); - if (updateHEAD) - linkHEAD(source); - return Result.IO_FAILURE; - } - - // Create the destination, logging the rename during the creation. - dst.setExpectedOldObjectId(ObjectId.zeroId()); - dst.setNewObjectId(objId); - if (dst.update(rw) != Result.NEW) { - // If we didn't create the destination we have to undo - // our work. Put the log back and restore source. - if (renameLog(destination, tmp)) - renameLog(tmp, source); - source.setExpectedOldObjectId(ObjectId.zeroId()); - source.setNewObjectId(objId); - source.update(rw); - if (updateHEAD) - linkHEAD(source); - return dst.getResult(); - } - - return Result.RENAMED; - } finally { - // Always try to free the temporary name. - try { - refdb.delete(tmp); - } catch (IOException err) { - refdb.fileFor(tmp.getName()).delete(); - } - } - } - - private boolean renameLog(RefUpdate src, RefUpdate dst) { - File srcLog = refdb.logFor(src.getName()); - File dstLog = refdb.logFor(dst.getName()); - - if (!srcLog.exists()) - return true; - - if (!rename(srcLog, dstLog)) - return false; - - try { - final int levels = RefDirectory.levelsIn(src.getName()) - 2; - RefDirectory.delete(srcLog, levels); - return true; - } catch (IOException e) { - rename(dstLog, srcLog); - return false; - } - } - - private static boolean rename(File src, File dst) { - if (src.renameTo(dst)) - return true; - - File dir = dst.getParentFile(); - if ((dir.exists() || !dir.mkdirs()) && !dir.isDirectory()) - return false; - return src.renameTo(dst); - } - - private boolean linkHEAD(RefUpdate target) { - try { - RefUpdate u = refdb.newUpdate(Constants.HEAD, false); - u.disableRefLog(); - switch (u.link(target.getName())) { - case NEW: - case FORCED: - case NO_CHANGE: - return true; - default: - return false; - } - } catch (IOException e) { - return false; - } - } -} diff --git a/org.eclipse.jgit/src/org/eclipse/jgit/lib/RefDirectoryUpdate.java b/org.eclipse.jgit/src/org/eclipse/jgit/lib/RefDirectoryUpdate.java deleted file mode 100644 index 447be104ab..0000000000 --- a/org.eclipse.jgit/src/org/eclipse/jgit/lib/RefDirectoryUpdate.java +++ /dev/null @@ -1,156 +0,0 @@ -/* - * Copyright (C) 2009-2010, Google Inc. - * Copyright (C) 2008, Shawn O. Pearce - * 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 static org.eclipse.jgit.lib.Constants.encode; - -import java.io.IOException; - -/** Updates any reference stored by {@link RefDirectory}. */ -class RefDirectoryUpdate extends RefUpdate { - private final RefDirectory database; - - private LockFile lock; - - RefDirectoryUpdate(final RefDirectory r, final Ref ref) { - super(ref); - database = r; - } - - @Override - protected RefDirectory getRefDatabase() { - return database; - } - - @Override - protected Repository getRepository() { - return database.getRepository(); - } - - @Override - protected boolean tryLock(boolean deref) throws IOException { - Ref dst = getRef(); - if (deref) - dst = dst.getLeaf(); - String name = dst.getName(); - lock = new LockFile(database.fileFor(name)); - if (lock.lock()) { - dst = database.getRef(name); - setOldObjectId(dst != null ? dst.getObjectId() : null); - return true; - } else { - return false; - } - } - - @Override - protected void unlock() { - if (lock != null) { - lock.unlock(); - lock = null; - } - } - - @Override - protected Result doUpdate(final Result status) throws IOException { - lock.setNeedStatInformation(true); - lock.write(getNewObjectId()); - - String msg = getRefLogMessage(); - if (msg != null) { - if (isRefLogIncludingResult()) { - String strResult = toResultString(status); - if (strResult != null) { - if (msg.length() > 0) - msg = msg + ": " + strResult; - else - msg = strResult; - } - } - database.log(this, msg, true); - } - if (!lock.commit()) - return Result.LOCK_FAILURE; - database.stored(this, lock.getCommitLastModified()); - return status; - } - - private String toResultString(final Result status) { - switch (status) { - case FORCED: - return "forced-update"; - case FAST_FORWARD: - return "fast forward"; - case NEW: - return "created"; - default: - return null; - } - } - - @Override - protected Result doDelete(final Result status) throws IOException { - if (getRef().getLeaf().getStorage() != Ref.Storage.NEW) - database.delete(this); - return status; - } - - @Override - protected Result doLink(final String target) throws IOException { - lock.setNeedStatInformation(true); - lock.write(encode(RefDirectory.SYMREF + target + '\n')); - - String msg = getRefLogMessage(); - if (msg != null) - database.log(this, msg, false); - if (!lock.commit()) - return Result.LOCK_FAILURE; - database.storedSymbolicRef(this, lock.getCommitLastModified(), target); - - if (getRef().getStorage() == Ref.Storage.NEW) - return Result.NEW; - return Result.FORCED; - } -} 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/ReflogReader.java b/org.eclipse.jgit/src/org/eclipse/jgit/lib/ReflogReader.java deleted file mode 100644 index 4c5503f321..0000000000 --- a/org.eclipse.jgit/src/org/eclipse/jgit/lib/ReflogReader.java +++ /dev/null @@ -1,184 +0,0 @@ -/* - * Copyright (C) 2009, Robin Rosenberg - * Copyright (C) 2009, Robin Rosenberg - * 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.ArrayList; -import java.util.Collections; -import java.util.List; - -import org.eclipse.jgit.JGitText; -import org.eclipse.jgit.util.IO; -import org.eclipse.jgit.util.RawParseUtils; - -/** - * Utility for reading reflog entries - */ -public class ReflogReader { - /** - * Parsed reflog entry - */ - static public class Entry { - private ObjectId oldId; - - private ObjectId newId; - - private PersonIdent who; - - private String comment; - - Entry(byte[] raw, int pos) { - oldId = ObjectId.fromString(raw, pos); - pos += Constants.OBJECT_ID_STRING_LENGTH; - if (raw[pos++] != ' ') - throw new IllegalArgumentException( - JGitText.get().rawLogMessageDoesNotParseAsLogEntry); - newId = ObjectId.fromString(raw, pos); - pos += Constants.OBJECT_ID_STRING_LENGTH; - if (raw[pos++] != ' ') { - throw new IllegalArgumentException( - JGitText.get().rawLogMessageDoesNotParseAsLogEntry); - } - who = RawParseUtils.parsePersonIdentOnly(raw, pos); - int p0 = RawParseUtils.next(raw, pos, '\t'); // personident has no - // \t - if (p0 == -1) { - throw new IllegalArgumentException( - JGitText.get().rawLogMessageDoesNotParseAsLogEntry); - } - int p1 = RawParseUtils.nextLF(raw, p0); - if (p1 == -1) { - throw new IllegalArgumentException( - JGitText.get().rawLogMessageDoesNotParseAsLogEntry); - } - comment = RawParseUtils.decode(raw, p0, p1 - 1); - } - - /** - * @return the commit id before the change - */ - public ObjectId getOldId() { - return oldId; - } - - /** - * @return the commit id after the change - */ - public ObjectId getNewId() { - return newId; - } - - /** - * @return user performin the change - */ - public PersonIdent getWho() { - return who; - } - - /** - * @return textual description of the change - */ - public String getComment() { - return comment; - } - - @Override - public String toString() { - return "Entry[" + oldId.name() + ", " + newId.name() + ", " + getWho() + ", " - + getComment() + "]"; - } - } - - private File logName; - - ReflogReader(Repository db, String refname) { - logName = new File(db.getDirectory(), "logs/" + refname); - } - - /** - * Get the last entry in the reflog - * - * @return the latest reflog entry, or null if no log - * @throws IOException - */ - public Entry getLastEntry() throws IOException { - List entries = getReverseEntries(1); - return entries.size() > 0 ? entries.get(0) : null; - } - - /** - * @return all reflog entries in reverse order - * @throws IOException - */ - public List getReverseEntries() throws IOException { - return getReverseEntries(Integer.MAX_VALUE); - } - - /** - * @param max - * max numer of entries to read - * @return all reflog entries in reverse order - * @throws IOException - */ - public List getReverseEntries(int max) throws IOException { - final byte[] log; - try { - log = IO.readFully(logName); - } catch (FileNotFoundException e) { - return Collections.emptyList(); - } - - int rs = RawParseUtils.prevLF(log, log.length); - List ret = new ArrayList(); - while (rs >= 0 && max-- > 0) { - rs = RawParseUtils.prevLF(log, rs); - Entry entry = new Entry(log, rs < 0 ? 0 : rs + 2); - ret.add(entry); - } - return ret; - } -} 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 6b91481d3e..65f75094ea 100644 --- a/org.eclipse.jgit/src/org/eclipse/jgit/lib/Repository.java +++ b/org.eclipse.jgit/src/org/eclipse/jgit/lib/Repository.java @@ -69,6 +69,7 @@ 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; diff --git a/org.eclipse.jgit/src/org/eclipse/jgit/lib/RepositoryBuilder.java b/org.eclipse.jgit/src/org/eclipse/jgit/lib/RepositoryBuilder.java index 4b0e1f6901..f9185e8f26 100644 --- a/org.eclipse.jgit/src/org/eclipse/jgit/lib/RepositoryBuilder.java +++ b/org.eclipse.jgit/src/org/eclipse/jgit/lib/RepositoryBuilder.java @@ -45,6 +45,8 @@ package org.eclipse.jgit.lib; import java.io.File; +import org.eclipse.jgit.storage.file.FileRepositoryBuilder; + /** * Base class to support constructing a {@link Repository}. *

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 39d734a32b..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; diff --git a/org.eclipse.jgit/src/org/eclipse/jgit/lib/UnpackedObjectCache.java b/org.eclipse.jgit/src/org/eclipse/jgit/lib/UnpackedObjectCache.java deleted file mode 100644 index 3cef48242d..0000000000 --- a/org.eclipse.jgit/src/org/eclipse/jgit/lib/UnpackedObjectCache.java +++ /dev/null @@ -1,195 +0,0 @@ -/* - * Copyright (C) 2008, Shawn O. Pearce - * 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.lang.ref.SoftReference; - -class UnpackedObjectCache { - private static final int CACHE_SZ = 1024; - - private static final SoftReference DEAD; - - private static int hash(final long position) { - return (((int) position) << 22) >>> 22; - } - - private static int maxByteCount; - - private static final Slot[] cache; - - private static Slot lruHead; - - private static Slot lruTail; - - private static int openByteCount; - - static { - DEAD = new SoftReference(null); - maxByteCount = new WindowCacheConfig().getDeltaBaseCacheLimit(); - - cache = new Slot[CACHE_SZ]; - for (int i = 0; i < CACHE_SZ; i++) - cache[i] = new Slot(); - } - - static synchronized void reconfigure(final WindowCacheConfig cfg) { - final int dbLimit = cfg.getDeltaBaseCacheLimit(); - if (maxByteCount != dbLimit) { - maxByteCount = dbLimit; - releaseMemory(); - } - } - - static synchronized Entry get(final PackFile pack, final long position) { - final Slot e = cache[hash(position)]; - if (e.provider == pack && e.position == position) { - final Entry buf = e.data.get(); - if (buf != null) { - moveToHead(e); - return buf; - } - } - return null; - } - - static synchronized void store(final PackFile pack, final long position, - final byte[] data, final int objectType) { - if (data.length > maxByteCount) - return; // Too large to cache. - - final Slot e = cache[hash(position)]; - clearEntry(e); - - openByteCount += data.length; - releaseMemory(); - - e.provider = pack; - e.position = position; - e.sz = data.length; - e.data = new SoftReference(new Entry(data, objectType)); - moveToHead(e); - } - - private static void releaseMemory() { - while (openByteCount > maxByteCount && lruTail != null) { - final Slot currOldest = lruTail; - final Slot nextOldest = currOldest.lruPrev; - - clearEntry(currOldest); - currOldest.lruPrev = null; - currOldest.lruNext = null; - - if (nextOldest == null) - lruHead = null; - else - nextOldest.lruNext = null; - lruTail = nextOldest; - } - } - - static synchronized void purge(final PackFile file) { - for (final Slot e : cache) { - if (e.provider == file) { - clearEntry(e); - unlink(e); - } - } - } - - private static void moveToHead(final Slot e) { - unlink(e); - e.lruPrev = null; - e.lruNext = lruHead; - if (lruHead != null) - lruHead.lruPrev = e; - else - lruTail = e; - lruHead = e; - } - - private static void unlink(final Slot e) { - final Slot prev = e.lruPrev; - final Slot next = e.lruNext; - if (prev != null) - prev.lruNext = next; - if (next != null) - next.lruPrev = prev; - } - - private static void clearEntry(final Slot e) { - openByteCount -= e.sz; - e.provider = null; - e.data = DEAD; - e.sz = 0; - } - - private UnpackedObjectCache() { - throw new UnsupportedOperationException(); - } - - static class Entry { - final byte[] data; - - final int type; - - Entry(final byte[] aData, final int aType) { - data = aData; - type = aType; - } - } - - private static class Slot { - Slot lruPrev; - - Slot lruNext; - - PackFile provider; - - long position; - - int sz; - - SoftReference data = DEAD; - } -} 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 - * Copyright (C) 2006-2008, Shawn O. Pearce - * 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 - * Copyright (C) 2006-2008, Shawn O. Pearce - * 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/lib/WindowCache.java b/org.eclipse.jgit/src/org/eclipse/jgit/lib/WindowCache.java deleted file mode 100644 index a44a30ee2d..0000000000 --- a/org.eclipse.jgit/src/org/eclipse/jgit/lib/WindowCache.java +++ /dev/null @@ -1,624 +0,0 @@ -/* - * Copyright (C) 2008-2009, Google Inc. - * Copyright (C) 2008, Shawn O. Pearce - * 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.lang.ref.ReferenceQueue; -import java.lang.ref.SoftReference; -import java.util.Random; -import java.util.concurrent.atomic.AtomicInteger; -import java.util.concurrent.atomic.AtomicLong; -import java.util.concurrent.atomic.AtomicReferenceArray; -import java.util.concurrent.locks.ReentrantLock; - -import org.eclipse.jgit.JGitText; - -/** - * Caches slices of a {@link PackFile} in memory for faster read access. - *

- * The WindowCache serves as a Java based "buffer cache", loading segments of a - * PackFile into the JVM heap prior to use. As JGit often wants to do reads of - * only tiny slices of a file, the WindowCache tries to smooth out these tiny - * reads into larger block-sized IO operations. - *

- * Whenever a cache miss occurs, {@link #load(PackFile, long)} is invoked by - * exactly one thread for the given (PackFile,position) key tuple. - * This is ensured by an array of locks, with the tuple hashed to a lock - * instance. - *

- * During a miss, older entries are evicted from the cache so long as - * {@link #isFull()} returns true. - *

- * Its too expensive during object access to be 100% accurate with a least - * recently used (LRU) algorithm. Strictly ordering every read is a lot of - * overhead that typically doesn't yield a corresponding benefit to the - * application. - *

- * This cache implements a loose LRU policy by randomly picking a window - * comprised of roughly 10% of the cache, and evicting the oldest accessed entry - * within that window. - *

- * Entities created by the cache are held under SoftReferences, permitting the - * Java runtime's garbage collector to evict entries when heap memory gets low. - * Most JREs implement a loose least recently used algorithm for this eviction. - *

- * The internal hash table does not expand at runtime, instead it is fixed in - * size at cache creation time. The internal lock table used to gate load - * invocations is also fixed in size. - *

- * The key tuple is passed through to methods as a pair of parameters rather - * than as a single Object, thus reducing the transient memory allocations of - * callers. It is more efficient to avoid the allocation, as we can't be 100% - * sure that a JIT would be able to stack-allocate a key tuple. - *

- * This cache has an implementation rule such that: - *

    - *
  • {@link #load(PackFile, long)} is invoked by at most one thread at a time - * for a given (PackFile,position) tuple.
  • - *
  • For every load() invocation there is exactly one - * {@link #createRef(PackFile, long, ByteWindow)} invocation to wrap a - * SoftReference around the cached entity.
  • - *
  • For every Reference created by createRef() there will be - * exactly one call to {@link #clear(Ref)} to cleanup any resources associated - * with the (now expired) cached entity.
  • - *
- *

- * Therefore, it is safe to perform resource accounting increments during the - * {@link #load(PackFile, long)} or - * {@link #createRef(PackFile, long, ByteWindow)} methods, and matching - * decrements during {@link #clear(Ref)}. Implementors may need to override - * {@link #createRef(PackFile, long, ByteWindow)} in order to embed additional - * accounting information into an implementation specific {@link Ref} subclass, - * as the cached entity may have already been evicted by the JRE's garbage - * collector. - *

- * To maintain higher concurrency workloads, during eviction only one thread - * performs the eviction work, while other threads can continue to insert new - * objects in parallel. This means that the cache can be temporarily over limit, - * especially if the nominated eviction thread is being starved relative to the - * other threads. - */ -public class WindowCache { - private static final int bits(int newSize) { - if (newSize < 4096) - throw new IllegalArgumentException(JGitText.get().invalidWindowSize); - if (Integer.bitCount(newSize) != 1) - throw new IllegalArgumentException(JGitText.get().windowSizeMustBePowerOf2); - return Integer.numberOfTrailingZeros(newSize); - } - - private static final Random rng = new Random(); - - private static volatile WindowCache cache; - - static { - reconfigure(new WindowCacheConfig()); - } - - /** - * Modify the configuration of the window cache. - *

- * The new configuration is applied immediately. If the new limits are - * smaller than what what is currently cached, older entries will be purged - * as soon as possible to allow the cache to meet the new limit. - * - * @param packedGitLimit - * maximum number of bytes to hold within this instance. - * @param packedGitWindowSize - * number of bytes per window within the cache. - * @param packedGitMMAP - * true to enable use of mmap when creating windows. - * @param deltaBaseCacheLimit - * number of bytes to hold in the delta base cache. - * @deprecated Use {@link WindowCacheConfig} instead. - */ - public static void reconfigure(final int packedGitLimit, - final int packedGitWindowSize, final boolean packedGitMMAP, - final int deltaBaseCacheLimit) { - final WindowCacheConfig c = new WindowCacheConfig(); - c.setPackedGitLimit(packedGitLimit); - c.setPackedGitWindowSize(packedGitWindowSize); - c.setPackedGitMMAP(packedGitMMAP); - c.setDeltaBaseCacheLimit(deltaBaseCacheLimit); - reconfigure(c); - } - - /** - * Modify the configuration of the window cache. - *

- * The new configuration is applied immediately. If the new limits are - * smaller than what what is currently cached, older entries will be purged - * as soon as possible to allow the cache to meet the new limit. - * - * @param cfg - * the new window cache configuration. - * @throws IllegalArgumentException - * the cache configuration contains one or more invalid - * settings, usually too low of a limit. - */ - public static void reconfigure(final WindowCacheConfig cfg) { - final WindowCache nc = new WindowCache(cfg); - final WindowCache oc = cache; - if (oc != null) - oc.removeAll(); - cache = nc; - UnpackedObjectCache.reconfigure(cfg); - } - - static WindowCache getInstance() { - return cache; - } - - static final ByteWindow get(final PackFile pack, final long offset) - throws IOException { - final WindowCache c = cache; - final ByteWindow r = c.getOrLoad(pack, c.toStart(offset)); - if (c != cache) { - // The cache was reconfigured while we were using the old one - // to load this window. The window is still valid, but our - // cache may think its still live. Ensure the window is removed - // from the old cache so resources can be released. - // - c.removeAll(); - } - return r; - } - - static final void purge(final PackFile pack) { - cache.removeAll(pack); - } - - /** ReferenceQueue to cleanup released and garbage collected windows. */ - private final ReferenceQueue queue; - - /** Number of entries in {@link #table}. */ - private final int tableSize; - - /** Access clock for loose LRU. */ - private final AtomicLong clock; - - /** Hash bucket directory; entries are chained below. */ - private final AtomicReferenceArray table; - - /** Locks to prevent concurrent loads for same (PackFile,position). */ - private final Lock[] locks; - - /** Lock to elect the eviction thread after a load occurs. */ - private final ReentrantLock evictLock; - - /** Number of {@link #table} buckets to scan for an eviction window. */ - private final int evictBatch; - - private final int maxFiles; - - private final long maxBytes; - - private final boolean mmap; - - private final int windowSizeShift; - - private final int windowSize; - - private final AtomicInteger openFiles; - - private final AtomicLong openBytes; - - private WindowCache(final WindowCacheConfig cfg) { - tableSize = tableSize(cfg); - final int lockCount = lockCount(cfg); - if (tableSize < 1) - throw new IllegalArgumentException(JGitText.get().tSizeMustBeGreaterOrEqual1); - if (lockCount < 1) - throw new IllegalArgumentException(JGitText.get().lockCountMustBeGreaterOrEqual1); - - queue = new ReferenceQueue(); - clock = new AtomicLong(1); - table = new AtomicReferenceArray(tableSize); - locks = new Lock[lockCount]; - for (int i = 0; i < locks.length; i++) - locks[i] = new Lock(); - evictLock = new ReentrantLock(); - - int eb = (int) (tableSize * .1); - if (64 < eb) - eb = 64; - else if (eb < 4) - eb = 4; - if (tableSize < eb) - eb = tableSize; - evictBatch = eb; - - maxFiles = cfg.getPackedGitOpenFiles(); - maxBytes = cfg.getPackedGitLimit(); - mmap = cfg.isPackedGitMMAP(); - windowSizeShift = bits(cfg.getPackedGitWindowSize()); - windowSize = 1 << windowSizeShift; - - openFiles = new AtomicInteger(); - openBytes = new AtomicLong(); - - if (maxFiles < 1) - throw new IllegalArgumentException(JGitText.get().openFilesMustBeAtLeast1); - if (maxBytes < windowSize) - throw new IllegalArgumentException(JGitText.get().windowSizeMustBeLesserThanLimit); - } - - int getOpenFiles() { - return openFiles.get(); - } - - long getOpenBytes() { - return openBytes.get(); - } - - private int hash(final int packHash, final long off) { - return packHash + (int) (off >>> windowSizeShift); - } - - private ByteWindow load(final PackFile pack, final long offset) - throws IOException { - if (pack.beginWindowCache()) - openFiles.incrementAndGet(); - try { - if (mmap) - return pack.mmap(offset, windowSize); - return pack.read(offset, windowSize); - } catch (IOException e) { - close(pack); - throw e; - } catch (RuntimeException e) { - close(pack); - throw e; - } catch (Error e) { - close(pack); - throw e; - } - } - - private Ref createRef(final PackFile p, final long o, final ByteWindow v) { - final Ref ref = new Ref(p, o, v, queue); - openBytes.addAndGet(ref.size); - return ref; - } - - private void clear(final Ref ref) { - openBytes.addAndGet(-ref.size); - close(ref.pack); - } - - private void close(final PackFile pack) { - if (pack.endWindowCache()) - openFiles.decrementAndGet(); - } - - private boolean isFull() { - return maxFiles < openFiles.get() || maxBytes < openBytes.get(); - } - - private long toStart(final long offset) { - return (offset >>> windowSizeShift) << windowSizeShift; - } - - private static int tableSize(final WindowCacheConfig cfg) { - final int wsz = cfg.getPackedGitWindowSize(); - final long limit = cfg.getPackedGitLimit(); - if (wsz <= 0) - throw new IllegalArgumentException(JGitText.get().invalidWindowSize); - if (limit < wsz) - throw new IllegalArgumentException(JGitText.get().windowSizeMustBeLesserThanLimit); - return (int) Math.min(5 * (limit / wsz) / 2, 2000000000); - } - - private static int lockCount(final WindowCacheConfig cfg) { - return Math.max(cfg.getPackedGitOpenFiles(), 32); - } - - /** - * Lookup a cached object, creating and loading it if it doesn't exist. - * - * @param pack - * the pack that "contains" the cached object. - * @param position - * offset within pack of the object. - * @return the object reference. - * @throws IOException - * the object reference was not in the cache and could not be - * obtained by {@link #load(PackFile, long)}. - */ - private ByteWindow getOrLoad(final PackFile pack, final long position) - throws IOException { - final int slot = slot(pack, position); - final Entry e1 = table.get(slot); - ByteWindow v = scan(e1, pack, position); - if (v != null) - return v; - - synchronized (lock(pack, position)) { - Entry e2 = table.get(slot); - if (e2 != e1) { - v = scan(e2, pack, position); - if (v != null) - return v; - } - - v = load(pack, position); - final Ref ref = createRef(pack, position, v); - hit(ref); - for (;;) { - final Entry n = new Entry(clean(e2), ref); - if (table.compareAndSet(slot, e2, n)) - break; - e2 = table.get(slot); - } - } - - if (evictLock.tryLock()) { - try { - gc(); - evict(); - } finally { - evictLock.unlock(); - } - } - - return v; - } - - private ByteWindow scan(Entry n, final PackFile pack, final long position) { - for (; n != null; n = n.next) { - final Ref r = n.ref; - if (r.pack == pack && r.position == position) { - final ByteWindow v = r.get(); - if (v != null) { - hit(r); - return v; - } - n.kill(); - break; - } - } - return null; - } - - private void hit(final Ref r) { - // We don't need to be 100% accurate here. Its sufficient that at least - // one thread performs the increment. Any other concurrent access at - // exactly the same time can simply use the same clock value. - // - // Consequently we attempt the set, but we don't try to recover should - // it fail. This is why we don't use getAndIncrement() here. - // - final long c = clock.get(); - clock.compareAndSet(c, c + 1); - r.lastAccess = c; - } - - private void evict() { - while (isFull()) { - int ptr = rng.nextInt(tableSize); - Entry old = null; - int slot = 0; - for (int b = evictBatch - 1; b >= 0; b--, ptr++) { - if (tableSize <= ptr) - ptr = 0; - for (Entry e = table.get(ptr); e != null; e = e.next) { - if (e.dead) - continue; - if (old == null || e.ref.lastAccess < old.ref.lastAccess) { - old = e; - slot = ptr; - } - } - } - if (old != null) { - old.kill(); - gc(); - final Entry e1 = table.get(slot); - table.compareAndSet(slot, e1, clean(e1)); - } - } - } - - /** - * Clear every entry from the cache. - *

- * This is a last-ditch effort to clear out the cache, such as before it - * gets replaced by another cache that is configured differently. This - * method tries to force every cached entry through {@link #clear(Ref)} to - * ensure that resources are correctly accounted for and cleaned up by the - * subclass. A concurrent reader loading entries while this method is - * running may cause resource accounting failures. - */ - private void removeAll() { - for (int s = 0; s < tableSize; s++) { - Entry e1; - do { - e1 = table.get(s); - for (Entry e = e1; e != null; e = e.next) - e.kill(); - } while (!table.compareAndSet(s, e1, null)); - } - gc(); - } - - /** - * Clear all entries related to a single file. - *

- * Typically this method is invoked during {@link PackFile#close()}, when we - * know the pack is never going to be useful to us again (for example, it no - * longer exists on disk). A concurrent reader loading an entry from this - * same pack may cause the pack to become stuck in the cache anyway. - * - * @param pack - * the file to purge all entries of. - */ - private void removeAll(final PackFile pack) { - for (int s = 0; s < tableSize; s++) { - final Entry e1 = table.get(s); - boolean hasDead = false; - for (Entry e = e1; e != null; e = e.next) { - if (e.ref.pack == pack) { - e.kill(); - hasDead = true; - } else if (e.dead) - hasDead = true; - } - if (hasDead) - table.compareAndSet(s, e1, clean(e1)); - } - gc(); - } - - @SuppressWarnings("unchecked") - private void gc() { - Ref r; - while ((r = (Ref) queue.poll()) != null) { - // Sun's Java 5 and 6 implementation have a bug where a Reference - // can be enqueued and dequeued twice on the same reference queue - // due to a race condition within ReferenceQueue.enqueue(Reference). - // - // http://bugs.sun.com/bugdatabase/view_bug.do?bug_id=6837858 - // - // We CANNOT permit a Reference to come through us twice, as it will - // skew the resource counters we maintain. Our canClear() check here - // provides a way to skip the redundant dequeues, if any. - // - if (r.canClear()) { - clear(r); - - boolean found = false; - final int s = slot(r.pack, r.position); - final Entry e1 = table.get(s); - for (Entry n = e1; n != null; n = n.next) { - if (n.ref == r) { - n.dead = true; - found = true; - break; - } - } - if (found) - table.compareAndSet(s, e1, clean(e1)); - } - } - } - - private int slot(final PackFile pack, final long position) { - return (hash(pack.hash, position) >>> 1) % tableSize; - } - - private Lock lock(final PackFile pack, final long position) { - return locks[(hash(pack.hash, position) >>> 1) % locks.length]; - } - - private static Entry clean(Entry top) { - while (top != null && top.dead) { - top.ref.enqueue(); - top = top.next; - } - if (top == null) - return null; - final Entry n = clean(top.next); - return n == top.next ? top : new Entry(n, top.ref); - } - - private static class Entry { - /** Next entry in the hash table's chain list. */ - final Entry next; - - /** The referenced object. */ - final Ref ref; - - /** - * Marked true when ref.get() returns null and the ref is dead. - *

- * A true here indicates that the ref is no longer accessible, and that - * we therefore need to eventually purge this Entry object out of the - * bucket's chain. - */ - volatile boolean dead; - - Entry(final Entry n, final Ref r) { - next = n; - ref = r; - } - - final void kill() { - dead = true; - ref.enqueue(); - } - } - - /** A soft reference wrapped around a cached object. */ - private static class Ref extends SoftReference { - final PackFile pack; - - final long position; - - final int size; - - long lastAccess; - - private boolean cleared; - - protected Ref(final PackFile pack, final long position, - final ByteWindow v, final ReferenceQueue queue) { - super(v, queue); - this.pack = pack; - this.position = position; - this.size = v.size(); - } - - final synchronized boolean canClear() { - if (cleared) - return false; - cleared = true; - return true; - } - } - - private static final class Lock { - // Used only for its implicit monitor. - } -} diff --git a/org.eclipse.jgit/src/org/eclipse/jgit/lib/WindowCacheConfig.java b/org.eclipse.jgit/src/org/eclipse/jgit/lib/WindowCacheConfig.java deleted file mode 100644 index 2d8aef34ca..0000000000 --- a/org.eclipse.jgit/src/org/eclipse/jgit/lib/WindowCacheConfig.java +++ /dev/null @@ -1,176 +0,0 @@ -/* - * 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; - -/** Configuration parameters for {@link WindowCache}. */ -public class WindowCacheConfig { - /** 1024 (number of bytes in one kibibyte/kilobyte) */ - public static final int KB = 1024; - - /** 1024 {@link #KB} (number of bytes in one mebibyte/megabyte) */ - public static final int MB = 1024 * KB; - - private int packedGitOpenFiles; - - private long packedGitLimit; - - private int packedGitWindowSize; - - private boolean packedGitMMAP; - - private int deltaBaseCacheLimit; - - /** Create a default configuration. */ - public WindowCacheConfig() { - packedGitOpenFiles = 128; - packedGitLimit = 10 * MB; - packedGitWindowSize = 8 * KB; - packedGitMMAP = false; - deltaBaseCacheLimit = 10 * MB; - } - - /** - * @return maximum number of streams to open at a time. Open packs count - * against the process limits. Default is 128. - */ - public int getPackedGitOpenFiles() { - return packedGitOpenFiles; - } - - /** - * @param fdLimit - * maximum number of streams to open at a time. Open packs count - * against the process limits - */ - public void setPackedGitOpenFiles(final int fdLimit) { - packedGitOpenFiles = fdLimit; - } - - /** - * @return maximum number bytes of heap memory to dedicate to caching pack - * file data. Default is 10 MB. - */ - public long getPackedGitLimit() { - return packedGitLimit; - } - - /** - * @param newLimit - * maximum number bytes of heap memory to dedicate to caching - * pack file data. - */ - public void setPackedGitLimit(final long newLimit) { - packedGitLimit = newLimit; - } - - /** - * @return size in bytes of a single window mapped or read in from the pack - * file. Default is 8 KB. - */ - public int getPackedGitWindowSize() { - return packedGitWindowSize; - } - - /** - * @param newSize - * size in bytes of a single window read in from the pack file. - */ - public void setPackedGitWindowSize(final int newSize) { - packedGitWindowSize = newSize; - } - - /** - * @return true enables use of Java NIO virtual memory mapping for windows; - * false reads entire window into a byte[] with standard read calls. - * Default false. - */ - public boolean isPackedGitMMAP() { - return packedGitMMAP; - } - - /** - * @param usemmap - * true enables use of Java NIO virtual memory mapping for - * windows; false reads entire window into a byte[] with standard - * read calls. - */ - public void setPackedGitMMAP(final boolean usemmap) { - packedGitMMAP = usemmap; - } - - /** - * @return maximum number of bytes to cache in {@link UnpackedObjectCache} - * for inflated, recently accessed objects, without delta chains. - * Default 10 MB. - */ - public int getDeltaBaseCacheLimit() { - return deltaBaseCacheLimit; - } - - /** - * @param newLimit - * maximum number of bytes to cache in - * {@link UnpackedObjectCache} for inflated, recently accessed - * objects, without delta chains. - */ - public void setDeltaBaseCacheLimit(final int newLimit) { - deltaBaseCacheLimit = newLimit; - } - - /** - * Update properties by setting fields from the configuration. - *

- * If a property is not defined in the configuration, then it is left - * unmodified. - * - * @param rc configuration to read properties from. - */ - public void fromConfig(final Config rc) { - setPackedGitOpenFiles(rc.getInt("core", null, "packedgitopenfiles", getPackedGitOpenFiles())); - setPackedGitLimit(rc.getLong("core", null, "packedgitlimit", getPackedGitLimit())); - setPackedGitWindowSize(rc.getInt("core", null, "packedgitwindowsize", getPackedGitWindowSize())); - setPackedGitMMAP(rc.getBoolean("core", null, "packedgitmmap", isPackedGitMMAP())); - setDeltaBaseCacheLimit(rc.getInt("core", null, "deltabasecachelimit", getDeltaBaseCacheLimit())); - } -} diff --git a/org.eclipse.jgit/src/org/eclipse/jgit/lib/WindowCursor.java b/org.eclipse.jgit/src/org/eclipse/jgit/lib/WindowCursor.java deleted file mode 100644 index 98916efcbd..0000000000 --- a/org.eclipse.jgit/src/org/eclipse/jgit/lib/WindowCursor.java +++ /dev/null @@ -1,213 +0,0 @@ -/* - * Copyright (C) 2008-2009, Google Inc. - * Copyright (C) 2006-2008, Shawn O. Pearce - * 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.zip.DataFormatException; -import java.util.zip.Inflater; - -import org.eclipse.jgit.errors.MissingObjectException; -import org.eclipse.jgit.errors.StoredObjectRepresentationNotAvailableException; -import org.eclipse.jgit.revwalk.RevObject; - -/** Active handle to a ByteWindow. */ -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]; - - private Inflater inf; - - private ByteWindow window; - - private final FileObjectDatabase db; - - WindowCursor(FileObjectDatabase db) { - this.db = db; - } - - public boolean hasObject(AnyObjectId objectId) throws IOException { - return db.hasObject(objectId); - } - - public ObjectLoader openObject(AnyObjectId objectId, int typeHint) - throws MissingObjectException, 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); - } - return ldr; - } - - 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.copyFromPack.copyAsIs(out, src, this); - } - - /** - * Copy bytes from the window to a caller supplied buffer. - * - * @param pack - * the file the desired window is stored within. - * @param position - * position within the file to read from. - * @param dstbuf - * destination buffer to copy into. - * @param dstoff - * offset within dstbuf to start copying into. - * @param cnt - * number of bytes to copy. This value may exceed the number of - * bytes remaining in the window starting at offset - * pos. - * @return number of bytes actually copied; this may be less than - * cnt if cnt 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. - */ - int copy(final PackFile pack, long position, final byte[] dstbuf, - int dstoff, final int cnt) throws IOException { - final long length = pack.length; - int need = cnt; - while (need > 0 && position < length) { - pin(pack, position); - final int r = window.copy(position, dstbuf, dstoff, need); - position += r; - dstoff += r; - need -= r; - } - return cnt - need; - } - - /** - * Pump bytes into the supplied inflater as input. - * - * @param pack - * the file the desired window is stored within. - * @param position - * position within the file to read from. - * @param dstbuf - * destination buffer the inflater should output decompressed - * data to. - * @param dstoff - * current offset within dstbuf to inflate into. - * @return updated dstoff based on the number of bytes - * successfully inflated into dstbuf. - * @throws IOException - * this cursor does not match the provider or id and the proper - * window could not be acquired through the provider's cache. - * @throws DataFormatException - * the inflater encountered an invalid chunk of data. Data - * stream corruption is likely. - */ - 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; - } - } - - 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(); - return inf; - } - - private void prepareInflater() { - if (inf == null) - inf = InflaterCache.get(); - else - inf.reset(); - } - - private void pin(final PackFile pack, final long position) - throws IOException { - final ByteWindow w = window; - if (w == null || !w.contains(pack, position)) { - // If memory is low, we may need what is in our window field to - // be cleaned up by the GC during the get for the next window. - // So we always clear it, even though we are just going to set - // it again. - // - window = null; - window = WindowCache.get(pack, position); - } - } - - /** Release the current window cursor. */ - public void release() { - window = null; - try { - InflaterCache.release(inf); - } finally { - inf = null; - } - } -} diff --git a/org.eclipse.jgit/src/org/eclipse/jgit/storage/file/ByteArrayWindow.java b/org.eclipse.jgit/src/org/eclipse/jgit/storage/file/ByteArrayWindow.java new file mode 100644 index 0000000000..840244b77f --- /dev/null +++ b/org.eclipse.jgit/src/org/eclipse/jgit/storage/file/ByteArrayWindow.java @@ -0,0 +1,101 @@ +/* + * Copyright (C) 2008-2009, Google Inc. + * Copyright (C) 2007, Robin Rosenberg + * Copyright (C) 2006-2008, Shawn O. Pearce + * 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.OutputStream; +import java.util.zip.CRC32; +import java.util.zip.DataFormatException; +import java.util.zip.Inflater; + +/** + * A {@link ByteWindow} with an underlying byte array for storage. + */ +final class ByteArrayWindow extends ByteWindow { + private final byte[] array; + + ByteArrayWindow(final PackFile pack, final long o, final byte[] b) { + super(pack, o, b.length); + array = b; + } + + @Override + protected int copy(final int p, final byte[] b, final int o, int n) { + n = Math.min(array.length - p, n); + System.arraycopy(array, p, b, o, n); + return n; + } + + @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; + } + + 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 { + 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/storage/file/ByteBufferWindow.java b/org.eclipse.jgit/src/org/eclipse/jgit/storage/file/ByteBufferWindow.java new file mode 100644 index 0000000000..52bc00f355 --- /dev/null +++ b/org.eclipse.jgit/src/org/eclipse/jgit/storage/file/ByteBufferWindow.java @@ -0,0 +1,92 @@ +/* + * Copyright (C) 2008-2009, Google Inc. + * Copyright (C) 2007, Robin Rosenberg + * Copyright (C) 2006-2008, Shawn O. Pearce + * 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.nio.ByteBuffer; +import java.util.zip.DataFormatException; +import java.util.zip.Inflater; + +/** + * A window for accessing git packs using a {@link ByteBuffer} for storage. + * + * @see ByteWindow + */ +final class ByteBufferWindow extends ByteWindow { + private final ByteBuffer buffer; + + ByteBufferWindow(final PackFile pack, final long o, final ByteBuffer b) { + super(pack, o, b.capacity()); + buffer = b; + } + + @Override + protected int copy(final int p, final byte[] b, final int o, int n) { + final ByteBuffer s = buffer.slice(); + s.position(p); + n = Math.min(s.remaining(), n); + s.get(b, o, n); + return n; + } + + @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; + } +} diff --git a/org.eclipse.jgit/src/org/eclipse/jgit/storage/file/ByteWindow.java b/org.eclipse.jgit/src/org/eclipse/jgit/storage/file/ByteWindow.java new file mode 100644 index 0000000000..5c77cff012 --- /dev/null +++ b/org.eclipse.jgit/src/org/eclipse/jgit/storage/file/ByteWindow.java @@ -0,0 +1,175 @@ +/* + * Copyright (C) 2007, Robin Rosenberg + * Copyright (C) 2006-2008, Shawn O. Pearce + * 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.util.zip.DataFormatException; +import java.util.zip.Inflater; + +/** + * A window of data currently stored within a cache. + *

+ * All bytes in the window can be assumed to be "immediately available", that is + * they are very likely already in memory, unless the operating system's memory + * is very low and has paged part of this process out to disk. Therefore copying + * bytes from a window is very inexpensive. + *

+ */ +abstract class ByteWindow { + protected final PackFile pack; + + protected final long start; + + protected final long end; + + protected ByteWindow(final PackFile p, final long s, final int n) { + pack = p; + start = s; + end = start + n; + } + + final int size() { + return (int) (end - start); + } + + final boolean contains(final PackFile neededFile, final long neededPos) { + return pack == neededFile && start <= neededPos && neededPos < end; + } + + /** + * Copy bytes from the window to a caller supplied buffer. + * + * @param pos + * offset within the file to start copying from. + * @param dstbuf + * destination buffer to copy into. + * @param dstoff + * offset within dstbuf to start copying into. + * @param cnt + * number of bytes to copy. This value may exceed the number of + * bytes remaining in the window starting at offset + * pos. + * @return number of bytes actually copied; this may be less than + * cnt if cnt exceeded the number of + * bytes available. + */ + final int copy(long pos, byte[] dstbuf, int dstoff, int cnt) { + return copy((int) (pos - start), dstbuf, dstoff, cnt); + } + + /** + * Copy bytes from the window to a caller supplied buffer. + * + * @param pos + * offset within the window to start copying from. + * @param dstbuf + * destination buffer to copy into. + * @param dstoff + * offset within dstbuf to start copying into. + * @param cnt + * number of bytes to copy. This value may exceed the number of + * bytes remaining in the window starting at offset + * pos. + * @return number of bytes actually copied; this may be less than + * cnt if cnt exceeded the number of + * bytes available. + */ + 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 dstbuf 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 dstoff based on the number of bytes + * successfully copied into dstbuf by + * inf. 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 dstbuf 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 dstoff based on the number of bytes + * successfully copied into dstbuf by + * inf. 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; +} diff --git a/org.eclipse.jgit/src/org/eclipse/jgit/storage/file/CachedObjectDirectory.java b/org.eclipse.jgit/src/org/eclipse/jgit/storage/file/CachedObjectDirectory.java new file mode 100644 index 0000000000..79730d0551 --- /dev/null +++ b/org.eclipse.jgit/src/org/eclipse/jgit/storage/file/CachedObjectDirectory.java @@ -0,0 +1,194 @@ +/* + * Copyright (C) 2010, Constantine Plotnikov + * 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.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.lib.ObjectToPack; +import org.eclipse.jgit.lib.PackWriter; + +/** + * The cached instance of an {@link ObjectDirectory}. + *

+ * This class caches the list of loose objects in memory, so the file system is + * not queried with stat calls. + */ +class CachedObjectDirectory extends FileObjectDatabase { + /** + * The set that contains unpacked objects identifiers, it is created when + * the cached instance is created. + */ + private final ObjectIdSubclassMap unpackedObjects = new ObjectIdSubclassMap(); + + private final ObjectDirectory wrapped; + + private AlternateHandle[] alts; + + /** + * The constructor + * + * @param wrapped + * the wrapped database + */ + CachedObjectDirectory(ObjectDirectory wrapped) { + this.wrapped = wrapped; + + File objects = wrapped.getDirectory(); + String[] fanout = objects.list(); + if (fanout == null) + fanout = new String[0]; + for (String d : fanout) { + if (d.length() != 2) + continue; + String[] entries = new File(objects, d).list(); + if (entries == null) + continue; + for (String e : entries) { + if (e.length() != Constants.OBJECT_ID_STRING_LENGTH - 2) + continue; + try { + unpackedObjects.add(ObjectId.fromString(d + e)); + } catch (IllegalArgumentException notAnObject) { + // ignoring the file that does not represent loose object + } + } + } + } + + @Override + 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 hasObject(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 { + // This method should never be invoked. + throw new UnsupportedOperationException(); + } + + @Override + void selectObjectRepresentation(PackWriter packer, ObjectToPack otp, + WindowCursor curs) throws IOException { + wrapped.selectObjectRepresentation(packer, otp, curs); + } +} diff --git a/org.eclipse.jgit/src/org/eclipse/jgit/storage/file/DeltaOfsPackedObjectLoader.java b/org.eclipse.jgit/src/org/eclipse/jgit/storage/file/DeltaOfsPackedObjectLoader.java new file mode 100644 index 0000000000..1a5fe8ea90 --- /dev/null +++ b/org.eclipse.jgit/src/org/eclipse/jgit/storage/file/DeltaOfsPackedObjectLoader.java @@ -0,0 +1,84 @@ +/* + * Copyright (C) 2009, Google Inc. + * Copyright (C) 2008, Marek Zawirski + * Copyright (C) 2007, Robin Rosenberg + * Copyright (C) 2006-2008, Shawn O. Pearce + * 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 org.eclipse.jgit.JGitText; +import org.eclipse.jgit.errors.CorruptObjectException; +import org.eclipse.jgit.lib.Constants; +import org.eclipse.jgit.lib.ObjectId; + +/** 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; + } + + @Override + ObjectId getDeltaBase() throws IOException { + final ObjectId id = pack.findObjectForOffset(deltaBase); + if (id == null) + throw new CorruptObjectException( + JGitText.get().offsetWrittenDeltaBaseForObjectNotFoundInAPack); + return id; + } +} diff --git a/org.eclipse.jgit/src/org/eclipse/jgit/storage/file/DeltaPackedObjectLoader.java b/org.eclipse.jgit/src/org/eclipse/jgit/storage/file/DeltaPackedObjectLoader.java new file mode 100644 index 0000000000..4a9323cccf --- /dev/null +++ b/org.eclipse.jgit/src/org/eclipse/jgit/storage/file/DeltaPackedObjectLoader.java @@ -0,0 +1,120 @@ +/* + * Copyright (C) 2008-2009, Google Inc. + * Copyright (C) 2008, Marek Zawirski + * Copyright (C) 2007, Robin Rosenberg + * Copyright (C) 2006-2008, Shawn O. Pearce + * 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.text.MessageFormat; +import java.util.zip.DataFormatException; + +import org.eclipse.jgit.JGitText; +import org.eclipse.jgit.errors.CorruptObjectException; +import org.eclipse.jgit.lib.BinaryDelta; +import org.eclipse.jgit.lib.Constants; + +/** 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 + 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/storage/file/DeltaRefPackedObjectLoader.java b/org.eclipse.jgit/src/org/eclipse/jgit/storage/file/DeltaRefPackedObjectLoader.java new file mode 100644 index 0000000000..40e1975e94 --- /dev/null +++ b/org.eclipse.jgit/src/org/eclipse/jgit/storage/file/DeltaRefPackedObjectLoader.java @@ -0,0 +1,82 @@ +/* + * Copyright (C) 2008-2009, Google Inc. + * Copyright (C) 2008, Marek Zawirski + * Copyright (C) 2007, Robin Rosenberg + * Copyright (C) 2006-2008, Shawn O. Pearce + * 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 org.eclipse.jgit.errors.MissingObjectException; +import org.eclipse.jgit.lib.Constants; +import org.eclipse.jgit.lib.ObjectId; + +/** Reads a deltified object which uses an {@link ObjectId} to find its base. */ +class DeltaRefPackedObjectLoader extends DeltaPackedObjectLoader { + private final ObjectId deltaBase; + + DeltaRefPackedObjectLoader(final PackFile pr, final long objectOffset, + final int headerSz, final int deltaSz, final ObjectId base) { + super(pr, objectOffset, headerSz, deltaSz); + deltaBase = base; + } + + 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; + } + + @Override + public int getRawType() { + return Constants.OBJ_REF_DELTA; + } + + @Override + ObjectId getDeltaBase() throws IOException { + return deltaBase; + } +} diff --git a/org.eclipse.jgit/src/org/eclipse/jgit/storage/file/FileBasedConfig.java b/org.eclipse.jgit/src/org/eclipse/jgit/storage/file/FileBasedConfig.java new file mode 100644 index 0000000000..b39efb0e8b --- /dev/null +++ b/org.eclipse.jgit/src/org/eclipse/jgit/storage/file/FileBasedConfig.java @@ -0,0 +1,165 @@ +/* + * Copyright (C) 2009, Constantine Plotnikov + * Copyright (C) 2007, Dave Watson + * Copyright (C) 2009, Google Inc. + * Copyright (C) 2009, JetBrains s.r.o. + * Copyright (C) 2008-2009, Robin Rosenberg + * Copyright (C) 2008, Shawn O. Pearce + * Copyright (C) 2008, Thad Hughes + * 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.FileNotFoundException; +import java.io.IOException; +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; + +/** + * The configuration file that is stored in the file of the file system. + */ +public class FileBasedConfig extends Config { + private final File configFile; + private volatile long lastModified; + + /** + * Create a configuration with no default fallback. + * + * @param cfgLocation + * the location of the configuration file on the file system + */ + public FileBasedConfig(File cfgLocation) { + this(null, cfgLocation); + } + + /** + * The constructor + * + * @param base + * the base configuration file + * @param cfgLocation + * the location of the configuration file on the file system + */ + public FileBasedConfig(Config base, File cfgLocation) { + super(base); + configFile = cfgLocation; + } + + /** @return location of the configuration file on disk */ + public final File getFile() { + return configFile; + } + + /** + * Load the configuration as a Git text style configuration file. + *

+ * If the file does not exist, this configuration is cleared, and thus + * behaves the same as though the file exists, but is empty. + * + * @throws IOException + * the file could not be read (but does exist). + * @throws ConfigInvalidException + * the file is not a properly formatted configuration file. + */ + public void load() throws IOException, ConfigInvalidException { + lastModified = getFile().lastModified(); + try { + fromText(RawParseUtils.decode(IO.readFully(getFile()))); + } catch (FileNotFoundException noFile) { + clear(); + } catch (IOException e) { + final IOException e2 = new IOException(MessageFormat.format(JGitText.get().cannotReadFile, getFile())); + e2.initCause(e); + throw e2; + } catch (ConfigInvalidException e) { + throw new ConfigInvalidException(MessageFormat.format(JGitText.get().cannotReadFile, getFile()), e); + } + } + + /** + * Save the configuration as a Git text style configuration file. + *

+ * Warning: Although this method uses the traditional Git file + * locking approach to protect against concurrent writes of the + * configuration file, it does not ensure that the file has not been + * modified since the last read, which means updates performed by other + * objects accessing the same backing file may be lost. + * + * @throws IOException + * the file could not be written. + */ + public void save() throws IOException { + final byte[] out = Constants.encode(toText()); + final LockFile lf = new LockFile(getFile()); + if (!lf.lock()) + throw new IOException(MessageFormat.format(JGitText.get().cannotLockFile, getFile())); + try { + lf.setNeedStatInformation(true); + lf.write(out); + if (!lf.commit()) + throw new IOException(MessageFormat.format(JGitText.get().cannotCommitWriteTo, getFile())); + } finally { + lf.unlock(); + } + lastModified = lf.getCommitLastModified(); + } + + @Override + public String toString() { + return getClass().getSimpleName() + "[" + getFile().getPath() + "]"; + } + + /** + * @return returns true if the currently loaded configuration file is older + * than the file on disk + */ + public boolean isOutdated() { + return getFile().lastModified() != lastModified; + } +} 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..6266a7c084 --- /dev/null +++ b/org.eclipse.jgit/src/org/eclipse/jgit/storage/file/FileObjectDatabase.java @@ -0,0 +1,214 @@ +/* + * 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.lib.ObjectToPack; +import org.eclipse.jgit.lib.PackWriter; + +abstract class FileObjectDatabase extends ObjectDatabase { + @Override + public ObjectReader newReader() { + return new WindowCursor(this); + } + + /** + * Does the requested object exist in this database? + *

+ * 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 hasObject(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. + *

+ * 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; + } + + 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 FileObjectDatabase newCachedFileObjectDatabase(); + + 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..c86549b2e2 --- /dev/null +++ b/org.eclipse.jgit/src/org/eclipse/jgit/storage/file/FileRepository.java @@ -0,0 +1,327 @@ +/* + * Copyright (C) 2007, Dave Watson + * Copyright (C) 2008-2010, Google Inc. + * Copyright (C) 2006-2010, Robin Rosenberg + * Copyright (C) 2006-2008, Shawn O. Pearce + * 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. + * + *

    + *
  • GIT_DIR + *
      + *
    • objects/ - objects
    • + *
    • refs/ - tags and heads
    • + *
    • config - configuration
    • + *
    • info/ - more configurations
    • + *
    + *
  • + *
+ *

+ * This class is thread-safe. + *

+ * 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. + *

+ * 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: + * + *

+	 * new FileRepositoryBuilder().setGitDir(gitDir).build()
+	 * 
+ * + * @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()); + + 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()}. + *

+ * 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 getAdditionalHaves() { + HashSet r = new HashSet(); + 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/storage/file/FileRepositoryBuilder.java b/org.eclipse.jgit/src/org/eclipse/jgit/storage/file/FileRepositoryBuilder.java new file mode 100644 index 0000000000..31d3e99ad6 --- /dev/null +++ b/org.eclipse.jgit/src/org/eclipse/jgit/storage/file/FileRepositoryBuilder.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.file; + +import java.io.File; +import java.io.IOException; + +import org.eclipse.jgit.lib.BaseRepositoryBuilder; + +/** + * Constructs a {@link FileRepository}. + *

+ * 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. + *

+ * Single repository applications trying to be compatible with other Git + * implementations are encouraged to use a model such as: + * + *

+ * 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()
+ * 
+ */ +public class FileRepositoryBuilder extends + BaseRepositoryBuilder { + /** + * Create a repository matching the configuration in this builder. + *

+ * 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 FileRepository build() throws IOException { + return new FileRepository(setup()); + } +} diff --git a/org.eclipse.jgit/src/org/eclipse/jgit/storage/file/LocalObjectRepresentation.java b/org.eclipse.jgit/src/org/eclipse/jgit/storage/file/LocalObjectRepresentation.java new file mode 100644 index 0000000000..4949b507eb --- /dev/null +++ b/org.eclipse.jgit/src/org/eclipse/jgit/storage/file/LocalObjectRepresentation.java @@ -0,0 +1,83 @@ +/* + * 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 org.eclipse.jgit.lib.ObjectId; +import org.eclipse.jgit.lib.StoredObjectRepresentation; + +class LocalObjectRepresentation extends StoredObjectRepresentation { + final PackedObjectLoader ldr; + + LocalObjectRepresentation(PackedObjectLoader ldr) { + this.ldr = ldr; + } + + @Override + public int getFormat() { + if (ldr instanceof DeltaPackedObjectLoader) + return PACK_DELTA; + if (ldr instanceof WholePackedObjectLoader) + return PACK_WHOLE; + return FORMAT_OTHER; + } + + @Override + public int getWeight() { + long sz = ldr.getRawSize(); + if (Integer.MAX_VALUE < sz) + return WEIGHT_UNKNOWN; + return (int) sz; + } + + @Override + public ObjectId getDeltaBase() { + try { + return ldr.getDeltaBase(); + } catch (IOException e) { + return null; + } + } +} 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..9ee43f398d --- /dev/null +++ b/org.eclipse.jgit/src/org/eclipse/jgit/storage/file/LocalObjectToPack.java @@ -0,0 +1,68 @@ +/* + * 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.lib.ObjectToPack; +import org.eclipse.jgit.lib.StoredObjectRepresentation; +import org.eclipse.jgit.revwalk.RevObject; + +/** {@link ObjectToPack} for {@link ObjectDirectory}. */ +class LocalObjectToPack extends ObjectToPack { + /** Pack to reuse compressed data from, otherwise null. */ + PackFile copyFromPack; + + /** Offset of the object's header in {@link #copyFromPack}. */ + long copyOffset; + + LocalObjectToPack(RevObject obj) { + super(obj); + } + + @Override + public void select(StoredObjectRepresentation ref) { + LocalObjectRepresentation ptr = (LocalObjectRepresentation)ref; + this.copyFromPack = ptr.ldr.pack; + this.copyOffset = ptr.ldr.objectOffset; + } +} diff --git a/org.eclipse.jgit/src/org/eclipse/jgit/storage/file/LockFile.java b/org.eclipse.jgit/src/org/eclipse/jgit/storage/file/LockFile.java new file mode 100644 index 0000000000..ad89a24847 --- /dev/null +++ b/org.eclipse.jgit/src/org/eclipse/jgit/storage/file/LockFile.java @@ -0,0 +1,450 @@ +/* + * Copyright (C) 2007, Robin Rosenberg + * Copyright (C) 2006-2008, Shawn O. Pearce + * 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.BufferedOutputStream; +import java.io.File; +import java.io.FileInputStream; +import java.io.FileNotFoundException; +import java.io.FileOutputStream; +import java.io.FilenameFilter; +import java.io.IOException; +import java.io.OutputStream; +import java.nio.channels.FileLock; +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. + *

+ * To modify a ref file Git tries to use an atomic update approach: we write the + * new data into a brand new file, then rename it in place over the old name. + * This way we can just delete the temporary file if anything goes wrong, and + * nothing has been damaged. To coordinate access from multiple processes at + * once Git tries to atomically create the new temporary file under a well-known + * name. + */ +public class LockFile { + static final String SUFFIX = ".lock"; //$NON-NLS-1$ + + /** Filter to skip over active lock files when listing a directory. */ + static final FilenameFilter FILTER = new FilenameFilter() { + public boolean accept(File dir, String name) { + return !name.endsWith(SUFFIX); + } + }; + + private final File ref; + + private final File lck; + + private FileLock fLck; + + private boolean haveLck; + + private FileOutputStream os; + + private boolean needStatInformation; + + private long commitLastModified; + + /** + * Create a new lock for any file. + * + * @param f + * the file that will be locked. + */ + public LockFile(final File f) { + ref = f; + lck = new File(ref.getParentFile(), ref.getName() + SUFFIX); + } + + /** + * Try to establish the lock. + * + * @return true if the lock is now held by the caller; false if it is held + * by someone else. + * @throws IOException + * the temporary output file could not be created. The caller + * does not hold the lock. + */ + public boolean lock() throws IOException { + lck.getParentFile().mkdirs(); + if (lck.createNewFile()) { + haveLck = true; + try { + os = new FileOutputStream(lck); + try { + fLck = os.getChannel().tryLock(); + if (fLck == null) + throw new OverlappingFileLockException(); + } catch (OverlappingFileLockException ofle) { + // We cannot use unlock() here as this file is not + // held by us, but we thought we created it. We must + // not delete it, as it belongs to some other process. + // + haveLck = false; + try { + os.close(); + } catch (IOException ioe) { + // Fail by returning haveLck = false. + } + os = null; + } + } catch (IOException ioe) { + unlock(); + throw ioe; + } + } + return haveLck; + } + + /** + * Try to establish the lock for appending. + * + * @return true if the lock is now held by the caller; false if it is held + * by someone else. + * @throws IOException + * the temporary output file could not be created. The caller + * does not hold the lock. + */ + public boolean lockForAppend() throws IOException { + if (!lock()) + return false; + copyCurrentContent(); + return true; + } + + /** + * Copy the current file content into the temporary file. + *

+ * This method saves the current file content by inserting it into the + * temporary file, so that the caller can safely append rather than replace + * the primary file. + *

+ * This method does nothing if the current file does not exist, or exists + * but is empty. + * + * @throws IOException + * the temporary file could not be written, or a read error + * occurred while reading from the current file. The lock is + * released before throwing the underlying IO exception to the + * caller. + * @throws RuntimeException + * the temporary file could not be written. The lock is released + * before throwing the underlying exception to the caller. + */ + public void copyCurrentContent() throws IOException { + requireLock(); + try { + final FileInputStream fis = new FileInputStream(ref); + try { + final byte[] buf = new byte[2048]; + int r; + while ((r = fis.read(buf)) >= 0) + os.write(buf, 0, r); + } finally { + fis.close(); + } + } catch (FileNotFoundException fnfe) { + // Don't worry about a file that doesn't exist yet, it + // conceptually has no current content to copy. + // + } catch (IOException ioe) { + unlock(); + throw ioe; + } catch (RuntimeException ioe) { + unlock(); + throw ioe; + } catch (Error ioe) { + unlock(); + throw ioe; + } + } + + /** + * Write an ObjectId and LF to the temporary file. + * + * @param id + * the id to store in the file. The id will be written in hex, + * followed by a sole LF. + * @throws IOException + * the temporary file could not be written. The lock is released + * before throwing the underlying IO exception to the caller. + * @throws RuntimeException + * the temporary file could not be written. The lock is released + * before throwing the underlying exception to the caller. + */ + public void write(final ObjectId id) throws IOException { + requireLock(); + try { + final BufferedOutputStream b; + b = new BufferedOutputStream(os, Constants.OBJECT_ID_STRING_LENGTH + 1); + id.copyTo(b); + b.write('\n'); + b.flush(); + fLck.release(); + b.close(); + os = null; + } catch (IOException ioe) { + unlock(); + throw ioe; + } catch (RuntimeException ioe) { + unlock(); + throw ioe; + } catch (Error ioe) { + unlock(); + throw ioe; + } + } + + /** + * Write arbitrary data to the temporary file. + * + * @param content + * the bytes to store in the temporary file. No additional bytes + * are added, so if the file must end with an LF it must appear + * at the end of the byte array. + * @throws IOException + * the temporary file could not be written. The lock is released + * before throwing the underlying IO exception to the caller. + * @throws RuntimeException + * the temporary file could not be written. The lock is released + * before throwing the underlying exception to the caller. + */ + public void write(final byte[] content) throws IOException { + requireLock(); + try { + os.write(content); + os.flush(); + fLck.release(); + os.close(); + os = null; + } catch (IOException ioe) { + unlock(); + throw ioe; + } catch (RuntimeException ioe) { + unlock(); + throw ioe; + } catch (Error ioe) { + unlock(); + throw ioe; + } + } + + /** + * Obtain the direct output stream for this lock. + *

+ * The stream may only be accessed once, and only after {@link #lock()} has + * been successfully invoked and returned true. Callers must close the + * stream prior to calling {@link #commit()} to commit the change. + * + * @return a stream to write to the new file. The stream is unbuffered. + */ + public OutputStream getOutputStream() { + requireLock(); + return new OutputStream() { + @Override + public void write(final byte[] b, final int o, final int n) + throws IOException { + os.write(b, o, n); + } + + @Override + public void write(final byte[] b) throws IOException { + os.write(b); + } + + @Override + public void write(final int b) throws IOException { + os.write(b); + } + + @Override + public void flush() throws IOException { + os.flush(); + } + + @Override + public void close() throws IOException { + try { + os.flush(); + fLck.release(); + os.close(); + os = null; + } catch (IOException ioe) { + unlock(); + throw ioe; + } catch (RuntimeException ioe) { + unlock(); + throw ioe; + } catch (Error ioe) { + unlock(); + throw ioe; + } + } + }; + } + + private void requireLock() { + if (os == null) { + unlock(); + throw new IllegalStateException(MessageFormat.format(JGitText.get().lockOnNotHeld, ref)); + } + } + + /** + * Request that {@link #commit()} remember modification time. + * + * @param on + * true if the commit method must remember the modification time. + */ + public void setNeedStatInformation(final boolean on) { + needStatInformation = on; + } + + /** + * Wait until the lock file information differs from the old file. + *

+ * This method tests both the length and the last modification date. If both + * are the same, this method sleeps until it can force the new lock file's + * modification date to be later than the target file. + * + * @throws InterruptedException + * the thread was interrupted before the last modified date of + * the lock file was different from the last modified date of + * the target file. + */ + public void waitForStatChange() throws InterruptedException { + if (ref.length() == lck.length()) { + long otime = ref.lastModified(); + long ntime = lck.lastModified(); + while (otime == ntime) { + Thread.sleep(25 /* milliseconds */); + lck.setLastModified(System.currentTimeMillis()); + ntime = lck.lastModified(); + } + } + } + + /** + * Commit this change and release the lock. + *

+ * If this method fails (returns false) the lock is still released. + * + * @return true if the commit was successful and the file contains the new + * data; false if the commit failed and the file remains with the + * old data. + * @throws IllegalStateException + * the lock is not held. + */ + public boolean commit() { + if (os != null) { + unlock(); + throw new IllegalStateException(MessageFormat.format(JGitText.get().lockOnNotClosed, ref)); + } + + saveStatInformation(); + if (lck.renameTo(ref)) + return true; + if (!ref.exists() || ref.delete()) + if (lck.renameTo(ref)) + return true; + unlock(); + return false; + } + + private void saveStatInformation() { + if (needStatInformation) + commitLastModified = lck.lastModified(); + } + + /** + * Get the modification time of the output file when it was committed. + * + * @return modification time of the lock file right before we committed it. + */ + public long getCommitLastModified() { + return commitLastModified; + } + + /** + * Unlock this file and abort this change. + *

+ * The temporary file (if created) is deleted before returning. + */ + public void unlock() { + if (os != null) { + if (fLck != null) { + try { + fLck.release(); + } catch (IOException ioe) { + // Huh? + } + fLck = null; + } + try { + os.close(); + } catch (IOException ioe) { + // Ignore this + } + os = null; + } + + if (haveLck) { + haveLck = false; + lck.delete(); + } + } + + @Override + public String toString() { + return "LockFile[" + lck + ", haveLck=" + haveLck + "]"; + } +} diff --git a/org.eclipse.jgit/src/org/eclipse/jgit/storage/file/ObjectDirectory.java b/org.eclipse.jgit/src/org/eclipse/jgit/storage/file/ObjectDirectory.java new file mode 100644 index 0000000000..8a8055605a --- /dev/null +++ b/org.eclipse.jgit/src/org/eclipse/jgit/storage/file/ObjectDirectory.java @@ -0,0 +1,630 @@ +/* + * 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.BufferedReader; +import java.io.File; +import java.io.FileNotFoundException; +import java.io.FileReader; +import java.io.IOException; +import java.text.MessageFormat; +import java.util.ArrayList; +import java.util.Arrays; +import java.util.Collection; +import java.util.Collections; +import java.util.HashMap; +import java.util.HashSet; +import java.util.List; +import java.util.Map; +import java.util.Set; +import java.util.concurrent.atomic.AtomicReference; + +import org.eclipse.jgit.JGitText; +import org.eclipse.jgit.errors.PackMismatchException; +import org.eclipse.jgit.lib.AnyObjectId; +import org.eclipse.jgit.lib.Config; +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.ObjectToPack; +import org.eclipse.jgit.lib.PackWriter; +import org.eclipse.jgit.lib.RepositoryCache; +import org.eclipse.jgit.lib.RepositoryCache.FileKey; +import org.eclipse.jgit.util.FS; + +/** + * Traditional file system based {@link ObjectDatabase}. + *

+ * This is the classical object database representation for a Git repository, + * 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. + *

+ * Optionally an object database can reference one or more alternates; other + * ObjectDatabase instances that are searched in addition to the current + * database. + *

+ * 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 FileObjectDatabase { + 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; + + private final File packDirectory; + + private final File alternatesFile; + + private final AtomicReference packList; + + private final FS fs; + + private final AtomicReference alternates; + + /** + * Initialize a reference to an on-disk object directory. + * + * @param cfg + * configuration this directory consults for write settings. + * @param dir + * the location of the objects directory. + * @param alternatePaths + * a list of alternate object directories + * @param fs + * 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 Config cfg, final File dir, + File[] alternatePaths, FS fs) throws IOException { + config = cfg; + objects = dir; + infoDirectory = new File(objects, "info"); + packDirectory = new File(objects, "pack"); + alternatesFile = new File(infoDirectory, "alternates"); + packList = new AtomicReference(NO_PACKS); + this.fs = fs; + + alternates = new AtomicReference(); + 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); + } + } + + /** + * @return the location of the objects directory. + */ + public final File getDirectory() { + return objects; + } + + @Override + public boolean exists() { + return objects.exists(); + } + + @Override + public void create() throws IOException { + objects.mkdirs(); + infoDirectory.mkdir(); + packDirectory.mkdir(); + } + + @Override + 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(); + } + } + + /** + * Compute the location of a loose object file. + * + * @param objectId + * identity of the loose object to map to the directory. + * @return location of the object, if it were to exist as a loose object. + */ + public File fileFor(final AnyObjectId objectId) { + return fileFor(objectId.name()); + } + + private File fileFor(final String objectName) { + final String d = objectName.substring(0, 2); + final String f = objectName.substring(2); + return new File(new File(objects, d), f); + } + + /** + * @return unmodifiable collection of all known pack files local to this + * directory. Most recent packs are presented first. Packs most + * likely to contain more recent objects appear before packs + * containing objects referenced by commits further back in the + * history of the repository. + */ + public Collection getPacks() { + final PackFile[] packs = packList.get().packs; + return Collections.unmodifiableCollection(Arrays.asList(packs)); + } + + /** + * 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 { + final String p = pack.getName(); + final String i = idx.getName(); + + if (p.length() != 50 || !p.startsWith("pack-") || !p.endsWith(".pack")) + throw new IOException(MessageFormat.format(JGitText.get().notAValidPack, pack)); + + if (i.length() != 49 || !i.startsWith("pack-") || !i.endsWith(".idx")) + throw new IOException(MessageFormat.format(JGitText.get().notAValidPack, idx)); + + if (!p.substring(0, 45).equals(i.substring(0, 45))) + throw new IOException(MessageFormat.format(JGitText.get().packDoesNotMatchIndex, pack)); + + insertPack(new PackFile(idx, pack)); + } + + @Override + public String toString() { + return "ObjectDirectory[" + getDirectory() + "]"; + } + + boolean hasObject1(final AnyObjectId objectId) { + for (final PackFile p : packList.get().packs) { + try { + if (p.hasObject(objectId)) { + return true; + } + } catch (IOException e) { + // The hasObject call should have only touched the index, + // so any failure here indicates the index is unreadable + // by this process, and the pack is likewise not readable. + // + removePack(p); + continue; + } + } + return false; + } + + 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); + return ldr; + } + } 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); + } + } + return null; + } + } + + @Override + void selectObjectRepresentation(PackWriter packer, ObjectToPack otp, + WindowCursor curs) throws IOException { + PackList pList = packList.get(); + SEARCH: for (;;) { + for (final PackFile p : pList.packs) { + try { + PackedObjectLoader ldr = p.get(curs, otp); + if (ldr != null) + packer.select(otp, new LocalObjectRepresentation(ldr)); + } 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); + } catch (FileNotFoundException noFile) { + return null; + } + } + + boolean tryAgain1() { + final PackList old = packList.get(); + if (old.tryAgain(packDirectory.lastModified())) + return old != scanPacks(old); + return false; + } + + private void insertPack(final PackFile pf) { + PackList o, n; + do { + o = packList.get(); + + // If the pack in question is already present in the list + // (picked up by a concurrent thread that did a scan?) we + // do not want to insert it a second time. + // + final PackFile[] oldList = o.packs; + final String name = pf.getPackFile().getName(); + for (PackFile p : oldList) { + if (PackFile.SORT.compare(pf, p) < 0) + break; + if (name.equals(p.getPackFile().getName())) + return; + } + + final PackFile[] newList = new PackFile[1 + oldList.length]; + newList[0] = pf; + System.arraycopy(oldList, 0, newList, 1, oldList.length); + n = new PackList(o.lastRead, o.lastModified, newList); + } while (!packList.compareAndSet(o, n)); + } + + private void removePack(final PackFile deadPack) { + PackList o, n; + do { + o = packList.get(); + + final PackFile[] oldList = o.packs; + final int j = indexOf(oldList, deadPack); + if (j < 0) + break; + + final PackFile[] newList = new PackFile[oldList.length - 1]; + System.arraycopy(oldList, 0, newList, 0, j); + System.arraycopy(oldList, j + 1, newList, j, newList.length - j); + n = new PackList(o.lastRead, o.lastModified, newList); + } while (!packList.compareAndSet(o, n)); + deadPack.close(); + } + + private static int indexOf(final PackFile[] list, final PackFile pack) { + for (int i = 0; i < list.length; i++) { + if (list[i] == pack) + return i; + } + return -1; + } + + private PackList scanPacks(final PackList original) { + synchronized (packList) { + PackList o, n; + do { + o = packList.get(); + if (o != original) { + // Another thread did the scan for us, while we + // were blocked on the monitor above. + // + return o; + } + n = scanPacksImpl(o); + if (n == o) + return n; + } while (!packList.compareAndSet(o, n)); + return n; + } + } + + private PackList scanPacksImpl(final PackList old) { + final Map forReuse = reuseMap(old); + final long lastRead = System.currentTimeMillis(); + final long lastModified = packDirectory.lastModified(); + final Set names = listPackDirectory(); + final List list = new ArrayList(names.size() >> 2); + boolean foundNew = false; + for (final String indexName : names) { + // Must match "pack-[0-9a-f]{40}.idx" to be an index. + // + if (indexName.length() != 49 || !indexName.endsWith(".idx")) + continue; + + final String base = indexName.substring(0, indexName.length() - 4); + final String packName = base + ".pack"; + if (!names.contains(packName)) { + // Sometimes C Git's HTTP fetch transport leaves a + // .idx file behind and does not download the .pack. + // We have to skip over such useless indexes. + // + continue; + } + + final PackFile oldPack = forReuse.remove(packName); + if (oldPack != null) { + list.add(oldPack); + continue; + } + + final File packFile = new File(packDirectory, packName); + final File idxFile = new File(packDirectory, indexName); + list.add(new PackFile(idxFile, packFile)); + foundNew = true; + } + + // If we did not discover any new files, the modification time was not + // changed, and we did not remove any files, then the set of files is + // the same as the set we were given. Instead of building a new object + // return the same collection. + // + if (!foundNew && lastModified == old.lastModified && forReuse.isEmpty()) + return old.updateLastRead(lastRead); + + for (final PackFile p : forReuse.values()) { + p.close(); + } + + if (list.isEmpty()) + return new PackList(lastRead, lastModified, NO_PACKS.packs); + + final PackFile[] r = list.toArray(new PackFile[list.size()]); + Arrays.sort(r, PackFile.SORT); + return new PackList(lastRead, lastModified, r); + } + + private static Map reuseMap(final PackList old) { + final Map forReuse = new HashMap(); + for (final PackFile p : old.packs) { + if (p.invalid()) { + // The pack instance is corrupted, and cannot be safely used + // again. Do not include it in our reuse map. + // + p.close(); + continue; + } + + final PackFile prior = forReuse.put(p.getPackFile().getName(), p); + if (prior != null) { + // This should never occur. It should be impossible for us + // to have two pack files with the same name, as all of them + // came out of the same directory. If it does, we promised to + // close any PackFiles we did not reuse, so close the second, + // readers are likely to be actively using the first. + // + forReuse.put(prior.getPackFile().getName(), prior); + p.close(); + } + } + return forReuse; + } + + private Set listPackDirectory() { + final String[] nameList = packDirectory.list(); + if (nameList == null) + return Collections.emptySet(); + final Set nameSet = new HashSet(nameList.length << 1); + for (final String name : nameList) { + if (name.startsWith("pack-")) + nameSet.add(name); + } + return nameSet; + } + + 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); + } + } + } + return alt; + } + + private AlternateHandle[] loadAlternates() throws IOException { + final List l = new ArrayList(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 AlternateHandle[l.size()]); + } + + private static BufferedReader open(final File f) + throws FileNotFoundException { + return new BufferedReader(new FileReader(f)); + } + + private AlternateHandle openAlternate(final String location) + throws IOException { + final File objdir = fs.resolve(objects, location); + return openAlternate(objdir); + } + + private AlternateHandle openAlternate(File objdir) throws IOException { + final File parent = objdir.getParentFile(); + if (FileKey.isGitRepository(parent, fs)) { + FileKey key = FileKey.exact(parent, fs); + FileRepository db = (FileRepository) RepositoryCache.open(key); + return new AlternateRepository(db); + } + + ObjectDirectory db = new ObjectDirectory(config, objdir, null, fs); + return new AlternateHandle(db); + } + + private static final class PackList { + /** Last wall-clock time the directory was read. */ + volatile long lastRead; + + /** Last modification time of {@link ObjectDirectory#packDirectory}. */ + final long lastModified; + + /** All known packs, sorted by {@link PackFile#SORT}. */ + final PackFile[] packs; + + private boolean cannotBeRacilyClean; + + PackList(final long lastRead, final long lastModified, + final PackFile[] packs) { + this.lastRead = lastRead; + this.lastModified = lastModified; + this.packs = packs; + this.cannotBeRacilyClean = notRacyClean(lastRead); + } + + private boolean notRacyClean(final long read) { + return read - lastModified > 2 * 60 * 1000L; + } + + PackList updateLastRead(final long now) { + if (notRacyClean(now)) + cannotBeRacilyClean = true; + lastRead = now; + return this; + } + + boolean tryAgain(final long currLastModified) { + // Any difference indicates the directory was modified. + // + if (lastModified != currLastModified) + return true; + + // We have already determined the last read was far enough + // after the last modification that any new modifications + // are certain to change the last modified time. + // + if (cannotBeRacilyClean) + return false; + + if (notRacyClean(lastRead)) { + // Our last read should have marked cannotBeRacilyClean, + // but this thread may not have seen the change. The read + // of the volatile field lastRead should have fixed that. + // + return false; + } + + // We last read this directory too close to its last observed + // modification time. We may have missed a modification. Scan + // the directory again, to ensure we still see the same state. + // + return true; + } + } + + @Override + public ObjectDatabase newCachedDatabase() { + return newCachedFileObjectDatabase(); + } + + FileObjectDatabase newCachedFileObjectDatabase() { + return new CachedObjectDirectory(this); + } +} 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..e6ed54022f --- /dev/null +++ b/org.eclipse.jgit/src/org/eclipse/jgit/storage/file/ObjectDirectoryInserter.java @@ -0,0 +1,182 @@ +/* + * Copyright (C) 2007, Robin Rosenberg + * Copyright (C) 2008, Shawn O. Pearce + * 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.hasObject(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.hasObject(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..e5f6f03f40 --- /dev/null +++ b/org.eclipse.jgit/src/org/eclipse/jgit/storage/file/PackFile.java @@ -0,0 +1,700 @@ +/* + * Copyright (C) 2008-2009, Google Inc. + * Copyright (C) 2007, Robin Rosenberg + * Copyright (C) 2006-2008, Shawn O. Pearce + * 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.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.ObjectToPack; +import org.eclipse.jgit.lib.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 { + /** Sorts PackFiles to be most recently created to least recently created. */ + public static Comparator SORT = new Comparator() { + 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. + *

+ * 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 .idx file listing the contents. + * @param packFile + * path of the .pack 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 { + if (isCorrupt(ofs)) { + throw new CorruptObjectException(MessageFormat.format(JGitText + .get().objectAtHasBadZlibStream, ofs, getPackFile())); + } + 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. + *

+ * For performance reasons only the index file is searched; the main pack + * content is ignored entirely. + *

+ * + * @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. + */ + public PackedObjectLoader get(final WindowCursor curs, final AnyObjectId id) + throws IOException { + final long offset = idx().findOffset(id); + return 0 < offset && !isCorrupt(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. + *

+ * Iterator returns objects in SHA-1 lexicographical order. + *

+ * + * @return iterator over entries of associated pack index + * + * @see PackIndex#iterator() + */ + public Iterator iterator() { + try { + return idx().iterator(); + } catch (IOException e) { + return Collections. 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 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.copyOffset, 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.copyOffset + 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.copyOffset + headerCnt; + final long dataLength; + final long expectedCRC; + final ByteArrayWindow quickCopy; + + // Verify the object isn't corrupt before sending. If it is, + // we report it missing instead. + // + try { + dataLength = findEndOffset(src.copyOffset) - dataOffset; + 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.copyOffset); + throw new CorruptObjectException(MessageFormat.format( + JGitText.get().objectAtHasBadZlibStream, + src.copyOffset, 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.copyOffset); + throw new EOFException(MessageFormat.format( + JGitText.get().shortCompressedStreamAt, + src.copyOffset)); + } + expectedCRC = crc1.getValue(); + } + } catch (DataFormatException dataFormat) { + setCorrupt(src.copyOffset); + + CorruptObjectException corruptObject = new CorruptObjectException( + MessageFormat.format( + JGitText.get().objectAtHasBadZlibStream, + src.copyOffset, 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.copyOffset, + 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())); + } + + 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; + } + + 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/storage/file/PackIndex.java b/org.eclipse.jgit/src/org/eclipse/jgit/storage/file/PackIndex.java new file mode 100644 index 0000000000..62d1c9d8f8 --- /dev/null +++ b/org.eclipse.jgit/src/org/eclipse/jgit/storage/file/PackIndex.java @@ -0,0 +1,318 @@ +/* + * Copyright (C) 2008, Marek Zawirski + * Copyright (C) 2008, Shawn O. Pearce + * 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.FileInputStream; +import java.io.FileNotFoundException; +import java.io.IOException; +import java.text.MessageFormat; +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; + +/** + * Access path to locate objects by {@link ObjectId} in a {@link PackFile}. + *

+ * Indexes are strictly redundant information in that we can rebuild all of the + * data held in the index file from the on disk representation of the pack file + * itself, but it is faster to access for random requests because data is stored + * by ObjectId. + *

+ */ +public abstract class PackIndex implements Iterable { + /** + * Open an existing pack .idx file for reading. + *

+ * The format of the file will be automatically detected and a proper access + * implementation for that format will be constructed and returned to the + * caller. The file may or may not be held open by the returned instance. + *

+ * + * @param idxFile + * existing pack .idx to read. + * @return access implementation for the requested file. + * @throws FileNotFoundException + * the file does not exist. + * @throws IOException + * the file exists but could not be read due to security errors, + * unrecognized data version, or unexpected data corruption. + */ + public static PackIndex open(final File idxFile) throws IOException { + final FileInputStream fd = new FileInputStream(idxFile); + try { + final byte[] hdr = new byte[8]; + IO.readFully(fd, hdr, 0, hdr.length); + if (isTOC(hdr)) { + final int v = NB.decodeInt32(hdr, 4); + switch (v) { + case 2: + return new PackIndexV2(fd); + default: + throw new IOException(MessageFormat.format(JGitText.get().unsupportedPackIndexVersion, v)); + } + } + return new PackIndexV1(fd, hdr); + } catch (IOException ioe) { + final String path = idxFile.getAbsolutePath(); + final IOException err; + err = new IOException(MessageFormat.format(JGitText.get().unreadablePackIndex, path)); + err.initCause(ioe); + throw err; + } finally { + try { + fd.close(); + } catch (IOException err2) { + // ignore + } + } + } + + private static boolean isTOC(final byte[] h) { + final byte[] toc = PackIndexWriter.TOC; + for (int i = 0; i < toc.length; i++) + if (h[i] != toc[i]) + return false; + return true; + } + + /** Footer checksum applied on the bottom of the pack file. */ + protected byte[] packChecksum; + + /** + * Determine if an object is contained within the pack file. + * + * @param id + * the object to look for. Must not be null. + * @return true if the object is listed in this index; false otherwise. + */ + public boolean hasObject(final AnyObjectId id) { + return findOffset(id) != -1; + } + + /** + * Provide iterator that gives access to index entries. Note, that iterator + * returns reference to mutable object, the same reference in each call - + * for performance reason. If client needs immutable objects, it must copy + * returned object on its own. + *

+ * Iterator returns objects in SHA-1 lexicographical order. + *

+ * + * @return iterator over pack index entries + */ + public abstract Iterator iterator(); + + /** + * Obtain the total number of objects described by this index. + * + * @return number of objects in this index, and likewise in the associated + * pack that this index was generated from. + */ + abstract long getObjectCount(); + + /** + * Obtain the total number of objects needing 64 bit offsets. + * + * @return number of objects in this index using a 64 bit offset; that is an + * object positioned after the 2 GB position within the file. + */ + abstract long getOffset64Count(); + + /** + * Get ObjectId for the n-th object entry returned by {@link #iterator()}. + *

+ * This method is a constant-time replacement for the following loop: + * + *

+	 * Iterator<MutableEntry> eItr = index.iterator();
+	 * int curPosition = 0;
+	 * while (eItr.hasNext() && curPosition++ < nthPosition)
+	 * 	eItr.next();
+	 * ObjectId result = eItr.next().toObjectId();
+	 * 
+ * + * @param nthPosition + * position within the traversal of {@link #iterator()} that the + * caller needs the object for. The first returned + * {@link MutableEntry} is 0, the second is 1, etc. + * @return the ObjectId for the corresponding entry. + */ + abstract ObjectId getObjectId(long nthPosition); + + /** + * Get ObjectId for the n-th object entry returned by {@link #iterator()}. + *

+ * This method is a constant-time replacement for the following loop: + * + *

+	 * Iterator<MutableEntry> eItr = index.iterator();
+	 * int curPosition = 0;
+	 * while (eItr.hasNext() && curPosition++ < nthPosition)
+	 * 	eItr.next();
+	 * ObjectId result = eItr.next().toObjectId();
+	 * 
+ * + * @param nthPosition + * unsigned 32 bit position within the traversal of + * {@link #iterator()} that the caller needs the object for. The + * first returned {@link MutableEntry} is 0, the second is 1, + * etc. Positions past 2**31-1 are negative, but still valid. + * @return the ObjectId for the corresponding entry. + */ + final ObjectId getObjectId(final int nthPosition) { + if (nthPosition >= 0) + return getObjectId((long) nthPosition); + final int u31 = nthPosition >>> 1; + final int one = nthPosition & 1; + return getObjectId(((long) u31) << 1 | one); + } + + /** + * Locate the file offset position for the requested object. + * + * @param objId + * name of the object to locate within the pack. + * @return offset of the object's header and compressed content; -1 if the + * object does not exist in this index and is thus not stored in the + * associated pack. + */ + abstract long findOffset(AnyObjectId objId); + + /** + * Retrieve stored CRC32 checksum of the requested object raw-data + * (including header). + * + * @param objId + * id of object to look for + * @return CRC32 checksum of specified object (at 32 less significant bits) + * @throws MissingObjectException + * when requested ObjectId was not found in this index + * @throws UnsupportedOperationException + * when this index doesn't support CRC32 checksum + */ + abstract long findCRC32(AnyObjectId objId) throws MissingObjectException, + UnsupportedOperationException; + + /** + * Check whether this index supports (has) CRC32 checksums for objects. + * + * @return true if CRC32 is stored, false otherwise + */ + abstract boolean hasCRC32Support(); + + /** + * Represent mutable entry of pack index consisting of object id and offset + * in pack (both mutable). + * + */ + public static class MutableEntry { + final MutableObjectId idBuffer = new MutableObjectId(); + + long offset; + + /** + * Returns offset for this index object entry + * + * @return offset of this object in a pack file + */ + public long getOffset() { + return offset; + } + + /** @return hex string describing the object id of this entry. */ + public String name() { + ensureId(); + return idBuffer.name(); + } + + /** @return a copy of the object id. */ + public ObjectId toObjectId() { + ensureId(); + return idBuffer.toObjectId(); + } + + /** @return a complete copy of this entry, that won't modify */ + public MutableEntry cloneEntry() { + final MutableEntry r = new MutableEntry(); + ensureId(); + r.idBuffer.fromObjectId(idBuffer); + r.offset = offset; + return r; + } + + void ensureId() { + // Override in implementations. + } + } + + abstract class EntriesIterator implements Iterator { + protected final MutableEntry entry = initEntry(); + + protected long returnedNumber = 0; + + protected abstract MutableEntry initEntry(); + + public boolean hasNext() { + return returnedNumber < getObjectCount(); + } + + /** + * Implementation must update {@link #returnedNumber} before returning + * element. + */ + public abstract MutableEntry next(); + + public void remove() { + throw new UnsupportedOperationException(); + } + } +} diff --git a/org.eclipse.jgit/src/org/eclipse/jgit/storage/file/PackIndexV1.java b/org.eclipse.jgit/src/org/eclipse/jgit/storage/file/PackIndexV1.java new file mode 100644 index 0000000000..3b68edc191 --- /dev/null +++ b/org.eclipse.jgit/src/org/eclipse/jgit/storage/file/PackIndexV1.java @@ -0,0 +1,205 @@ +/* + * Copyright (C) 2008-2009, Google Inc. + * Copyright (C) 2008, Marek Zawirski + * Copyright (C) 2007-2009, Robin Rosenberg + * Copyright (C) 2006-2008, Shawn O. Pearce + * 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; +import java.util.Arrays; +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; + +class PackIndexV1 extends PackIndex { + private static final int IDX_HDR_LEN = 256 * 4; + + private final long[] idxHeader; + + private byte[][] idxdata; + + private long objectCnt; + + PackIndexV1(final InputStream fd, final byte[] hdr) + throws CorruptObjectException, IOException { + final byte[] fanoutTable = new byte[IDX_HDR_LEN]; + System.arraycopy(hdr, 0, fanoutTable, 0, hdr.length); + IO.readFully(fd, fanoutTable, hdr.length, IDX_HDR_LEN - hdr.length); + + idxHeader = new long[256]; // really unsigned 32-bit... + for (int k = 0; k < idxHeader.length; k++) + idxHeader[k] = NB.decodeUInt32(fanoutTable, k * 4); + idxdata = new byte[idxHeader.length][]; + for (int k = 0; k < idxHeader.length; k++) { + int n; + if (k == 0) { + n = (int) (idxHeader[k]); + } else { + n = (int) (idxHeader[k] - idxHeader[k - 1]); + } + if (n > 0) { + idxdata[k] = new byte[n * (Constants.OBJECT_ID_LENGTH + 4)]; + IO.readFully(fd, idxdata[k], 0, idxdata[k].length); + } + } + objectCnt = idxHeader[255]; + + packChecksum = new byte[20]; + IO.readFully(fd, packChecksum, 0, packChecksum.length); + } + + long getObjectCount() { + return objectCnt; + } + + @Override + long getOffset64Count() { + long n64 = 0; + for (final MutableEntry e : this) { + if (e.getOffset() >= Integer.MAX_VALUE) + n64++; + } + return n64; + } + + @Override + ObjectId getObjectId(final long nthPosition) { + int levelOne = Arrays.binarySearch(idxHeader, nthPosition + 1); + long base; + if (levelOne >= 0) { + // If we hit the bucket exactly the item is in the bucket, or + // any bucket before it which has the same object count. + // + base = idxHeader[levelOne]; + while (levelOne > 0 && base == idxHeader[levelOne - 1]) + levelOne--; + } else { + // The item is in the bucket we would insert it into. + // + levelOne = -(levelOne + 1); + } + + base = levelOne > 0 ? idxHeader[levelOne - 1] : 0; + final int p = (int) (nthPosition - base); + final int dataIdx = ((4 + Constants.OBJECT_ID_LENGTH) * p) + 4; + return ObjectId.fromRaw(idxdata[levelOne], dataIdx); + } + + long findOffset(final AnyObjectId objId) { + final int levelOne = objId.getFirstByte(); + byte[] data = idxdata[levelOne]; + if (data == null) + return -1; + int high = data.length / (4 + Constants.OBJECT_ID_LENGTH); + int low = 0; + do { + final int mid = (low + high) >>> 1; + final int pos = ((4 + Constants.OBJECT_ID_LENGTH) * mid) + 4; + final int cmp = objId.compareTo(data, pos); + if (cmp < 0) + high = mid; + else if (cmp == 0) { + int b0 = data[pos - 4] & 0xff; + int b1 = data[pos - 3] & 0xff; + int b2 = data[pos - 2] & 0xff; + int b3 = data[pos - 1] & 0xff; + return (((long) b0) << 24) | (b1 << 16) | (b2 << 8) | (b3); + } else + low = mid + 1; + } while (low < high); + return -1; + } + + @Override + long findCRC32(AnyObjectId objId) { + throw new UnsupportedOperationException(); + } + + @Override + boolean hasCRC32Support() { + return false; + } + + public Iterator iterator() { + return new IndexV1Iterator(); + } + + private class IndexV1Iterator extends EntriesIterator { + private int levelOne; + + private int levelTwo; + + @Override + protected MutableEntry initEntry() { + return new MutableEntry() { + protected void ensureId() { + idBuffer.fromRaw(idxdata[levelOne], levelTwo + - Constants.OBJECT_ID_LENGTH); + } + }; + } + + public MutableEntry next() { + for (; levelOne < idxdata.length; levelOne++) { + if (idxdata[levelOne] == null) + continue; + if (levelTwo < idxdata[levelOne].length) { + entry.offset = NB.decodeUInt32(idxdata[levelOne], levelTwo); + levelTwo += Constants.OBJECT_ID_LENGTH + 4; + returnedNumber++; + return entry; + } + levelTwo = 0; + } + throw new NoSuchElementException(); + } + } +} diff --git a/org.eclipse.jgit/src/org/eclipse/jgit/storage/file/PackIndexV2.java b/org.eclipse.jgit/src/org/eclipse/jgit/storage/file/PackIndexV2.java new file mode 100644 index 0000000000..cef7cc429e --- /dev/null +++ b/org.eclipse.jgit/src/org/eclipse/jgit/storage/file/PackIndexV2.java @@ -0,0 +1,282 @@ +/* + * Copyright (C) 2008, Shawn O. Pearce + * 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; +import java.util.Arrays; +import java.util.Iterator; +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; + +/** Support for the pack index v2 format. */ +class PackIndexV2 extends PackIndex { + private static final long IS_O64 = 1L << 31; + + private static final int FANOUT = 256; + + private static final int[] NO_INTS = {}; + + private static final byte[] NO_BYTES = {}; + + private long objectCnt; + + private final long[] fanoutTable; + + /** 256 arrays of contiguous object names. */ + private int[][] names; + + /** 256 arrays of the 32 bit offset data, matching {@link #names}. */ + private byte[][] offset32; + + /** 256 arrays of the CRC-32 of objects, matching {@link #names}. */ + private byte[][] crc32; + + /** 64 bit offset table. */ + private byte[] offset64; + + PackIndexV2(final InputStream fd) throws IOException { + final byte[] fanoutRaw = new byte[4 * FANOUT]; + IO.readFully(fd, fanoutRaw, 0, fanoutRaw.length); + fanoutTable = new long[FANOUT]; + for (int k = 0; k < FANOUT; k++) + fanoutTable[k] = NB.decodeUInt32(fanoutRaw, k * 4); + objectCnt = fanoutTable[FANOUT - 1]; + + names = new int[FANOUT][]; + offset32 = new byte[FANOUT][]; + crc32 = new byte[FANOUT][]; + + // Object name table. The size we can permit per fan-out bucket + // is limited to Java's 2 GB per byte array limitation. That is + // no more than 107,374,182 objects per fan-out. + // + for (int k = 0; k < FANOUT; k++) { + final long bucketCnt; + if (k == 0) + bucketCnt = fanoutTable[k]; + else + bucketCnt = fanoutTable[k] - fanoutTable[k - 1]; + + if (bucketCnt == 0) { + names[k] = NO_INTS; + offset32[k] = NO_BYTES; + crc32[k] = NO_BYTES; + continue; + } + + final long nameLen = bucketCnt * Constants.OBJECT_ID_LENGTH; + if (nameLen > Integer.MAX_VALUE) + throw new IOException(JGitText.get().indexFileIsTooLargeForJgit); + + final int intNameLen = (int) nameLen; + final byte[] raw = new byte[intNameLen]; + final int[] bin = new int[intNameLen >>> 2]; + IO.readFully(fd, raw, 0, raw.length); + for (int i = 0; i < bin.length; i++) + bin[i] = NB.decodeInt32(raw, i << 2); + + names[k] = bin; + offset32[k] = new byte[(int) (bucketCnt * 4)]; + crc32[k] = new byte[(int) (bucketCnt * 4)]; + } + + // CRC32 table. + for (int k = 0; k < FANOUT; k++) + IO.readFully(fd, crc32[k], 0, crc32[k].length); + + // 32 bit offset table. Any entries with the most significant bit + // set require a 64 bit offset entry in another table. + // + int o64cnt = 0; + for (int k = 0; k < FANOUT; k++) { + final byte[] ofs = offset32[k]; + IO.readFully(fd, ofs, 0, ofs.length); + for (int p = 0; p < ofs.length; p += 4) + if (ofs[p] < 0) + o64cnt++; + } + + // 64 bit offset table. Most objects should not require an entry. + // + if (o64cnt > 0) { + offset64 = new byte[o64cnt * 8]; + IO.readFully(fd, offset64, 0, offset64.length); + } else { + offset64 = NO_BYTES; + } + + packChecksum = new byte[20]; + IO.readFully(fd, packChecksum, 0, packChecksum.length); + } + + @Override + long getObjectCount() { + return objectCnt; + } + + @Override + long getOffset64Count() { + return offset64.length / 8; + } + + @Override + ObjectId getObjectId(final long nthPosition) { + int levelOne = Arrays.binarySearch(fanoutTable, nthPosition + 1); + long base; + if (levelOne >= 0) { + // If we hit the bucket exactly the item is in the bucket, or + // any bucket before it which has the same object count. + // + base = fanoutTable[levelOne]; + while (levelOne > 0 && base == fanoutTable[levelOne - 1]) + levelOne--; + } else { + // The item is in the bucket we would insert it into. + // + levelOne = -(levelOne + 1); + } + + base = levelOne > 0 ? fanoutTable[levelOne - 1] : 0; + final int p = (int) (nthPosition - base); + final int p4 = p << 2; + return ObjectId.fromRaw(names[levelOne], p4 + p); // p * 5 + } + + @Override + long findOffset(final AnyObjectId objId) { + final int levelOne = objId.getFirstByte(); + final int levelTwo = binarySearchLevelTwo(objId, levelOne); + if (levelTwo == -1) + return -1; + final long p = NB.decodeUInt32(offset32[levelOne], levelTwo << 2); + if ((p & IS_O64) != 0) + return NB.decodeUInt64(offset64, (8 * (int) (p & ~IS_O64))); + return p; + } + + @Override + long findCRC32(AnyObjectId objId) throws MissingObjectException { + final int levelOne = objId.getFirstByte(); + final int levelTwo = binarySearchLevelTwo(objId, levelOne); + if (levelTwo == -1) + throw new MissingObjectException(objId.copy(), "unknown"); + return NB.decodeUInt32(crc32[levelOne], levelTwo << 2); + } + + @Override + boolean hasCRC32Support() { + return true; + } + + public Iterator iterator() { + return new EntriesIteratorV2(); + } + + private int binarySearchLevelTwo(final AnyObjectId objId, final int levelOne) { + final int[] data = names[levelOne]; + int high = offset32[levelOne].length >>> 2; + if (high == 0) + return -1; + int low = 0; + do { + final int mid = (low + high) >>> 1; + final int mid4 = mid << 2; + final int cmp; + + cmp = objId.compareTo(data, mid4 + mid); // mid * 5 + if (cmp < 0) + high = mid; + else if (cmp == 0) { + return mid; + } else + low = mid + 1; + } while (low < high); + return -1; + } + + private class EntriesIteratorV2 extends EntriesIterator { + private int levelOne; + + private int levelTwo; + + @Override + protected MutableEntry initEntry() { + return new MutableEntry() { + protected void ensureId() { + idBuffer.fromRaw(names[levelOne], levelTwo + - Constants.OBJECT_ID_LENGTH / 4); + } + }; + } + + public MutableEntry next() { + for (; levelOne < names.length; levelOne++) { + if (levelTwo < names[levelOne].length) { + int idx = levelTwo / (Constants.OBJECT_ID_LENGTH / 4) * 4; + long offset = NB.decodeUInt32(offset32[levelOne], idx); + if ((offset & IS_O64) != 0) { + idx = (8 * (int) (offset & ~IS_O64)); + offset = NB.decodeUInt64(offset64, idx); + } + entry.offset = offset; + + levelTwo += Constants.OBJECT_ID_LENGTH / 4; + returnedNumber++; + return entry; + } + levelTwo = 0; + } + throw new NoSuchElementException(); + } + } + +} diff --git a/org.eclipse.jgit/src/org/eclipse/jgit/storage/file/PackIndexWriter.java b/org.eclipse.jgit/src/org/eclipse/jgit/storage/file/PackIndexWriter.java new file mode 100644 index 0000000000..6bd73adcb1 --- /dev/null +++ b/org.eclipse.jgit/src/org/eclipse/jgit/storage/file/PackIndexWriter.java @@ -0,0 +1,277 @@ +/* + * Copyright (C) 2008, Robin Rosenberg + * Copyright (C) 2008, Shawn O. Pearce + * 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.BufferedOutputStream; +import java.io.IOException; +import java.io.OutputStream; +import java.security.DigestOutputStream; +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; + +/** + * Creates a table of contents to support random access by {@link PackFile}. + *

+ * Pack index files (the .idx suffix in a pack file pair) + * provides random access to any object in the pack by associating an ObjectId + * to the byte offset within the pack where the object's data can be read. + */ +public abstract class PackIndexWriter { + /** Magic constant indicating post-version 1 format. */ + protected static final byte[] TOC = { -1, 't', 'O', 'c' }; + + /** + * Create a new writer for the oldest (most widely understood) format. + *

+ * This method selects an index format that can accurate describe the + * supplied objects and that will be the most compatible format with older + * Git implementations. + *

+ * Index version 1 is widely recognized by all Git implementations, but + * index version 2 (and later) is not as well recognized as it was + * introduced more than a year later. Index version 1 can only be used if + * the resulting pack file is under 4 gigabytes in size; packs larger than + * that limit must use index version 2. + * + * @param dst + * the stream the index data will be written to. If not already + * buffered it will be automatically wrapped in a buffered + * stream. Callers are always responsible for closing the stream. + * @param objs + * the objects the caller needs to store in the index. Entries + * will be examined until a format can be conclusively selected. + * @return a new writer to output an index file of the requested format to + * the supplied stream. + * @throws IllegalArgumentException + * no recognized pack index version can support the supplied + * objects. This is likely a bug in the implementation. + */ + @SuppressWarnings("fallthrough") + public static PackIndexWriter createOldestPossible(final OutputStream dst, + final List objs) { + int version = 1; + LOOP: for (final PackedObjectInfo oe : objs) { + switch (version) { + case 1: + if (PackIndexWriterV1.canStore(oe)) + continue; + version = 2; + case 2: + break LOOP; + } + } + return createVersion(dst, version); + } + + /** + * Create a new writer instance for a specific index format version. + * + * @param dst + * the stream the index data will be written to. If not already + * buffered it will be automatically wrapped in a buffered + * stream. Callers are always responsible for closing the stream. + * @param version + * index format version number required by the caller. Exactly + * this formatted version will be written. + * @return a new writer to output an index file of the requested format to + * the supplied stream. + * @throws IllegalArgumentException + * the version requested is not supported by this + * implementation. + */ + public static PackIndexWriter createVersion(final OutputStream dst, + final int version) { + switch (version) { + case 1: + return new PackIndexWriterV1(dst); + case 2: + return new PackIndexWriterV2(dst); + default: + throw new IllegalArgumentException(MessageFormat.format( + JGitText.get().unsupportedPackIndexVersion, version)); + } + } + + /** The index data stream we are responsible for creating. */ + protected final DigestOutputStream out; + + /** A temporary buffer for use during IO to {link #out}. */ + protected final byte[] tmp; + + /** The entries this writer must pack. */ + protected List entries; + + /** SHA-1 checksum for the entire pack data. */ + protected byte[] packChecksum; + + /** + * Create a new writer instance. + * + * @param dst + * the stream this instance outputs to. If not already buffered + * it will be automatically wrapped in a buffered stream. + */ + protected PackIndexWriter(final OutputStream dst) { + out = new DigestOutputStream(dst instanceof BufferedOutputStream ? dst + : new BufferedOutputStream(dst), Constants.newMessageDigest()); + tmp = new byte[4 + Constants.OBJECT_ID_LENGTH]; + } + + /** + * Write all object entries to the index stream. + *

+ * After writing the stream passed to the factory is flushed but remains + * open. Callers are always responsible for closing the output stream. + * + * @param toStore + * sorted list of objects to store in the index. The caller must + * have previously sorted the list using {@link PackedObjectInfo}'s + * native {@link Comparable} implementation. + * @param packDataChecksum + * checksum signature of the entire pack data content. This is + * traditionally the last 20 bytes of the pack file's own stream. + * @throws IOException + * an error occurred while writing to the output stream, or this + * index format cannot store the object data supplied. + */ + public void write(final List toStore, + final byte[] packDataChecksum) throws IOException { + entries = toStore; + packChecksum = packDataChecksum; + writeImpl(); + out.flush(); + } + + /** + * Writes the index file to {@link #out}. + *

+ * Implementations should go something like: + * + *

+	 * writeFanOutTable();
+	 * for (final PackedObjectInfo po : entries)
+	 * 	writeOneEntry(po);
+	 * writeChecksumFooter();
+	 * 
+ * + *

+ * Where the logic for writeOneEntry is specific to the index + * format in use. Additional headers/footers may be used if necessary and + * the {@link #entries} collection may be iterated over more than once if + * necessary. Implementors therefore have complete control over the data. + * + * @throws IOException + * an error occurred while writing to the output stream, or this + * index format cannot store the object data supplied. + */ + protected abstract void writeImpl() throws IOException; + + /** + * Output the version 2 (and later) TOC header, with version number. + *

+ * Post version 1 all index files start with a TOC header that makes the + * file an invalid version 1 file, and then includes the version number. + * This header is necessary to recognize a version 1 from a version 2 + * formatted index. + * + * @param version + * version number of this index format being written. + * @throws IOException + * an error occurred while writing to the output stream. + */ + protected void writeTOC(final int version) throws IOException { + out.write(TOC); + NB.encodeInt32(tmp, 0, version); + out.write(tmp, 0, 4); + } + + /** + * Output the standard 256 entry first-level fan-out table. + *

+ * The fan-out table is 4 KB in size, holding 256 32-bit unsigned integer + * counts. Each count represents the number of objects within this index + * whose {@link ObjectId#getFirstByte()} matches the count's position in the + * fan-out table. + * + * @throws IOException + * an error occurred while writing to the output stream. + */ + protected void writeFanOutTable() throws IOException { + final int[] fanout = new int[256]; + for (final PackedObjectInfo po : entries) + fanout[po.getFirstByte() & 0xff]++; + for (int i = 1; i < 256; i++) + fanout[i] += fanout[i - 1]; + for (final int n : fanout) { + NB.encodeInt32(tmp, 0, n); + out.write(tmp, 0, 4); + } + } + + /** + * Output the standard two-checksum index footer. + *

+ * The standard footer contains two checksums (20 byte SHA-1 values): + *

    + *
  1. Pack data checksum - taken from the last 20 bytes of the pack file.
  2. + *
  3. Index data checksum - checksum of all index bytes written, including + * the pack data checksum above.
  4. + *
+ * + * @throws IOException + * an error occurred while writing to the output stream. + */ + protected void writeChecksumFooter() throws IOException { + out.write(packChecksum); + out.on(false); + out.write(out.getMessageDigest().digest()); + } +} diff --git a/org.eclipse.jgit/src/org/eclipse/jgit/storage/file/PackIndexWriterV1.java b/org.eclipse.jgit/src/org/eclipse/jgit/storage/file/PackIndexWriterV1.java new file mode 100644 index 0000000000..722ab0e06b --- /dev/null +++ b/org.eclipse.jgit/src/org/eclipse/jgit/storage/file/PackIndexWriterV1.java @@ -0,0 +1,85 @@ +/* + * Copyright (C) 2008, Robin Rosenberg + * Copyright (C) 2008, Shawn O. Pearce + * 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.OutputStream; + +import org.eclipse.jgit.JGitText; +import org.eclipse.jgit.transport.PackedObjectInfo; +import org.eclipse.jgit.util.NB; + +/** + * Creates the version 1 (old style) pack table of contents files. + * + * @see PackIndexWriter + * @see PackIndexV1 + */ +class PackIndexWriterV1 extends PackIndexWriter { + static boolean canStore(final PackedObjectInfo oe) { + // We are limited to 4 GB per pack as offset is 32 bit unsigned int. + // + return oe.getOffset() >>> 1 < Integer.MAX_VALUE; + } + + PackIndexWriterV1(final OutputStream dst) { + super(dst); + } + + @Override + protected void writeImpl() throws IOException { + writeFanOutTable(); + + for (final PackedObjectInfo oe : entries) { + if (!canStore(oe)) + throw new IOException(JGitText.get().packTooLargeForIndexVersion1); + NB.encodeInt32(tmp, 0, (int) oe.getOffset()); + oe.copyRawTo(tmp, 4); + out.write(tmp); + } + + writeChecksumFooter(); + } +} diff --git a/org.eclipse.jgit/src/org/eclipse/jgit/storage/file/PackIndexWriterV2.java b/org.eclipse.jgit/src/org/eclipse/jgit/storage/file/PackIndexWriterV2.java new file mode 100644 index 0000000000..21ebd1ca9c --- /dev/null +++ b/org.eclipse.jgit/src/org/eclipse/jgit/storage/file/PackIndexWriterV2.java @@ -0,0 +1,107 @@ +/* + * Copyright (C) 2008, Shawn O. Pearce + * 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.OutputStream; + +import org.eclipse.jgit.transport.PackedObjectInfo; +import org.eclipse.jgit.util.NB; + +/** + * Creates the version 2 pack table of contents files. + * + * @see PackIndexWriter + * @see PackIndexV2 + */ +class PackIndexWriterV2 extends PackIndexWriter { + PackIndexWriterV2(final OutputStream dst) { + super(dst); + } + + @Override + protected void writeImpl() throws IOException { + writeTOC(2); + writeFanOutTable(); + writeObjectNames(); + writeCRCs(); + writeOffset32(); + writeOffset64(); + writeChecksumFooter(); + } + + private void writeObjectNames() throws IOException { + for (final PackedObjectInfo oe : entries) + oe.copyRawTo(out); + } + + private void writeCRCs() throws IOException { + for (final PackedObjectInfo oe : entries) { + NB.encodeInt32(tmp, 0, oe.getCRC()); + out.write(tmp, 0, 4); + } + } + + private void writeOffset32() throws IOException { + int o64 = 0; + for (final PackedObjectInfo oe : entries) { + final long o = oe.getOffset(); + if (o < Integer.MAX_VALUE) + NB.encodeInt32(tmp, 0, (int) o); + else + NB.encodeInt32(tmp, 0, (1 << 31) | o64++); + out.write(tmp, 0, 4); + } + } + + private void writeOffset64() throws IOException { + for (final PackedObjectInfo oe : entries) { + final long o = oe.getOffset(); + if (o > Integer.MAX_VALUE) { + NB.encodeInt64(tmp, 0, o); + out.write(tmp, 0, 8); + } + } + } +} diff --git a/org.eclipse.jgit/src/org/eclipse/jgit/storage/file/PackLock.java b/org.eclipse.jgit/src/org/eclipse/jgit/storage/file/PackLock.java new file mode 100644 index 0000000000..be250114c2 --- /dev/null +++ b/org.eclipse.jgit/src/org/eclipse/jgit/storage/file/PackLock.java @@ -0,0 +1,92 @@ +/* + * 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.File; +import java.io.IOException; + +import org.eclipse.jgit.lib.Constants; + +/** Keeps track of a {@link PackFile}'s associated .keep file. */ +public class PackLock { + private final File keepFile; + + /** + * Create a new lock for a pack file. + * + * @param packFile + * location of the pack-*.pack file. + */ + public PackLock(final File packFile) { + final File p = packFile.getParentFile(); + final String n = packFile.getName(); + keepFile = new File(p, n.substring(0, n.length() - 5) + ".keep"); + } + + /** + * Create the pack-*.keep file, with the given message. + * + * @param msg + * message to store in the file. + * @return true if the keep file was successfully written; false otherwise. + * @throws IOException + * the keep file could not be written. + */ + public boolean lock(String msg) throws IOException { + if (msg == null) + return false; + if (!msg.endsWith("\n")) + msg += "\n"; + final LockFile lf = new LockFile(keepFile); + if (!lf.lock()) + return false; + lf.write(Constants.encode(msg)); + return lf.commit(); + } + + /** Remove the .keep file that holds this pack in place. */ + public void unlock() { + keepFile.delete(); + } +} diff --git a/org.eclipse.jgit/src/org/eclipse/jgit/storage/file/PackReverseIndex.java b/org.eclipse.jgit/src/org/eclipse/jgit/storage/file/PackReverseIndex.java new file mode 100644 index 0000000000..96abaeefd3 --- /dev/null +++ b/org.eclipse.jgit/src/org/eclipse/jgit/storage/file/PackReverseIndex.java @@ -0,0 +1,194 @@ +/* + * Copyright (C) 2008, Marek Zawirski + * 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.text.MessageFormat; +import java.util.Arrays; + +import org.eclipse.jgit.JGitText; +import org.eclipse.jgit.errors.CorruptObjectException; +import org.eclipse.jgit.lib.ObjectId; +import org.eclipse.jgit.storage.file.PackIndex.MutableEntry; + +/** + *

+ * Reverse index for forward pack index. Provides operations based on offset + * instead of object id. Such offset-based reverse lookups are performed in + * O(log n) time. + *

+ * + * @see PackIndex + * @see PackFile + */ +class PackReverseIndex { + /** Index we were created from, and that has our ObjectId data. */ + private final PackIndex index; + + /** + * (offset31, truly) Offsets accommodating in 31 bits. + */ + private final int offsets32[]; + + /** + * Offsets not accommodating in 31 bits. + */ + private final long offsets64[]; + + /** Position of the corresponding {@link #offsets32} in {@link #index}. */ + private final int nth32[]; + + /** Position of the corresponding {@link #offsets64} in {@link #index}. */ + private final int nth64[]; + + /** + * Create reverse index from straight/forward pack index, by indexing all + * its entries. + * + * @param packIndex + * forward index - entries to (reverse) index. + */ + PackReverseIndex(final PackIndex packIndex) { + index = packIndex; + + final long cnt = index.getObjectCount(); + final long n64 = index.getOffset64Count(); + final long n32 = cnt - n64; + if (n32 > Integer.MAX_VALUE || n64 > Integer.MAX_VALUE + || cnt > 0xffffffffL) + throw new IllegalArgumentException( + JGitText.get().hugeIndexesAreNotSupportedByJgitYet); + + offsets32 = new int[(int) n32]; + offsets64 = new long[(int) n64]; + nth32 = new int[offsets32.length]; + nth64 = new int[offsets64.length]; + + int i32 = 0; + int i64 = 0; + for (final MutableEntry me : index) { + final long o = me.getOffset(); + if (o < Integer.MAX_VALUE) + offsets32[i32++] = (int) o; + else + offsets64[i64++] = o; + } + + Arrays.sort(offsets32); + Arrays.sort(offsets64); + + int nth = 0; + for (final MutableEntry me : index) { + final long o = me.getOffset(); + if (o < Integer.MAX_VALUE) + nth32[Arrays.binarySearch(offsets32, (int) o)] = nth++; + else + nth64[Arrays.binarySearch(offsets64, o)] = nth++; + } + } + + /** + * Search for object id with the specified start offset in this pack + * (reverse) index. + * + * @param offset + * start offset of object to find. + * @return object id for this offset, or null if no object was found. + */ + ObjectId findObject(final long offset) { + if (offset <= Integer.MAX_VALUE) { + final int i32 = Arrays.binarySearch(offsets32, (int) offset); + if (i32 < 0) + return null; + return index.getObjectId(nth32[i32]); + } else { + final int i64 = Arrays.binarySearch(offsets64, offset); + if (i64 < 0) + return null; + return index.getObjectId(nth64[i64]); + } + } + + /** + * Search for the next offset to the specified offset in this pack (reverse) + * index. + * + * @param offset + * start offset of previous object (must be valid-existing + * offset). + * @param maxOffset + * maximum offset in a pack (returned when there is no next + * offset). + * @return offset of the next object in a pack or maxOffset if provided + * offset was the last one. + * @throws CorruptObjectException + * when there is no object with the provided offset. + */ + long findNextOffset(final long offset, final long maxOffset) + throws CorruptObjectException { + if (offset <= Integer.MAX_VALUE) { + final int i32 = Arrays.binarySearch(offsets32, (int) offset); + if (i32 < 0) + throw new CorruptObjectException(MessageFormat.format( + JGitText.get().cantFindObjectInReversePackIndexForTheSpecifiedOffset + , offset)); + + if (i32 + 1 == offsets32.length) { + if (offsets64.length > 0) + return offsets64[0]; + return maxOffset; + } + return offsets32[i32 + 1]; + } else { + final int i64 = Arrays.binarySearch(offsets64, offset); + if (i64 < 0) + throw new CorruptObjectException(MessageFormat.format( + JGitText.get().cantFindObjectInReversePackIndexForTheSpecifiedOffset + , offset)); + + if (i64 + 1 == offsets64.length) + return maxOffset; + return offsets64[i64 + 1]; + } + } +} diff --git a/org.eclipse.jgit/src/org/eclipse/jgit/storage/file/PackedObjectLoader.java b/org.eclipse.jgit/src/org/eclipse/jgit/storage/file/PackedObjectLoader.java new file mode 100644 index 0000000000..e784f3d181 --- /dev/null +++ b/org.eclipse.jgit/src/org/eclipse/jgit/storage/file/PackedObjectLoader.java @@ -0,0 +1,127 @@ +/* + * Copyright (C) 2009, Google Inc. + * Copyright (C) 2008, Marek Zawirski + * Copyright (C) 2007, Robin Rosenberg + * Copyright (C) 2006-2008, Shawn O. Pearce + * 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 org.eclipse.jgit.lib.ObjectId; +import org.eclipse.jgit.lib.ObjectLoader; + +/** + * 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. + *

+ * 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. + *

    + *
  • {@link #getType()}
  • + *
  • {@link #getSize()}
  • + *
  • {@link #getBytes()}, {@link #getCachedBytes}
  • + *
  • {@link #getRawSize()}
  • + *
  • {@link #getRawType()}
  • + *
+ * + * @param curs + * temporary thread storage during data access. + * @throws IOException + * the object cannot be read. + */ + 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 + */ + final long getObjectOffset() { + return objectOffset; + } + + /** + * @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. + */ + abstract ObjectId getDeltaBase() throws IOException; +} diff --git a/org.eclipse.jgit/src/org/eclipse/jgit/storage/file/RefDirectory.java b/org.eclipse.jgit/src/org/eclipse/jgit/storage/file/RefDirectory.java new file mode 100644 index 0000000000..f7ffa3e39a --- /dev/null +++ b/org.eclipse.jgit/src/org/eclipse/jgit/storage/file/RefDirectory.java @@ -0,0 +1,1018 @@ +/* + * Copyright (C) 2007, Dave Watson + * Copyright (C) 2009-2010, Google Inc. + * Copyright (C) 2007, Robin Rosenberg + * Copyright (C) 2006, Shawn O. Pearce + * 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 static org.eclipse.jgit.lib.Constants.CHARSET; +import static org.eclipse.jgit.lib.Constants.HEAD; +import static org.eclipse.jgit.lib.Constants.LOGS; +import static org.eclipse.jgit.lib.Constants.OBJECT_ID_STRING_LENGTH; +import static org.eclipse.jgit.lib.Constants.PACKED_REFS; +import static org.eclipse.jgit.lib.Constants.R_HEADS; +import static org.eclipse.jgit.lib.Constants.R_REFS; +import static org.eclipse.jgit.lib.Constants.R_REMOTES; +import static org.eclipse.jgit.lib.Constants.R_TAGS; +import static org.eclipse.jgit.lib.Constants.encode; +import static org.eclipse.jgit.lib.Ref.Storage.LOOSE; +import static org.eclipse.jgit.lib.Ref.Storage.NEW; +import static org.eclipse.jgit.lib.Ref.Storage.PACKED; + +import java.io.BufferedReader; +import java.io.File; +import java.io.FileInputStream; +import java.io.FileNotFoundException; +import java.io.FileOutputStream; +import java.io.IOException; +import java.io.InputStreamReader; +import java.text.MessageFormat; +import java.util.Arrays; +import java.util.Map; +import java.util.concurrent.atomic.AtomicInteger; +import java.util.concurrent.atomic.AtomicReference; + +import org.eclipse.jgit.JGitText; +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; +import org.eclipse.jgit.util.FS; +import org.eclipse.jgit.util.IO; +import org.eclipse.jgit.util.RawParseUtils; +import org.eclipse.jgit.util.RefList; +import org.eclipse.jgit.util.RefMap; + +/** + * Traditional file system based {@link RefDatabase}. + *

+ * This is the classical reference database representation for a Git repository. + * References are stored in two formats: loose, and packed. + *

+ * Loose references are stored as individual files within the {@code refs/} + * directory. The file name matches the reference name and the file contents is + * the current {@link ObjectId} in string form. + *

+ * Packed references are stored in a single text file named {@code packed-refs}. + * In the packed format, each reference is stored on its own line. This file + * reduces the number of files needed for large reference spaces, reducing the + * overall size of a Git repository on disk. + */ +public class RefDirectory extends RefDatabase { + /** Magic string denoting the start of a symbolic reference file. */ + public static final String SYMREF = "ref: "; //$NON-NLS-1$ + + /** Magic string denoting the header of a packed-refs file. */ + public static final String PACKED_REFS_HEADER = "# pack-refs with:"; //$NON-NLS-1$ + + /** If in the header, denotes the file has peeled data. */ + public static final String PACKED_REFS_PEELED = " peeled"; //$NON-NLS-1$ + + private final FileRepository parent; + + private final File gitDir; + + private final File refsDir; + + private final File logsDir; + + private final File logsRefsDir; + + private final File packedRefsFile; + + /** + * Immutable sorted list of loose references. + *

+ * Symbolic references in this collection are stored unresolved, that is + * their target appears to be a new reference with no ObjectId. These are + * converted into resolved references during a get operation, ensuring the + * live value is always returned. + */ + private final AtomicReference> looseRefs = new AtomicReference>(); + + /** Immutable sorted list of packed references. */ + private final AtomicReference packedRefs = new AtomicReference(); + + /** + * Number of modifications made to this database. + *

+ * This counter is incremented when a change is made, or detected from the + * filesystem during a read operation. + */ + private final AtomicInteger modCnt = new AtomicInteger(); + + /** + * Last {@link #modCnt} that we sent to listeners. + *

+ * This value is compared to {@link #modCnt}, and a notification is sent to + * the listeners only when it differs. + */ + private final AtomicInteger lastNotifiedModCnt = new AtomicInteger(); + + RefDirectory(final FileRepository db) { + final FS fs = db.getFS(); + parent = db; + gitDir = db.getDirectory(); + refsDir = fs.resolve(gitDir, R_REFS); + logsDir = fs.resolve(gitDir, LOGS); + logsRefsDir = fs.resolve(gitDir, LOGS + '/' + R_REFS); + packedRefsFile = fs.resolve(gitDir, PACKED_REFS); + + looseRefs.set(RefList. emptyList()); + packedRefs.set(PackedRefList.NO_PACKED_REFS); + } + + Repository getRepository() { + return parent; + } + + public void create() throws IOException { + refsDir.mkdir(); + logsDir.mkdir(); + logsRefsDir.mkdir(); + + new File(refsDir, R_HEADS.substring(R_REFS.length())).mkdir(); + new File(refsDir, R_TAGS.substring(R_REFS.length())).mkdir(); + new File(logsRefsDir, R_HEADS.substring(R_REFS.length())).mkdir(); + } + + @Override + public void close() { + // We have no resources to close. + } + + void rescan() { + looseRefs.set(RefList. emptyList()); + packedRefs.set(PackedRefList.NO_PACKED_REFS); + } + + @Override + public boolean isNameConflicting(String name) throws IOException { + RefList packed = getPackedRefs(); + RefList loose = getLooseRefs(); + + // Cannot be nested within an existing reference. + int lastSlash = name.lastIndexOf('/'); + while (0 < lastSlash) { + String needle = name.substring(0, lastSlash); + if (loose.contains(needle) || packed.contains(needle)) + return true; + lastSlash = name.lastIndexOf('/', lastSlash - 1); + } + + // Cannot be the container of an existing reference. + String prefix = name + '/'; + int idx; + + idx = -(packed.find(prefix) + 1); + if (idx < packed.size() && packed.get(idx).getName().startsWith(prefix)) + return true; + + idx = -(loose.find(prefix) + 1); + if (idx < loose.size() && loose.get(idx).getName().startsWith(prefix)) + return true; + + return false; + } + + private RefList getLooseRefs() { + final RefList oldLoose = looseRefs.get(); + + LooseScanner scan = new LooseScanner(oldLoose); + scan.scan(ALL); + + RefList loose; + if (scan.newLoose != null) { + loose = scan.newLoose.toRefList(); + if (looseRefs.compareAndSet(oldLoose, loose)) + modCnt.incrementAndGet(); + } else + loose = oldLoose; + return loose; + } + + @Override + public Ref getRef(final String needle) throws IOException { + final RefList packed = getPackedRefs(); + Ref ref = null; + for (String prefix : SEARCH_PATH) { + ref = readRef(prefix + needle, packed); + if (ref != null) { + ref = resolve(ref, 0, null, null, packed); + break; + } + } + fireRefsChanged(); + return ref; + } + + @Override + public Map getRefs(String prefix) throws IOException { + final RefList packed = getPackedRefs(); + final RefList oldLoose = looseRefs.get(); + + LooseScanner scan = new LooseScanner(oldLoose); + scan.scan(prefix); + + RefList loose; + if (scan.newLoose != null) { + loose = scan.newLoose.toRefList(); + if (looseRefs.compareAndSet(oldLoose, loose)) + modCnt.incrementAndGet(); + } else + loose = oldLoose; + fireRefsChanged(); + + RefList.Builder symbolic = scan.symbolic; + for (int idx = 0; idx < symbolic.size();) { + Ref ref = symbolic.get(idx); + ref = resolve(ref, 0, prefix, loose, packed); + if (ref != null && ref.getObjectId() != null) { + symbolic.set(idx, ref); + idx++; + } else { + // A broken symbolic reference, we have to drop it from the + // collections the client is about to receive. Should be a + // rare occurrence so pay a copy penalty. + loose = loose.remove(idx); + symbolic.remove(idx); + } + } + + return new RefMap(prefix, packed, upcast(loose), symbolic.toRefList()); + } + + @SuppressWarnings("unchecked") + private RefList upcast(RefList loose) { + return (RefList) loose; + } + + private class LooseScanner { + private final RefList curLoose; + + private int curIdx; + + final RefList.Builder symbolic = new RefList.Builder(4); + + RefList.Builder newLoose; + + LooseScanner(final RefList curLoose) { + this.curLoose = curLoose; + } + + void scan(String prefix) { + if (ALL.equals(prefix)) { + scanOne(HEAD); + scanTree(R_REFS, refsDir); + + // If any entries remain, they are deleted, drop them. + if (newLoose == null && curIdx < curLoose.size()) + newLoose = curLoose.copy(curIdx); + + } else if (prefix.startsWith(R_REFS) && prefix.endsWith("/")) { + curIdx = -(curLoose.find(prefix) + 1); + File dir = new File(refsDir, prefix.substring(R_REFS.length())); + scanTree(prefix, dir); + + // Skip over entries still within the prefix; these have + // been removed from the directory. + while (curIdx < curLoose.size()) { + if (!curLoose.get(curIdx).getName().startsWith(prefix)) + break; + if (newLoose == null) + newLoose = curLoose.copy(curIdx); + curIdx++; + } + + // Keep any entries outside of the prefix space, we + // do not know anything about their status. + if (newLoose != null) { + while (curIdx < curLoose.size()) + newLoose.add(curLoose.get(curIdx++)); + } + } + } + + private boolean scanTree(String prefix, File dir) { + final String[] entries = dir.list(LockFile.FILTER); + if (entries == null) // not a directory or an I/O error + return false; + if (0 < entries.length) { + Arrays.sort(entries); + for (String name : entries) { + File e = new File(dir, name); + if (!scanTree(prefix + name + '/', e)) + scanOne(prefix + name); + } + } + return true; + } + + private void scanOne(String name) { + LooseRef cur; + + if (curIdx < curLoose.size()) { + do { + cur = curLoose.get(curIdx); + int cmp = RefComparator.compareTo(cur, name); + if (cmp < 0) { + // Reference is not loose anymore, its been deleted. + // Skip the name in the new result list. + if (newLoose == null) + newLoose = curLoose.copy(curIdx); + curIdx++; + cur = null; + continue; + } + + if (cmp > 0) // Newly discovered loose reference. + cur = null; + break; + } while (curIdx < curLoose.size()); + } else + cur = null; // Newly discovered loose reference. + + LooseRef n; + try { + n = scanRef(cur, name); + } catch (IOException notValid) { + n = null; + } + + if (n != null) { + if (cur != n && newLoose == null) + newLoose = curLoose.copy(curIdx); + if (newLoose != null) + newLoose.add(n); + if (n.isSymbolic()) + symbolic.add(n); + } else if (cur != null) { + // Tragically, this file is no longer a loose reference. + // Kill our cached entry of it. + if (newLoose == null) + newLoose = curLoose.copy(curIdx); + } + + if (cur != null) + curIdx++; + } + } + + @Override + public Ref peel(final Ref ref) throws IOException { + final Ref leaf = ref.getLeaf(); + 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) { + newLeaf = new ObjectIdRef.PeeledTag(leaf.getStorage(), leaf + .getName(), leaf.getObjectId(), rw.peel(obj).copy()); + } else { + newLeaf = new ObjectIdRef.PeeledNonTag(leaf.getStorage(), leaf + .getName(), leaf.getObjectId()); + } + + // 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. + if (leaf.getStorage().isLoose()) { + RefList curList = looseRefs.get(); + int idx = curList.find(leaf.getName()); + if (0 <= idx && curList.get(idx) == leaf) { + LooseRef asPeeled = ((LooseRef) leaf).peel(newLeaf); + RefList newList = curList.set(idx, asPeeled); + looseRefs.compareAndSet(curList, newList); + } + } + + return recreate(ref, newLeaf); + } + + private static Ref recreate(final Ref old, final ObjectIdRef leaf) { + if (old.isSymbolic()) { + Ref dst = recreate(old.getTarget(), leaf); + return new SymbolicRef(old.getName(), dst); + } + return leaf; + } + + void storedSymbolicRef(RefDirectoryUpdate u, long modified, String target) { + putLooseRef(newSymbolicRef(modified, u.getRef().getName(), target)); + fireRefsChanged(); + } + + public RefDirectoryUpdate newUpdate(String name, boolean detach) + throws IOException { + final RefList packed = getPackedRefs(); + Ref ref = readRef(name, packed); + if (ref != null) + ref = resolve(ref, 0, null, null, packed); + if (ref == null) + ref = new ObjectIdRef.Unpeeled(NEW, name, null); + else if (detach && ref.isSymbolic()) + ref = new ObjectIdRef.Unpeeled(LOOSE, name, ref.getObjectId()); + return new RefDirectoryUpdate(this, ref); + } + + @Override + public RefDirectoryRename newRename(String fromName, String toName) + throws IOException { + RefDirectoryUpdate from = newUpdate(fromName, false); + RefDirectoryUpdate to = newUpdate(toName, false); + return new RefDirectoryRename(from, to); + } + + void stored(RefDirectoryUpdate update, long modified) { + final ObjectId target = update.getNewObjectId().copy(); + final Ref leaf = update.getRef().getLeaf(); + putLooseRef(new LooseUnpeeled(modified, leaf.getName(), target)); + } + + private void putLooseRef(LooseRef ref) { + RefList cList, nList; + do { + cList = looseRefs.get(); + nList = cList.put(ref); + } while (!looseRefs.compareAndSet(cList, nList)); + modCnt.incrementAndGet(); + fireRefsChanged(); + } + + void delete(RefDirectoryUpdate update) throws IOException { + Ref dst = update.getRef().getLeaf(); + String name = dst.getName(); + + // Write the packed-refs file using an atomic update. We might + // wind up reading it twice, before and after the lock, to ensure + // we don't miss an edit made externally. + final PackedRefList packed = getPackedRefs(); + if (packed.contains(name)) { + LockFile lck = new LockFile(packedRefsFile); + if (!lck.lock()) + throw new IOException(MessageFormat.format( + JGitText.get().cannotLockFile, packedRefsFile)); + try { + PackedRefList cur = readPackedRefs(0, 0); + int idx = cur.find(name); + if (0 <= idx) + commitPackedRefs(lck, cur.remove(idx), packed); + } finally { + lck.unlock(); + } + } + + RefList curLoose, newLoose; + do { + curLoose = looseRefs.get(); + int idx = curLoose.find(name); + if (idx < 0) + break; + newLoose = curLoose.remove(idx); + } while (!looseRefs.compareAndSet(curLoose, newLoose)); + + int levels = levelsIn(name) - 2; + delete(logFor(name), levels); + if (dst.getStorage().isLoose()) { + update.unlock(); + delete(fileFor(name), levels); + } + + modCnt.incrementAndGet(); + fireRefsChanged(); + } + + void log(final RefUpdate update, final String msg, final boolean deref) + throws IOException { + final ObjectId oldId = update.getOldObjectId(); + final ObjectId newId = update.getNewObjectId(); + final Ref ref = update.getRef(); + + PersonIdent ident = update.getRefLogIdent(); + if (ident == null) + ident = new PersonIdent(parent); + else + ident = new PersonIdent(ident); + + final StringBuilder r = new StringBuilder(); + r.append(ObjectId.toString(oldId)); + r.append(' '); + r.append(ObjectId.toString(newId)); + r.append(' '); + r.append(ident.toExternalString()); + r.append('\t'); + r.append(msg); + r.append('\n'); + final byte[] rec = encode(r.toString()); + + if (deref && ref.isSymbolic()) { + log(ref.getName(), rec); + log(ref.getLeaf().getName(), rec); + } else { + log(ref.getName(), rec); + } + } + + private void log(final String refName, final byte[] rec) throws IOException { + final File log = logFor(refName); + final boolean write; + if (isLogAllRefUpdates() && shouldAutoCreateLog(refName)) + write = true; + else if (log.isFile()) + write = true; + else + write = false; + + if (write) { + FileOutputStream out; + try { + out = new FileOutputStream(log, true); + } catch (FileNotFoundException err) { + final File dir = log.getParentFile(); + if (dir.exists()) + throw err; + if (!dir.mkdirs() && !dir.isDirectory()) + throw new IOException(MessageFormat.format(JGitText.get().cannotCreateDirectory, dir)); + out = new FileOutputStream(log, true); + } + try { + out.write(rec); + } finally { + out.close(); + } + } + } + + private boolean isLogAllRefUpdates() { + return parent.getConfig().get(CoreConfig.KEY).isLogAllRefUpdates(); + } + + private boolean shouldAutoCreateLog(final String refName) { + return refName.equals(HEAD) // + || refName.startsWith(R_HEADS) // + || refName.startsWith(R_REMOTES); + } + + private Ref resolve(final Ref ref, int depth, String prefix, + RefList loose, RefList packed) throws IOException { + if (ref.isSymbolic()) { + Ref dst = ref.getTarget(); + + if (MAX_SYMBOLIC_REF_DEPTH <= depth) + return null; // claim it doesn't exist + + // If the cached value can be assumed to be current due to a + // recent scan of the loose directory, use it. + if (loose != null && dst.getName().startsWith(prefix)) { + int idx; + if (0 <= (idx = loose.find(dst.getName()))) + dst = loose.get(idx); + else if (0 <= (idx = packed.find(dst.getName()))) + dst = packed.get(idx); + else + return ref; + } else { + dst = readRef(dst.getName(), packed); + if (dst == null) + return ref; + } + + dst = resolve(dst, depth + 1, prefix, loose, packed); + if (dst == null) + return null; + return new SymbolicRef(ref.getName(), dst); + } + return ref; + } + + private PackedRefList getPackedRefs() throws IOException { + long size = packedRefsFile.length(); + long mtime = size != 0 ? packedRefsFile.lastModified() : 0; + + final PackedRefList curList = packedRefs.get(); + if (size == curList.lastSize && mtime == curList.lastModified) + return curList; + + final PackedRefList newList = readPackedRefs(size, mtime); + if (packedRefs.compareAndSet(curList, newList)) + modCnt.incrementAndGet(); + return newList; + } + + private PackedRefList readPackedRefs(long size, long mtime) + throws IOException { + final BufferedReader br; + try { + br = new BufferedReader(new InputStreamReader(new FileInputStream( + packedRefsFile), CHARSET)); + } catch (FileNotFoundException noPackedRefs) { + // Ignore it and leave the new list empty. + return PackedRefList.NO_PACKED_REFS; + } + try { + return new PackedRefList(parsePackedRefs(br), size, mtime); + } finally { + br.close(); + } + } + + private RefList parsePackedRefs(final BufferedReader br) + throws IOException { + RefList.Builder all = new RefList.Builder(); + Ref last = null; + boolean peeled = false; + boolean needSort = false; + + String p; + while ((p = br.readLine()) != null) { + if (p.charAt(0) == '#') { + if (p.startsWith(PACKED_REFS_HEADER)) { + p = p.substring(PACKED_REFS_HEADER.length()); + peeled = p.contains(PACKED_REFS_PEELED); + } + continue; + } + + if (p.charAt(0) == '^') { + if (last == null) + throw new IOException(JGitText.get().peeledLineBeforeRef); + + ObjectId id = ObjectId.fromString(p.substring(1)); + last = new ObjectIdRef.PeeledTag(PACKED, last.getName(), last + .getObjectId(), id); + all.set(all.size() - 1, last); + continue; + } + + int sp = p.indexOf(' '); + ObjectId id = ObjectId.fromString(p.substring(0, sp)); + String name = copy(p, sp + 1, p.length()); + ObjectIdRef cur; + if (peeled) + cur = new ObjectIdRef.PeeledNonTag(PACKED, name, id); + else + cur = new ObjectIdRef.Unpeeled(PACKED, name, id); + if (last != null && RefComparator.compareTo(last, cur) > 0) + needSort = true; + all.add(cur); + last = cur; + } + + if (needSort) + all.sort(); + return all.toRefList(); + } + + private static String copy(final String src, final int off, final int end) { + // Don't use substring since it could leave a reference to the much + // larger existing string. Force construction of a full new object. + return new StringBuilder(end - off).append(src, off, end).toString(); + } + + private void commitPackedRefs(final LockFile lck, final RefList refs, + final PackedRefList oldPackedList) throws IOException { + new RefWriter(refs) { + @Override + protected void writeFile(String name, byte[] content) + throws IOException { + lck.setNeedStatInformation(true); + try { + lck.write(content); + } catch (IOException ioe) { + throw new ObjectWritingException(MessageFormat.format(JGitText.get().unableToWrite, name), ioe); + } + try { + lck.waitForStatChange(); + } catch (InterruptedException e) { + lck.unlock(); + throw new ObjectWritingException(MessageFormat.format(JGitText.get().interruptedWriting, name)); + } + if (!lck.commit()) + throw new ObjectWritingException(MessageFormat.format(JGitText.get().unableToWrite, name)); + + packedRefs.compareAndSet(oldPackedList, new PackedRefList(refs, + content.length, lck.getCommitLastModified())); + } + }.writePackedRefs(); + } + + private Ref readRef(String name, RefList packed) throws IOException { + final RefList curList = looseRefs.get(); + final int idx = curList.find(name); + if (0 <= idx) { + final LooseRef o = curList.get(idx); + final LooseRef n = scanRef(o, name); + if (n == null) { + if (looseRefs.compareAndSet(curList, curList.remove(idx))) + modCnt.incrementAndGet(); + return packed.get(name); + } + + if (o == n) + return n; + if (looseRefs.compareAndSet(curList, curList.set(idx, n))) + modCnt.incrementAndGet(); + return n; + } + + final LooseRef n = scanRef(null, name); + if (n == null) + return packed.get(name); + if (looseRefs.compareAndSet(curList, curList.add(idx, n))) + modCnt.incrementAndGet(); + return n; + } + + private LooseRef scanRef(LooseRef ref, String name) throws IOException { + final File path = fileFor(name); + final long modified = path.lastModified(); + + if (ref != null) { + if (ref.getLastModified() == modified) + return ref; + name = ref.getName(); + } else if (modified == 0) + return null; + + final byte[] buf; + try { + buf = IO.readFully(path, 4096); + } catch (FileNotFoundException noFile) { + return null; // doesn't exist; not a reference. + } + + int n = buf.length; + if (n == 0) + return null; // empty file; not a reference. + + if (isSymRef(buf, n)) { + // trim trailing whitespace + while (0 < n && Character.isWhitespace(buf[n - 1])) + n--; + if (n < 6) { + String content = RawParseUtils.decode(buf, 0, n); + throw new IOException(MessageFormat.format(JGitText.get().notARef, name, content)); + } + final String target = RawParseUtils.decode(buf, 5, n); + return newSymbolicRef(modified, name, target); + } + + if (n < OBJECT_ID_STRING_LENGTH) + return null; // impossibly short object identifier; not a reference. + + final ObjectId id; + try { + id = ObjectId.fromString(buf, 0); + } catch (IllegalArgumentException notRef) { + while (0 < n && Character.isWhitespace(buf[n - 1])) + n--; + String content = RawParseUtils.decode(buf, 0, n); + throw new IOException(MessageFormat.format(JGitText.get().notARef, name, content)); + } + return new LooseUnpeeled(modified, name, id); + } + + private static boolean isSymRef(final byte[] buf, int n) { + if (n < 6) + return false; + return /**/buf[0] == 'r' // + && buf[1] == 'e' // + && buf[2] == 'f' // + && buf[3] == ':' // + && buf[4] == ' '; + } + + /** If the parent should fire listeners, fires them. */ + private void fireRefsChanged() { + final int last = lastNotifiedModCnt.get(); + final int curr = modCnt.get(); + if (last != curr && lastNotifiedModCnt.compareAndSet(last, curr)) + parent.fireEvent(new RefsChangedEvent()); + } + + /** + * Create a reference update to write a temporary reference. + * + * @return an update for a new temporary reference. + * @throws IOException + * a temporary name cannot be allocated. + */ + RefDirectoryUpdate newTemporaryUpdate() throws IOException { + File tmp = File.createTempFile("renamed_", "_ref", refsDir); + String name = Constants.R_REFS + tmp.getName(); + Ref ref = new ObjectIdRef.Unpeeled(NEW, name, null); + return new RefDirectoryUpdate(this, ref); + } + + /** + * Locate the file on disk for a single reference name. + * + * @param name + * name of the ref, relative to the Git repository top level + * directory (so typically starts with refs/). + * @return the loose file location. + */ + File fileFor(String name) { + if (name.startsWith(R_REFS)) { + name = name.substring(R_REFS.length()); + return new File(refsDir, name); + } + return new File(gitDir, name); + } + + /** + * Locate the log file on disk for a single reference name. + * + * @param name + * name of the ref, relative to the Git repository top level + * directory (so typically starts with refs/). + * @return the log file location. + */ + File logFor(String name) { + if (name.startsWith(R_REFS)) { + name = name.substring(R_REFS.length()); + return new File(logsRefsDir, name); + } + return new File(logsDir, name); + } + + static int levelsIn(final String name) { + int count = 0; + for (int p = name.indexOf('/'); p >= 0; p = name.indexOf('/', p + 1)) + count++; + return count; + } + + static void delete(final File file, final int depth) throws IOException { + if (!file.delete() && file.isFile()) + throw new IOException(MessageFormat.format(JGitText.get().fileCannotBeDeleted, file)); + + File dir = file.getParentFile(); + for (int i = 0; i < depth; ++i) { + if (!dir.delete()) + break; // ignore problem here + dir = dir.getParentFile(); + } + } + + private static class PackedRefList extends RefList { + static final PackedRefList NO_PACKED_REFS = new PackedRefList(RefList + .emptyList(), 0, 0); + + /** Last length of the packed-refs file when we read it. */ + final long lastSize; + + /** Last modified time of the packed-refs file when we read it. */ + final long lastModified; + + PackedRefList(RefList src, long size, long mtime) { + super(src); + lastSize = size; + lastModified = mtime; + } + } + + private static LooseSymbolicRef newSymbolicRef(long lastModified, + String name, String target) { + Ref dst = new ObjectIdRef.Unpeeled(NEW, target, null); + return new LooseSymbolicRef(lastModified, name, dst); + } + + private static interface LooseRef extends Ref { + long getLastModified(); + + LooseRef peel(ObjectIdRef newLeaf); + } + + private final static class LoosePeeledTag extends ObjectIdRef.PeeledTag + implements LooseRef { + private final long lastModified; + + LoosePeeledTag(long mtime, String refName, ObjectId id, ObjectId p) { + super(LOOSE, refName, id, p); + this.lastModified = mtime; + } + + public long getLastModified() { + return lastModified; + } + + public LooseRef peel(ObjectIdRef newLeaf) { + return this; + } + } + + private final static class LooseNonTag extends ObjectIdRef.PeeledNonTag + implements LooseRef { + private final long lastModified; + + LooseNonTag(long mtime, String refName, ObjectId id) { + super(LOOSE, refName, id); + this.lastModified = mtime; + } + + public long getLastModified() { + return lastModified; + } + + public LooseRef peel(ObjectIdRef newLeaf) { + return this; + } + } + + private final static class LooseUnpeeled extends ObjectIdRef.Unpeeled + implements LooseRef { + private final long lastModified; + + LooseUnpeeled(long mtime, String refName, ObjectId id) { + super(LOOSE, refName, id); + this.lastModified = mtime; + } + + public long getLastModified() { + return lastModified; + } + + public LooseRef peel(ObjectIdRef newLeaf) { + if (newLeaf.getPeeledObjectId() != null) + return new LoosePeeledTag(lastModified, getName(), + getObjectId(), newLeaf.getPeeledObjectId()); + else + return new LooseNonTag(lastModified, getName(), getObjectId()); + } + } + + private final static class LooseSymbolicRef extends SymbolicRef implements + LooseRef { + private final long lastModified; + + LooseSymbolicRef(long mtime, String refName, Ref target) { + super(refName, target); + this.lastModified = mtime; + } + + public long getLastModified() { + return lastModified; + } + + public LooseRef peel(ObjectIdRef newLeaf) { + // We should never try to peel the symbolic references. + throw new UnsupportedOperationException(); + } + } +} diff --git a/org.eclipse.jgit/src/org/eclipse/jgit/storage/file/RefDirectoryRename.java b/org.eclipse.jgit/src/org/eclipse/jgit/storage/file/RefDirectoryRename.java new file mode 100644 index 0000000000..b43b70f1e4 --- /dev/null +++ b/org.eclipse.jgit/src/org/eclipse/jgit/storage/file/RefDirectoryRename.java @@ -0,0 +1,229 @@ +/* + * Copyright (C) 2010, Google Inc. + * Copyright (C) 2009, Robin Rosenberg + * 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.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; + +/** + * Rename any reference stored by {@link RefDirectory}. + *

+ * This class works by first renaming the source reference to a temporary name, + * then renaming the temporary name to the final destination reference. + *

+ * This strategy permits switching a reference like {@code refs/heads/foo}, + * which is a file, to {@code refs/heads/foo/bar}, which is stored inside a + * directory that happens to match the source name. + */ +class RefDirectoryRename extends RefRename { + private final RefDirectory refdb; + + /** + * The value of the source reference at the start of the rename. + *

+ * At the end of the rename the destination reference must have this same + * value, otherwise we have a concurrent update and the rename must fail + * without making any changes. + */ + private ObjectId objId; + + /** True if HEAD must be moved to the destination reference. */ + private boolean updateHEAD; + + /** A reference we backup {@link #objId} into during the rename. */ + private RefDirectoryUpdate tmp; + + RefDirectoryRename(RefDirectoryUpdate src, RefDirectoryUpdate dst) { + super(src, dst); + refdb = src.getRefDatabase(); + } + + @Override + protected Result doRename() throws IOException { + 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(); + try { + // First backup the source so its never unreachable. + tmp.setNewObjectId(objId); + tmp.setForceUpdate(true); + tmp.disableRefLog(); + switch (tmp.update(rw)) { + case NEW: + case FORCED: + case NO_CHANGE: + break; + default: + return tmp.getResult(); + } + + // Save the source's log under the temporary name, we must do + // this before we delete the source, otherwise we lose the log. + if (!renameLog(source, tmp)) + return Result.IO_FAILURE; + + // If HEAD has to be updated, link it now to destination. + // We have to link before we delete, otherwise the delete + // fails because its the current branch. + RefUpdate dst = destination; + if (updateHEAD) { + if (!linkHEAD(destination)) { + renameLog(tmp, source); + return Result.LOCK_FAILURE; + } + + // Replace the update operation so HEAD will log the rename. + dst = refdb.newUpdate(Constants.HEAD, false); + dst.setRefLogIdent(destination.getRefLogIdent()); + dst.setRefLogMessage(destination.getRefLogMessage(), false); + } + + // Delete the source name so its path is free for replacement. + source.setExpectedOldObjectId(objId); + source.setForceUpdate(true); + source.disableRefLog(); + if (source.delete(rw) != Result.FORCED) { + renameLog(tmp, source); + if (updateHEAD) + linkHEAD(source); + return source.getResult(); + } + + // Move the log to the destination. + if (!renameLog(tmp, destination)) { + renameLog(tmp, source); + source.setExpectedOldObjectId(ObjectId.zeroId()); + source.setNewObjectId(objId); + source.update(rw); + if (updateHEAD) + linkHEAD(source); + return Result.IO_FAILURE; + } + + // Create the destination, logging the rename during the creation. + dst.setExpectedOldObjectId(ObjectId.zeroId()); + dst.setNewObjectId(objId); + if (dst.update(rw) != Result.NEW) { + // If we didn't create the destination we have to undo + // our work. Put the log back and restore source. + if (renameLog(destination, tmp)) + renameLog(tmp, source); + source.setExpectedOldObjectId(ObjectId.zeroId()); + source.setNewObjectId(objId); + source.update(rw); + if (updateHEAD) + linkHEAD(source); + return dst.getResult(); + } + + return Result.RENAMED; + } finally { + // Always try to free the temporary name. + try { + refdb.delete(tmp); + } catch (IOException err) { + refdb.fileFor(tmp.getName()).delete(); + } + } + } + + private boolean renameLog(RefUpdate src, RefUpdate dst) { + File srcLog = refdb.logFor(src.getName()); + File dstLog = refdb.logFor(dst.getName()); + + if (!srcLog.exists()) + return true; + + if (!rename(srcLog, dstLog)) + return false; + + try { + final int levels = RefDirectory.levelsIn(src.getName()) - 2; + RefDirectory.delete(srcLog, levels); + return true; + } catch (IOException e) { + rename(dstLog, srcLog); + return false; + } + } + + private static boolean rename(File src, File dst) { + if (src.renameTo(dst)) + return true; + + File dir = dst.getParentFile(); + if ((dir.exists() || !dir.mkdirs()) && !dir.isDirectory()) + return false; + return src.renameTo(dst); + } + + private boolean linkHEAD(RefUpdate target) { + try { + RefUpdate u = refdb.newUpdate(Constants.HEAD, false); + u.disableRefLog(); + switch (u.link(target.getName())) { + case NEW: + case FORCED: + case NO_CHANGE: + return true; + default: + return false; + } + } catch (IOException e) { + return false; + } + } +} diff --git a/org.eclipse.jgit/src/org/eclipse/jgit/storage/file/RefDirectoryUpdate.java b/org.eclipse.jgit/src/org/eclipse/jgit/storage/file/RefDirectoryUpdate.java new file mode 100644 index 0000000000..8d35ec34f6 --- /dev/null +++ b/org.eclipse.jgit/src/org/eclipse/jgit/storage/file/RefDirectoryUpdate.java @@ -0,0 +1,160 @@ +/* + * Copyright (C) 2009-2010, Google Inc. + * Copyright (C) 2008, Shawn O. Pearce + * 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 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; + + private LockFile lock; + + RefDirectoryUpdate(final RefDirectory r, final Ref ref) { + super(ref); + database = r; + } + + @Override + protected RefDirectory getRefDatabase() { + return database; + } + + @Override + protected Repository getRepository() { + return database.getRepository(); + } + + @Override + protected boolean tryLock(boolean deref) throws IOException { + Ref dst = getRef(); + if (deref) + dst = dst.getLeaf(); + String name = dst.getName(); + lock = new LockFile(database.fileFor(name)); + if (lock.lock()) { + dst = database.getRef(name); + setOldObjectId(dst != null ? dst.getObjectId() : null); + return true; + } else { + return false; + } + } + + @Override + protected void unlock() { + if (lock != null) { + lock.unlock(); + lock = null; + } + } + + @Override + protected Result doUpdate(final Result status) throws IOException { + lock.setNeedStatInformation(true); + lock.write(getNewObjectId()); + + String msg = getRefLogMessage(); + if (msg != null) { + if (isRefLogIncludingResult()) { + String strResult = toResultString(status); + if (strResult != null) { + if (msg.length() > 0) + msg = msg + ": " + strResult; + else + msg = strResult; + } + } + database.log(this, msg, true); + } + if (!lock.commit()) + return Result.LOCK_FAILURE; + database.stored(this, lock.getCommitLastModified()); + return status; + } + + private String toResultString(final Result status) { + switch (status) { + case FORCED: + return "forced-update"; + case FAST_FORWARD: + return "fast forward"; + case NEW: + return "created"; + default: + return null; + } + } + + @Override + protected Result doDelete(final Result status) throws IOException { + if (getRef().getLeaf().getStorage() != Ref.Storage.NEW) + database.delete(this); + return status; + } + + @Override + protected Result doLink(final String target) throws IOException { + lock.setNeedStatInformation(true); + lock.write(encode(RefDirectory.SYMREF + target + '\n')); + + String msg = getRefLogMessage(); + if (msg != null) + database.log(this, msg, false); + if (!lock.commit()) + return Result.LOCK_FAILURE; + database.storedSymbolicRef(this, lock.getCommitLastModified(), target); + + if (getRef().getStorage() == Ref.Storage.NEW) + return Result.NEW; + return Result.FORCED; + } +} diff --git a/org.eclipse.jgit/src/org/eclipse/jgit/storage/file/ReflogReader.java b/org.eclipse.jgit/src/org/eclipse/jgit/storage/file/ReflogReader.java new file mode 100644 index 0000000000..75214308d6 --- /dev/null +++ b/org.eclipse.jgit/src/org/eclipse/jgit/storage/file/ReflogReader.java @@ -0,0 +1,188 @@ +/* + * Copyright (C) 2009, Robin Rosenberg + * Copyright (C) 2009, Robin Rosenberg + * 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.FileNotFoundException; +import java.io.IOException; +import java.util.ArrayList; +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; + +/** + * Utility for reading reflog entries + */ +public class ReflogReader { + /** + * Parsed reflog entry + */ + static public class Entry { + private ObjectId oldId; + + private ObjectId newId; + + private PersonIdent who; + + private String comment; + + Entry(byte[] raw, int pos) { + oldId = ObjectId.fromString(raw, pos); + pos += Constants.OBJECT_ID_STRING_LENGTH; + if (raw[pos++] != ' ') + throw new IllegalArgumentException( + JGitText.get().rawLogMessageDoesNotParseAsLogEntry); + newId = ObjectId.fromString(raw, pos); + pos += Constants.OBJECT_ID_STRING_LENGTH; + if (raw[pos++] != ' ') { + throw new IllegalArgumentException( + JGitText.get().rawLogMessageDoesNotParseAsLogEntry); + } + who = RawParseUtils.parsePersonIdentOnly(raw, pos); + int p0 = RawParseUtils.next(raw, pos, '\t'); // personident has no + // \t + if (p0 == -1) { + throw new IllegalArgumentException( + JGitText.get().rawLogMessageDoesNotParseAsLogEntry); + } + int p1 = RawParseUtils.nextLF(raw, p0); + if (p1 == -1) { + throw new IllegalArgumentException( + JGitText.get().rawLogMessageDoesNotParseAsLogEntry); + } + comment = RawParseUtils.decode(raw, p0, p1 - 1); + } + + /** + * @return the commit id before the change + */ + public ObjectId getOldId() { + return oldId; + } + + /** + * @return the commit id after the change + */ + public ObjectId getNewId() { + return newId; + } + + /** + * @return user performin the change + */ + public PersonIdent getWho() { + return who; + } + + /** + * @return textual description of the change + */ + public String getComment() { + return comment; + } + + @Override + public String toString() { + return "Entry[" + oldId.name() + ", " + newId.name() + ", " + getWho() + ", " + + getComment() + "]"; + } + } + + private File logName; + + ReflogReader(Repository db, String refname) { + logName = new File(db.getDirectory(), "logs/" + refname); + } + + /** + * Get the last entry in the reflog + * + * @return the latest reflog entry, or null if no log + * @throws IOException + */ + public Entry getLastEntry() throws IOException { + List entries = getReverseEntries(1); + return entries.size() > 0 ? entries.get(0) : null; + } + + /** + * @return all reflog entries in reverse order + * @throws IOException + */ + public List getReverseEntries() throws IOException { + return getReverseEntries(Integer.MAX_VALUE); + } + + /** + * @param max + * max numer of entries to read + * @return all reflog entries in reverse order + * @throws IOException + */ + public List getReverseEntries(int max) throws IOException { + final byte[] log; + try { + log = IO.readFully(logName); + } catch (FileNotFoundException e) { + return Collections.emptyList(); + } + + int rs = RawParseUtils.prevLF(log, log.length); + List ret = new ArrayList(); + while (rs >= 0 && max-- > 0) { + rs = RawParseUtils.prevLF(log, rs); + Entry entry = new Entry(log, rs < 0 ? 0 : rs + 2); + ret.add(entry); + } + return ret; + } +} diff --git a/org.eclipse.jgit/src/org/eclipse/jgit/storage/file/UnpackedObjectCache.java b/org.eclipse.jgit/src/org/eclipse/jgit/storage/file/UnpackedObjectCache.java new file mode 100644 index 0000000000..92f4824254 --- /dev/null +++ b/org.eclipse.jgit/src/org/eclipse/jgit/storage/file/UnpackedObjectCache.java @@ -0,0 +1,195 @@ +/* + * Copyright (C) 2008, Shawn O. Pearce + * 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.lang.ref.SoftReference; + +class UnpackedObjectCache { + private static final int CACHE_SZ = 1024; + + private static final SoftReference DEAD; + + private static int hash(final long position) { + return (((int) position) << 22) >>> 22; + } + + private static int maxByteCount; + + private static final Slot[] cache; + + private static Slot lruHead; + + private static Slot lruTail; + + private static int openByteCount; + + static { + DEAD = new SoftReference(null); + maxByteCount = new WindowCacheConfig().getDeltaBaseCacheLimit(); + + cache = new Slot[CACHE_SZ]; + for (int i = 0; i < CACHE_SZ; i++) + cache[i] = new Slot(); + } + + static synchronized void reconfigure(final WindowCacheConfig cfg) { + final int dbLimit = cfg.getDeltaBaseCacheLimit(); + if (maxByteCount != dbLimit) { + maxByteCount = dbLimit; + releaseMemory(); + } + } + + static synchronized Entry get(final PackFile pack, final long position) { + final Slot e = cache[hash(position)]; + if (e.provider == pack && e.position == position) { + final Entry buf = e.data.get(); + if (buf != null) { + moveToHead(e); + return buf; + } + } + return null; + } + + static synchronized void store(final PackFile pack, final long position, + final byte[] data, final int objectType) { + if (data.length > maxByteCount) + return; // Too large to cache. + + final Slot e = cache[hash(position)]; + clearEntry(e); + + openByteCount += data.length; + releaseMemory(); + + e.provider = pack; + e.position = position; + e.sz = data.length; + e.data = new SoftReference(new Entry(data, objectType)); + moveToHead(e); + } + + private static void releaseMemory() { + while (openByteCount > maxByteCount && lruTail != null) { + final Slot currOldest = lruTail; + final Slot nextOldest = currOldest.lruPrev; + + clearEntry(currOldest); + currOldest.lruPrev = null; + currOldest.lruNext = null; + + if (nextOldest == null) + lruHead = null; + else + nextOldest.lruNext = null; + lruTail = nextOldest; + } + } + + static synchronized void purge(final PackFile file) { + for (final Slot e : cache) { + if (e.provider == file) { + clearEntry(e); + unlink(e); + } + } + } + + private static void moveToHead(final Slot e) { + unlink(e); + e.lruPrev = null; + e.lruNext = lruHead; + if (lruHead != null) + lruHead.lruPrev = e; + else + lruTail = e; + lruHead = e; + } + + private static void unlink(final Slot e) { + final Slot prev = e.lruPrev; + final Slot next = e.lruNext; + if (prev != null) + prev.lruNext = next; + if (next != null) + next.lruPrev = prev; + } + + private static void clearEntry(final Slot e) { + openByteCount -= e.sz; + e.provider = null; + e.data = DEAD; + e.sz = 0; + } + + private UnpackedObjectCache() { + throw new UnsupportedOperationException(); + } + + static class Entry { + final byte[] data; + + final int type; + + Entry(final byte[] aData, final int aType) { + data = aData; + type = aType; + } + } + + private static class Slot { + Slot lruPrev; + + Slot lruNext; + + PackFile provider; + + long position; + + int sz; + + SoftReference data = DEAD; + } +} diff --git a/org.eclipse.jgit/src/org/eclipse/jgit/storage/file/UnpackedObjectLoader.java b/org.eclipse.jgit/src/org/eclipse/jgit/storage/file/UnpackedObjectLoader.java new file mode 100644 index 0000000000..054a4ae37e --- /dev/null +++ b/org.eclipse.jgit/src/org/eclipse/jgit/storage/file/UnpackedObjectLoader.java @@ -0,0 +1,227 @@ +/* + * Copyright (C) 2007, Robin Rosenberg + * Copyright (C) 2006-2008, Shawn O. Pearce + * 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.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.lib.AnyObjectId; +import org.eclipse.jgit.lib.Constants; +import org.eclipse.jgit.lib.InflaterCache; +import org.eclipse.jgit.lib.ObjectLoader; +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/storage/file/WholePackedObjectLoader.java b/org.eclipse.jgit/src/org/eclipse/jgit/storage/file/WholePackedObjectLoader.java new file mode 100644 index 0000000000..ba7781a0f1 --- /dev/null +++ b/org.eclipse.jgit/src/org/eclipse/jgit/storage/file/WholePackedObjectLoader.java @@ -0,0 +1,112 @@ +/* + * Copyright (C) 2008-2009, Google Inc. + * Copyright (C) 2008, Marek Zawirski + * Copyright (C) 2006-2008, Shawn O. Pearce + * 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.text.MessageFormat; +import java.util.zip.DataFormatException; + +import org.eclipse.jgit.JGitText; +import org.eclipse.jgit.errors.CorruptObjectException; +import org.eclipse.jgit.lib.Constants; +import org.eclipse.jgit.lib.ObjectId; + +/** 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 + 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 + ObjectId getDeltaBase() { + return null; + } +} diff --git a/org.eclipse.jgit/src/org/eclipse/jgit/storage/file/WindowCache.java b/org.eclipse.jgit/src/org/eclipse/jgit/storage/file/WindowCache.java new file mode 100644 index 0000000000..39633ee5ef --- /dev/null +++ b/org.eclipse.jgit/src/org/eclipse/jgit/storage/file/WindowCache.java @@ -0,0 +1,624 @@ +/* + * Copyright (C) 2008-2009, Google Inc. + * Copyright (C) 2008, Shawn O. Pearce + * 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.lang.ref.ReferenceQueue; +import java.lang.ref.SoftReference; +import java.util.Random; +import java.util.concurrent.atomic.AtomicInteger; +import java.util.concurrent.atomic.AtomicLong; +import java.util.concurrent.atomic.AtomicReferenceArray; +import java.util.concurrent.locks.ReentrantLock; + +import org.eclipse.jgit.JGitText; + +/** + * Caches slices of a {@link PackFile} in memory for faster read access. + *

+ * The WindowCache serves as a Java based "buffer cache", loading segments of a + * PackFile into the JVM heap prior to use. As JGit often wants to do reads of + * only tiny slices of a file, the WindowCache tries to smooth out these tiny + * reads into larger block-sized IO operations. + *

+ * Whenever a cache miss occurs, {@link #load(PackFile, long)} is invoked by + * exactly one thread for the given (PackFile,position) key tuple. + * This is ensured by an array of locks, with the tuple hashed to a lock + * instance. + *

+ * During a miss, older entries are evicted from the cache so long as + * {@link #isFull()} returns true. + *

+ * Its too expensive during object access to be 100% accurate with a least + * recently used (LRU) algorithm. Strictly ordering every read is a lot of + * overhead that typically doesn't yield a corresponding benefit to the + * application. + *

+ * This cache implements a loose LRU policy by randomly picking a window + * comprised of roughly 10% of the cache, and evicting the oldest accessed entry + * within that window. + *

+ * Entities created by the cache are held under SoftReferences, permitting the + * Java runtime's garbage collector to evict entries when heap memory gets low. + * Most JREs implement a loose least recently used algorithm for this eviction. + *

+ * The internal hash table does not expand at runtime, instead it is fixed in + * size at cache creation time. The internal lock table used to gate load + * invocations is also fixed in size. + *

+ * The key tuple is passed through to methods as a pair of parameters rather + * than as a single Object, thus reducing the transient memory allocations of + * callers. It is more efficient to avoid the allocation, as we can't be 100% + * sure that a JIT would be able to stack-allocate a key tuple. + *

+ * This cache has an implementation rule such that: + *

    + *
  • {@link #load(PackFile, long)} is invoked by at most one thread at a time + * for a given (PackFile,position) tuple.
  • + *
  • For every load() invocation there is exactly one + * {@link #createRef(PackFile, long, ByteWindow)} invocation to wrap a + * SoftReference around the cached entity.
  • + *
  • For every Reference created by createRef() there will be + * exactly one call to {@link #clear(Ref)} to cleanup any resources associated + * with the (now expired) cached entity.
  • + *
+ *

+ * Therefore, it is safe to perform resource accounting increments during the + * {@link #load(PackFile, long)} or + * {@link #createRef(PackFile, long, ByteWindow)} methods, and matching + * decrements during {@link #clear(Ref)}. Implementors may need to override + * {@link #createRef(PackFile, long, ByteWindow)} in order to embed additional + * accounting information into an implementation specific {@link Ref} subclass, + * as the cached entity may have already been evicted by the JRE's garbage + * collector. + *

+ * To maintain higher concurrency workloads, during eviction only one thread + * performs the eviction work, while other threads can continue to insert new + * objects in parallel. This means that the cache can be temporarily over limit, + * especially if the nominated eviction thread is being starved relative to the + * other threads. + */ +public class WindowCache { + private static final int bits(int newSize) { + if (newSize < 4096) + throw new IllegalArgumentException(JGitText.get().invalidWindowSize); + if (Integer.bitCount(newSize) != 1) + throw new IllegalArgumentException(JGitText.get().windowSizeMustBePowerOf2); + return Integer.numberOfTrailingZeros(newSize); + } + + private static final Random rng = new Random(); + + private static volatile WindowCache cache; + + static { + reconfigure(new WindowCacheConfig()); + } + + /** + * Modify the configuration of the window cache. + *

+ * The new configuration is applied immediately. If the new limits are + * smaller than what what is currently cached, older entries will be purged + * as soon as possible to allow the cache to meet the new limit. + * + * @param packedGitLimit + * maximum number of bytes to hold within this instance. + * @param packedGitWindowSize + * number of bytes per window within the cache. + * @param packedGitMMAP + * true to enable use of mmap when creating windows. + * @param deltaBaseCacheLimit + * number of bytes to hold in the delta base cache. + * @deprecated Use {@link WindowCacheConfig} instead. + */ + public static void reconfigure(final int packedGitLimit, + final int packedGitWindowSize, final boolean packedGitMMAP, + final int deltaBaseCacheLimit) { + final WindowCacheConfig c = new WindowCacheConfig(); + c.setPackedGitLimit(packedGitLimit); + c.setPackedGitWindowSize(packedGitWindowSize); + c.setPackedGitMMAP(packedGitMMAP); + c.setDeltaBaseCacheLimit(deltaBaseCacheLimit); + reconfigure(c); + } + + /** + * Modify the configuration of the window cache. + *

+ * The new configuration is applied immediately. If the new limits are + * smaller than what what is currently cached, older entries will be purged + * as soon as possible to allow the cache to meet the new limit. + * + * @param cfg + * the new window cache configuration. + * @throws IllegalArgumentException + * the cache configuration contains one or more invalid + * settings, usually too low of a limit. + */ + public static void reconfigure(final WindowCacheConfig cfg) { + final WindowCache nc = new WindowCache(cfg); + final WindowCache oc = cache; + if (oc != null) + oc.removeAll(); + cache = nc; + UnpackedObjectCache.reconfigure(cfg); + } + + static WindowCache getInstance() { + return cache; + } + + static final ByteWindow get(final PackFile pack, final long offset) + throws IOException { + final WindowCache c = cache; + final ByteWindow r = c.getOrLoad(pack, c.toStart(offset)); + if (c != cache) { + // The cache was reconfigured while we were using the old one + // to load this window. The window is still valid, but our + // cache may think its still live. Ensure the window is removed + // from the old cache so resources can be released. + // + c.removeAll(); + } + return r; + } + + static final void purge(final PackFile pack) { + cache.removeAll(pack); + } + + /** ReferenceQueue to cleanup released and garbage collected windows. */ + private final ReferenceQueue queue; + + /** Number of entries in {@link #table}. */ + private final int tableSize; + + /** Access clock for loose LRU. */ + private final AtomicLong clock; + + /** Hash bucket directory; entries are chained below. */ + private final AtomicReferenceArray table; + + /** Locks to prevent concurrent loads for same (PackFile,position). */ + private final Lock[] locks; + + /** Lock to elect the eviction thread after a load occurs. */ + private final ReentrantLock evictLock; + + /** Number of {@link #table} buckets to scan for an eviction window. */ + private final int evictBatch; + + private final int maxFiles; + + private final long maxBytes; + + private final boolean mmap; + + private final int windowSizeShift; + + private final int windowSize; + + private final AtomicInteger openFiles; + + private final AtomicLong openBytes; + + private WindowCache(final WindowCacheConfig cfg) { + tableSize = tableSize(cfg); + final int lockCount = lockCount(cfg); + if (tableSize < 1) + throw new IllegalArgumentException(JGitText.get().tSizeMustBeGreaterOrEqual1); + if (lockCount < 1) + throw new IllegalArgumentException(JGitText.get().lockCountMustBeGreaterOrEqual1); + + queue = new ReferenceQueue(); + clock = new AtomicLong(1); + table = new AtomicReferenceArray(tableSize); + locks = new Lock[lockCount]; + for (int i = 0; i < locks.length; i++) + locks[i] = new Lock(); + evictLock = new ReentrantLock(); + + int eb = (int) (tableSize * .1); + if (64 < eb) + eb = 64; + else if (eb < 4) + eb = 4; + if (tableSize < eb) + eb = tableSize; + evictBatch = eb; + + maxFiles = cfg.getPackedGitOpenFiles(); + maxBytes = cfg.getPackedGitLimit(); + mmap = cfg.isPackedGitMMAP(); + windowSizeShift = bits(cfg.getPackedGitWindowSize()); + windowSize = 1 << windowSizeShift; + + openFiles = new AtomicInteger(); + openBytes = new AtomicLong(); + + if (maxFiles < 1) + throw new IllegalArgumentException(JGitText.get().openFilesMustBeAtLeast1); + if (maxBytes < windowSize) + throw new IllegalArgumentException(JGitText.get().windowSizeMustBeLesserThanLimit); + } + + int getOpenFiles() { + return openFiles.get(); + } + + long getOpenBytes() { + return openBytes.get(); + } + + private int hash(final int packHash, final long off) { + return packHash + (int) (off >>> windowSizeShift); + } + + private ByteWindow load(final PackFile pack, final long offset) + throws IOException { + if (pack.beginWindowCache()) + openFiles.incrementAndGet(); + try { + if (mmap) + return pack.mmap(offset, windowSize); + return pack.read(offset, windowSize); + } catch (IOException e) { + close(pack); + throw e; + } catch (RuntimeException e) { + close(pack); + throw e; + } catch (Error e) { + close(pack); + throw e; + } + } + + private Ref createRef(final PackFile p, final long o, final ByteWindow v) { + final Ref ref = new Ref(p, o, v, queue); + openBytes.addAndGet(ref.size); + return ref; + } + + private void clear(final Ref ref) { + openBytes.addAndGet(-ref.size); + close(ref.pack); + } + + private void close(final PackFile pack) { + if (pack.endWindowCache()) + openFiles.decrementAndGet(); + } + + private boolean isFull() { + return maxFiles < openFiles.get() || maxBytes < openBytes.get(); + } + + private long toStart(final long offset) { + return (offset >>> windowSizeShift) << windowSizeShift; + } + + private static int tableSize(final WindowCacheConfig cfg) { + final int wsz = cfg.getPackedGitWindowSize(); + final long limit = cfg.getPackedGitLimit(); + if (wsz <= 0) + throw new IllegalArgumentException(JGitText.get().invalidWindowSize); + if (limit < wsz) + throw new IllegalArgumentException(JGitText.get().windowSizeMustBeLesserThanLimit); + return (int) Math.min(5 * (limit / wsz) / 2, 2000000000); + } + + private static int lockCount(final WindowCacheConfig cfg) { + return Math.max(cfg.getPackedGitOpenFiles(), 32); + } + + /** + * Lookup a cached object, creating and loading it if it doesn't exist. + * + * @param pack + * the pack that "contains" the cached object. + * @param position + * offset within pack of the object. + * @return the object reference. + * @throws IOException + * the object reference was not in the cache and could not be + * obtained by {@link #load(PackFile, long)}. + */ + private ByteWindow getOrLoad(final PackFile pack, final long position) + throws IOException { + final int slot = slot(pack, position); + final Entry e1 = table.get(slot); + ByteWindow v = scan(e1, pack, position); + if (v != null) + return v; + + synchronized (lock(pack, position)) { + Entry e2 = table.get(slot); + if (e2 != e1) { + v = scan(e2, pack, position); + if (v != null) + return v; + } + + v = load(pack, position); + final Ref ref = createRef(pack, position, v); + hit(ref); + for (;;) { + final Entry n = new Entry(clean(e2), ref); + if (table.compareAndSet(slot, e2, n)) + break; + e2 = table.get(slot); + } + } + + if (evictLock.tryLock()) { + try { + gc(); + evict(); + } finally { + evictLock.unlock(); + } + } + + return v; + } + + private ByteWindow scan(Entry n, final PackFile pack, final long position) { + for (; n != null; n = n.next) { + final Ref r = n.ref; + if (r.pack == pack && r.position == position) { + final ByteWindow v = r.get(); + if (v != null) { + hit(r); + return v; + } + n.kill(); + break; + } + } + return null; + } + + private void hit(final Ref r) { + // We don't need to be 100% accurate here. Its sufficient that at least + // one thread performs the increment. Any other concurrent access at + // exactly the same time can simply use the same clock value. + // + // Consequently we attempt the set, but we don't try to recover should + // it fail. This is why we don't use getAndIncrement() here. + // + final long c = clock.get(); + clock.compareAndSet(c, c + 1); + r.lastAccess = c; + } + + private void evict() { + while (isFull()) { + int ptr = rng.nextInt(tableSize); + Entry old = null; + int slot = 0; + for (int b = evictBatch - 1; b >= 0; b--, ptr++) { + if (tableSize <= ptr) + ptr = 0; + for (Entry e = table.get(ptr); e != null; e = e.next) { + if (e.dead) + continue; + if (old == null || e.ref.lastAccess < old.ref.lastAccess) { + old = e; + slot = ptr; + } + } + } + if (old != null) { + old.kill(); + gc(); + final Entry e1 = table.get(slot); + table.compareAndSet(slot, e1, clean(e1)); + } + } + } + + /** + * Clear every entry from the cache. + *

+ * This is a last-ditch effort to clear out the cache, such as before it + * gets replaced by another cache that is configured differently. This + * method tries to force every cached entry through {@link #clear(Ref)} to + * ensure that resources are correctly accounted for and cleaned up by the + * subclass. A concurrent reader loading entries while this method is + * running may cause resource accounting failures. + */ + private void removeAll() { + for (int s = 0; s < tableSize; s++) { + Entry e1; + do { + e1 = table.get(s); + for (Entry e = e1; e != null; e = e.next) + e.kill(); + } while (!table.compareAndSet(s, e1, null)); + } + gc(); + } + + /** + * Clear all entries related to a single file. + *

+ * Typically this method is invoked during {@link PackFile#close()}, when we + * know the pack is never going to be useful to us again (for example, it no + * longer exists on disk). A concurrent reader loading an entry from this + * same pack may cause the pack to become stuck in the cache anyway. + * + * @param pack + * the file to purge all entries of. + */ + private void removeAll(final PackFile pack) { + for (int s = 0; s < tableSize; s++) { + final Entry e1 = table.get(s); + boolean hasDead = false; + for (Entry e = e1; e != null; e = e.next) { + if (e.ref.pack == pack) { + e.kill(); + hasDead = true; + } else if (e.dead) + hasDead = true; + } + if (hasDead) + table.compareAndSet(s, e1, clean(e1)); + } + gc(); + } + + @SuppressWarnings("unchecked") + private void gc() { + Ref r; + while ((r = (Ref) queue.poll()) != null) { + // Sun's Java 5 and 6 implementation have a bug where a Reference + // can be enqueued and dequeued twice on the same reference queue + // due to a race condition within ReferenceQueue.enqueue(Reference). + // + // http://bugs.sun.com/bugdatabase/view_bug.do?bug_id=6837858 + // + // We CANNOT permit a Reference to come through us twice, as it will + // skew the resource counters we maintain. Our canClear() check here + // provides a way to skip the redundant dequeues, if any. + // + if (r.canClear()) { + clear(r); + + boolean found = false; + final int s = slot(r.pack, r.position); + final Entry e1 = table.get(s); + for (Entry n = e1; n != null; n = n.next) { + if (n.ref == r) { + n.dead = true; + found = true; + break; + } + } + if (found) + table.compareAndSet(s, e1, clean(e1)); + } + } + } + + private int slot(final PackFile pack, final long position) { + return (hash(pack.hash, position) >>> 1) % tableSize; + } + + private Lock lock(final PackFile pack, final long position) { + return locks[(hash(pack.hash, position) >>> 1) % locks.length]; + } + + private static Entry clean(Entry top) { + while (top != null && top.dead) { + top.ref.enqueue(); + top = top.next; + } + if (top == null) + return null; + final Entry n = clean(top.next); + return n == top.next ? top : new Entry(n, top.ref); + } + + private static class Entry { + /** Next entry in the hash table's chain list. */ + final Entry next; + + /** The referenced object. */ + final Ref ref; + + /** + * Marked true when ref.get() returns null and the ref is dead. + *

+ * A true here indicates that the ref is no longer accessible, and that + * we therefore need to eventually purge this Entry object out of the + * bucket's chain. + */ + volatile boolean dead; + + Entry(final Entry n, final Ref r) { + next = n; + ref = r; + } + + final void kill() { + dead = true; + ref.enqueue(); + } + } + + /** A soft reference wrapped around a cached object. */ + private static class Ref extends SoftReference { + final PackFile pack; + + final long position; + + final int size; + + long lastAccess; + + private boolean cleared; + + protected Ref(final PackFile pack, final long position, + final ByteWindow v, final ReferenceQueue queue) { + super(v, queue); + this.pack = pack; + this.position = position; + this.size = v.size(); + } + + final synchronized boolean canClear() { + if (cleared) + return false; + cleared = true; + return true; + } + } + + private static final class Lock { + // Used only for its implicit monitor. + } +} diff --git a/org.eclipse.jgit/src/org/eclipse/jgit/storage/file/WindowCacheConfig.java b/org.eclipse.jgit/src/org/eclipse/jgit/storage/file/WindowCacheConfig.java new file mode 100644 index 0000000000..48d7018e42 --- /dev/null +++ b/org.eclipse.jgit/src/org/eclipse/jgit/storage/file/WindowCacheConfig.java @@ -0,0 +1,178 @@ +/* + * 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 org.eclipse.jgit.lib.Config; + +/** Configuration parameters for {@link WindowCache}. */ +public class WindowCacheConfig { + /** 1024 (number of bytes in one kibibyte/kilobyte) */ + public static final int KB = 1024; + + /** 1024 {@link #KB} (number of bytes in one mebibyte/megabyte) */ + public static final int MB = 1024 * KB; + + private int packedGitOpenFiles; + + private long packedGitLimit; + + private int packedGitWindowSize; + + private boolean packedGitMMAP; + + private int deltaBaseCacheLimit; + + /** Create a default configuration. */ + public WindowCacheConfig() { + packedGitOpenFiles = 128; + packedGitLimit = 10 * MB; + packedGitWindowSize = 8 * KB; + packedGitMMAP = false; + deltaBaseCacheLimit = 10 * MB; + } + + /** + * @return maximum number of streams to open at a time. Open packs count + * against the process limits. Default is 128. + */ + public int getPackedGitOpenFiles() { + return packedGitOpenFiles; + } + + /** + * @param fdLimit + * maximum number of streams to open at a time. Open packs count + * against the process limits + */ + public void setPackedGitOpenFiles(final int fdLimit) { + packedGitOpenFiles = fdLimit; + } + + /** + * @return maximum number bytes of heap memory to dedicate to caching pack + * file data. Default is 10 MB. + */ + public long getPackedGitLimit() { + return packedGitLimit; + } + + /** + * @param newLimit + * maximum number bytes of heap memory to dedicate to caching + * pack file data. + */ + public void setPackedGitLimit(final long newLimit) { + packedGitLimit = newLimit; + } + + /** + * @return size in bytes of a single window mapped or read in from the pack + * file. Default is 8 KB. + */ + public int getPackedGitWindowSize() { + return packedGitWindowSize; + } + + /** + * @param newSize + * size in bytes of a single window read in from the pack file. + */ + public void setPackedGitWindowSize(final int newSize) { + packedGitWindowSize = newSize; + } + + /** + * @return true enables use of Java NIO virtual memory mapping for windows; + * false reads entire window into a byte[] with standard read calls. + * Default false. + */ + public boolean isPackedGitMMAP() { + return packedGitMMAP; + } + + /** + * @param usemmap + * true enables use of Java NIO virtual memory mapping for + * windows; false reads entire window into a byte[] with standard + * read calls. + */ + public void setPackedGitMMAP(final boolean usemmap) { + packedGitMMAP = usemmap; + } + + /** + * @return maximum number of bytes to cache in {@link UnpackedObjectCache} + * for inflated, recently accessed objects, without delta chains. + * Default 10 MB. + */ + public int getDeltaBaseCacheLimit() { + return deltaBaseCacheLimit; + } + + /** + * @param newLimit + * maximum number of bytes to cache in + * {@link UnpackedObjectCache} for inflated, recently accessed + * objects, without delta chains. + */ + public void setDeltaBaseCacheLimit(final int newLimit) { + deltaBaseCacheLimit = newLimit; + } + + /** + * Update properties by setting fields from the configuration. + *

+ * If a property is not defined in the configuration, then it is left + * unmodified. + * + * @param rc configuration to read properties from. + */ + public void fromConfig(final Config rc) { + setPackedGitOpenFiles(rc.getInt("core", null, "packedgitopenfiles", getPackedGitOpenFiles())); + setPackedGitLimit(rc.getLong("core", null, "packedgitlimit", getPackedGitLimit())); + setPackedGitWindowSize(rc.getInt("core", null, "packedgitwindowsize", getPackedGitWindowSize())); + setPackedGitMMAP(rc.getBoolean("core", null, "packedgitmmap", isPackedGitMMAP())); + setDeltaBaseCacheLimit(rc.getInt("core", null, "deltabasecachelimit", getDeltaBaseCacheLimit())); + } +} diff --git a/org.eclipse.jgit/src/org/eclipse/jgit/storage/file/WindowCursor.java b/org.eclipse.jgit/src/org/eclipse/jgit/storage/file/WindowCursor.java new file mode 100644 index 0000000000..a88261162d --- /dev/null +++ b/org.eclipse.jgit/src/org/eclipse/jgit/storage/file/WindowCursor.java @@ -0,0 +1,222 @@ +/* + * Copyright (C) 2008-2009, Google Inc. + * Copyright (C) 2006-2008, Shawn O. Pearce + * 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.util.zip.DataFormatException; +import java.util.zip.Inflater; + +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.lib.ObjectReuseAsIs; +import org.eclipse.jgit.lib.ObjectToPack; +import org.eclipse.jgit.lib.PackOutputStream; +import org.eclipse.jgit.lib.PackWriter; +import org.eclipse.jgit.revwalk.RevObject; + +/** Active handle to a ByteWindow. */ +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]; + + private Inflater inf; + + private ByteWindow window; + + private final FileObjectDatabase db; + + WindowCursor(FileObjectDatabase db) { + this.db = db; + } + + public boolean hasObject(AnyObjectId objectId) throws IOException { + return db.hasObject(objectId); + } + + public ObjectLoader openObject(AnyObjectId objectId, int typeHint) + throws MissingObjectException, 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); + } + return ldr; + } + + 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.copyFromPack.copyAsIs(out, src, this); + } + + /** + * Copy bytes from the window to a caller supplied buffer. + * + * @param pack + * the file the desired window is stored within. + * @param position + * position within the file to read from. + * @param dstbuf + * destination buffer to copy into. + * @param dstoff + * offset within dstbuf to start copying into. + * @param cnt + * number of bytes to copy. This value may exceed the number of + * bytes remaining in the window starting at offset + * pos. + * @return number of bytes actually copied; this may be less than + * cnt if cnt 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. + */ + int copy(final PackFile pack, long position, final byte[] dstbuf, + int dstoff, final int cnt) throws IOException { + final long length = pack.length; + int need = cnt; + while (need > 0 && position < length) { + pin(pack, position); + final int r = window.copy(position, dstbuf, dstoff, need); + position += r; + dstoff += r; + need -= r; + } + return cnt - need; + } + + /** + * Pump bytes into the supplied inflater as input. + * + * @param pack + * the file the desired window is stored within. + * @param position + * position within the file to read from. + * @param dstbuf + * destination buffer the inflater should output decompressed + * data to. + * @param dstoff + * current offset within dstbuf to inflate into. + * @return updated dstoff based on the number of bytes + * successfully inflated into dstbuf. + * @throws IOException + * this cursor does not match the provider or id and the proper + * window could not be acquired through the provider's cache. + * @throws DataFormatException + * the inflater encountered an invalid chunk of data. Data + * stream corruption is likely. + */ + 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; + } + } + + 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(); + return inf; + } + + private void prepareInflater() { + if (inf == null) + inf = InflaterCache.get(); + else + inf.reset(); + } + + private void pin(final PackFile pack, final long position) + throws IOException { + final ByteWindow w = window; + if (w == null || !w.contains(pack, position)) { + // If memory is low, we may need what is in our window field to + // be cleaned up by the GC during the get for the next window. + // So we always clear it, even though we are just going to set + // it again. + // + window = null; + window = WindowCache.get(pack, position); + } + } + + /** Release the current window cursor. */ + public void release() { + window = null; + try { + InflaterCache.release(inf); + } finally { + inf = null; + } + } +} 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..8c336c5255 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; 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..98ecc5540f 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; 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 27505bef65..72d73eb59b 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. */ 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 0cd6733695..5a37825cea 100644 --- a/org.eclipse.jgit/src/org/eclipse/jgit/transport/IndexPack.java +++ b/org.eclipse.jgit/src/org/eclipse/jgit/transport/IndexPack.java @@ -75,11 +75,11 @@ 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.ObjectReader; +import org.eclipse.jgit.storage.file.PackIndexWriter; +import org.eclipse.jgit.storage.file.PackLock; import org.eclipse.jgit.util.NB; /** Indexes Git pack files for local use. */ 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 91105cc8a3..1597a35fe9 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; @@ -86,6 +85,7 @@ import org.eclipse.jgit.revwalk.RevFlag; import org.eclipse.jgit.revwalk.RevObject; 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; 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 ae54a155a1..c9b18be1f8 100644 --- a/org.eclipse.jgit/src/org/eclipse/jgit/transport/TransportLocal.java +++ b/org.eclipse.jgit/src/org/eclipse/jgit/transport/TransportLocal.java @@ -60,8 +60,8 @@ import org.eclipse.jgit.JGitText; import org.eclipse.jgit.errors.NotSupportedException; import org.eclipse.jgit.errors.TransportException; import org.eclipse.jgit.lib.Constants; -import org.eclipse.jgit.lib.FileRepository; 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; 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 c9d2e9e193..89dd6b4904 100644 --- a/org.eclipse.jgit/src/org/eclipse/jgit/transport/WalkFetchConnection.java +++ b/org.eclipse.jgit/src/org/eclipse/jgit/transport/WalkFetchConnection.java @@ -68,15 +68,11 @@ import org.eclipse.jgit.lib.Constants; import org.eclipse.jgit.lib.FileMode; import org.eclipse.jgit.lib.MutableObjectId; import org.eclipse.jgit.lib.ObjectChecker; -import org.eclipse.jgit.lib.ObjectDirectory; import org.eclipse.jgit.lib.ObjectId; import org.eclipse.jgit.lib.ObjectInserter; -import org.eclipse.jgit.lib.PackIndex; -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.lib.UnpackedObjectLoader; import org.eclipse.jgit.revwalk.DateRevQueue; import org.eclipse.jgit.revwalk.RevCommit; import org.eclipse.jgit.revwalk.RevFlag; @@ -84,6 +80,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.UnpackedObjectLoader; import org.eclipse.jgit.treewalk.TreeWalk; /** 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/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. -- cgit v1.2.3 From acb7be2c5adb270d21182d389ce93f3bca38cd29 Mon Sep 17 00:00:00 2001 From: "Shawn O. Pearce" Date: Mon, 28 Jun 2010 11:54:58 -0700 Subject: Refactor Repository.openObject to be Repository.open We drop the "Object" suffix, because its pretty clear here that we want to open an object, given that we pass in AnyObjectId as the main parameter. We also fix the calling convention to throw a MissingObjectException or IncorrectObjectTypeException, so that callers don't have to do this error checking themselves. Change-Id: I72c43353cea8372278b032f5086d52082c1eee39 Signed-off-by: Shawn O. Pearce --- .../src/org/eclipse/jgit/junit/TestRepository.java | 4 +- .../src/org/eclipse/jgit/pgm/Diff.java | 8 +- .../src/org/eclipse/jgit/pgm/Tag.java | 9 +- .../jgit/storage/file/ConcurrentRepackTest.java | 4 +- .../jgit/storage/file/T0004_PackReader.java | 2 +- .../jgit/storage/file/WindowCacheGetTest.java | 2 +- .../jgit/transport/ReceivePackRefFilterTest.java | 10 +- .../src/org/eclipse/jgit/lib/BlobBasedConfig.java | 13 +-- .../src/org/eclipse/jgit/lib/FileTreeEntry.java | 2 +- .../src/org/eclipse/jgit/lib/GitIndex.java | 4 +- .../src/org/eclipse/jgit/lib/ObjectDatabase.java | 30 ++++- .../src/org/eclipse/jgit/lib/ObjectReader.java | 7 +- .../src/org/eclipse/jgit/lib/Repository.java | 125 +++++++++++---------- .../src/org/eclipse/jgit/lib/Tree.java | 6 +- 14 files changed, 124 insertions(+), 102 deletions(-) (limited to 'org.eclipse.jgit.pgm/src/org') 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 daa959f116..a179773d15 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 @@ -563,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); } @@ -573,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); } diff --git a/org.eclipse.jgit.pgm/src/org/eclipse/jgit/pgm/Diff.java b/org.eclipse.jgit.pgm/src/org/eclipse/jgit/pgm/Diff.java index a5db453688..e1f78244b1 100644 --- a/org.eclipse.jgit.pgm/src/org/eclipse/jgit/pgm/Diff.java +++ b/org.eclipse.jgit.pgm/src/org/eclipse/jgit/pgm/Diff.java @@ -50,12 +50,10 @@ import java.io.PrintStream; import java.util.ArrayList; import java.util.List; -import org.kohsuke.args4j.Argument; -import org.kohsuke.args4j.Option; - import org.eclipse.jgit.diff.DiffFormatter; import org.eclipse.jgit.diff.MyersDiff; import org.eclipse.jgit.diff.RawText; +import org.eclipse.jgit.lib.Constants; import org.eclipse.jgit.lib.FileMode; import org.eclipse.jgit.lib.ObjectId; import org.eclipse.jgit.pgm.opt.PathTreeFilterHandler; @@ -63,6 +61,8 @@ import org.eclipse.jgit.treewalk.AbstractTreeIterator; import org.eclipse.jgit.treewalk.TreeWalk; import org.eclipse.jgit.treewalk.filter.AndTreeFilter; import org.eclipse.jgit.treewalk.filter.TreeFilter; +import org.kohsuke.args4j.Argument; +import org.kohsuke.args4j.Option; @Command(common = true, usage = "usage_ShowDiffs") class Diff extends TextBuiltin { @@ -125,7 +125,7 @@ class Diff extends TextBuiltin { private RawText getRawText(ObjectId id) throws IOException { if (id.equals(ObjectId.zeroId())) return new RawText(new byte[] { }); - return new RawText(db.openBlob(id).getCachedBytes()); + return new RawText(db.open(id, Constants.OBJ_BLOB).getCachedBytes()); } } 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.test/tst/org/eclipse/jgit/storage/file/ConcurrentRepackTest.java b/org.eclipse.jgit.test/tst/org/eclipse/jgit/storage/file/ConcurrentRepackTest.java index 8e7df41a8d..c8f2aad759 100644 --- a/org.eclipse.jgit.test/tst/org/eclipse/jgit/storage/file/ConcurrentRepackTest.java +++ b/org.eclipse.jgit.test/tst/org/eclipse/jgit/storage/file/ConcurrentRepackTest.java @@ -158,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"); @@ -173,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); diff --git a/org.eclipse.jgit.test/tst/org/eclipse/jgit/storage/file/T0004_PackReader.java b/org.eclipse.jgit.test/tst/org/eclipse/jgit/storage/file/T0004_PackReader.java index 70016f9ea1..b8bcca2e14 100644 --- a/org.eclipse.jgit.test/tst/org/eclipse/jgit/storage/file/T0004_PackReader.java +++ b/org.eclipse.jgit.test/tst/org/eclipse/jgit/storage/file/T0004_PackReader.java @@ -79,7 +79,7 @@ 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()); diff --git a/org.eclipse.jgit.test/tst/org/eclipse/jgit/storage/file/WindowCacheGetTest.java b/org.eclipse.jgit.test/tst/org/eclipse/jgit/storage/file/WindowCacheGetTest.java index 89b91558ba..177a1d5cf7 100644 --- a/org.eclipse.jgit.test/tst/org/eclipse/jgit/storage/file/WindowCacheGetTest.java +++ b/org.eclipse.jgit.test/tst/org/eclipse/jgit/storage/file/WindowCacheGetTest.java @@ -126,7 +126,7 @@ 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()); 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 cb1ce6f382..0bcdbcea41 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 @@ -299,8 +299,8 @@ public class ReceivePackRefFilterTest extends LocalDiskRepositoryTestCase { // final TemporaryBuffer.Heap pack = new TemporaryBuffer.Heap(64); 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); @@ -341,8 +341,8 @@ public class ReceivePackRefFilterTest extends LocalDiskRepositoryTestCase { // final TemporaryBuffer.Heap pack = new TemporaryBuffer.Heap(64); 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); @@ -381,7 +381,7 @@ public class ReceivePackRefFilterTest extends LocalDiskRepositoryTestCase { // final TemporaryBuffer.Heap pack = new TemporaryBuffer.Heap(64); packHeader(pack, 1); - copy(pack, src.openObject(N)); + copy(pack, src.open(N)); digest(pack); final TemporaryBuffer.Heap inBuf = new TemporaryBuffer.Heap(256); 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/FileTreeEntry.java b/org.eclipse.jgit/src/org/eclipse/jgit/lib/FileTreeEntry.java index 3da91dd2a2..5bb3f62da3 100644 --- a/org.eclipse.jgit/src/org/eclipse/jgit/lib/FileTreeEntry.java +++ b/org.eclipse.jgit/src/org/eclipse/jgit/lib/FileTreeEntry.java @@ -93,7 +93,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 929cd2d2ee..0495d38a11 100644 --- a/org.eclipse.jgit/src/org/eclipse/jgit/lib/GitIndex.java +++ b/org.eclipse.jgit/src/org/eclipse/jgit/lib/GitIndex.java @@ -433,7 +433,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; @@ -873,7 +873,7 @@ public class GitIndex { * @throws IOException */ public void checkoutEntry(File wd, Entry e) throws IOException { - ObjectLoader ol = db.openBlob(e.sha1); + ObjectLoader ol = db.open(e.sha1, Constants.OBJ_BLOB); byte[] bytes = ol.getBytes(); File file = new File(wd, e.getName()); file.delete(); 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 c2d2beda9a..a095663bf4 100644 --- a/org.eclipse.jgit/src/org/eclipse/jgit/lib/ObjectDatabase.java +++ b/org.eclipse.jgit/src/org/eclipse/jgit/lib/ObjectDatabase.java @@ -45,6 +45,7 @@ package org.eclipse.jgit.lib; import java.io.IOException; +import org.eclipse.jgit.errors.IncorrectObjectTypeException; import org.eclipse.jgit.errors.MissingObjectException; /** @@ -143,9 +144,36 @@ public abstract class ObjectDatabase { */ public ObjectLoader openObject(final AnyObjectId objectId) throws IOException { + return openObject(objectId, ObjectReader.OBJ_ANY); + } + + /** + * Open an object from this database. + *

+ * This is a one-shot call interface which may be faster than allocating a + * {@link #newReader()} to perform the lookup. + * + * @param objectId + * 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 openObject(AnyObjectId objectId, int typeHint) + throws MissingObjectException, IncorrectObjectTypeException, + IOException { final ObjectReader or = newReader(); try { - return or.openObject(objectId); + return or.openObject(objectId, typeHint); } finally { or.release(); } diff --git a/org.eclipse.jgit/src/org/eclipse/jgit/lib/ObjectReader.java b/org.eclipse.jgit/src/org/eclipse/jgit/lib/ObjectReader.java index e8961bdce2..001b18c4c8 100644 --- a/org.eclipse.jgit/src/org/eclipse/jgit/lib/ObjectReader.java +++ b/org.eclipse.jgit/src/org/eclipse/jgit/lib/ObjectReader.java @@ -86,6 +86,7 @@ public abstract class ObjectReader { * @throws MissingObjectException * the object does not exist. * @throws IOException + * the object store cannot be accessed. */ public ObjectLoader openObject(AnyObjectId objectId) throws MissingObjectException, IOException { @@ -97,7 +98,7 @@ public abstract class ObjectReader { * * @param objectId * identity of the object to open. - *@param typeHint + * @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. @@ -108,9 +109,11 @@ public abstract class ObjectReader { * 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 openObject(AnyObjectId objectId, int typeHint) - throws MissingObjectException, IncorrectObjectTypeException, IOException; + throws MissingObjectException, IncorrectObjectTypeException, + IOException; /** * Release any resources used by this reader. 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 bab349e6fb..a2d9f61e0a 100644 --- a/org.eclipse.jgit/src/org/eclipse/jgit/lib/Repository.java +++ b/org.eclipse.jgit/src/org/eclipse/jgit/lib/Repository.java @@ -221,41 +221,49 @@ public abstract class Repository { } /** - * @param id - * SHA-1 of an object. + * Open an object from this repository. + *

+ * This is a one-shot call interface which may be faster than allocating a + * {@link #newObjectReader()} to perform the lookup. * - * @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 { - try { - return getObjectDatabase().openObject(id); - } catch (MissingObjectException notFound) { - // Legacy API, return null - return null; - } - } - - /** - * @param id - * SHA'1 of a blob - * @return an {@link ObjectLoader} for accessing the data of a named blob + * @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 openBlob(final ObjectId id) throws IOException { - return openObject(id); + public ObjectLoader open(final AnyObjectId objectId) + throws MissingObjectException, IOException { + return getObjectDatabase().openObject(objectId); } /** - * @param id - * SHA'1 of a tree - * @return an {@link ObjectLoader} for accessing the data of a named tree + * Open an object from this repository. + *

+ * This is a one-shot call interface which may be faster than allocating a + * {@link #newObjectReader()} to perform the lookup. + * + * @param objectId + * 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 openTree(final ObjectId id) throws IOException { - return openObject(id); + public ObjectLoader open(AnyObjectId objectId, int typeHint) + throws MissingObjectException, IncorrectObjectTypeException, + IOException { + return getObjectDatabase().openObject(objectId, typeHint); } /** @@ -284,19 +292,22 @@ public abstract class Repository { * @throws IOException */ 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; @@ -314,18 +325,13 @@ public abstract class Repository { * @throws IOException for I/O error or unexpected object type. */ 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()); } /** @@ -351,10 +357,13 @@ public abstract class Repository { * @throws IOException for I/O error or unexpected object type. */ 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); @@ -367,16 +376,6 @@ public abstract 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. * @@ -397,12 +396,14 @@ public abstract class Repository { * @throws IOException for I/O error or unexpected object type. */ 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); } 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 2aa3098f17..f3b5409917 100644 --- a/org.eclipse.jgit/src/org/eclipse/jgit/lib/Tree.java +++ b/org.eclipse.jgit/src/org/eclipse/jgit/lib/Tree.java @@ -530,10 +530,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()); } } -- cgit v1.2.3 From 121d009b9b7159ca0bd73a7c25b925516852a026 Mon Sep 17 00:00:00 2001 From: "Shawn O. Pearce" Date: Mon, 28 Jun 2010 18:25:22 -0700 Subject: Use ObjectReader in RevWalk, TreeWalk We don't actually need a Repository object here, just an ObjectReader that can load content for us. So change the API to depend on that. However, this breaks the asCommit and asTag legacy translation methods on RevCommit and RevTag, so we still have to keep the Repository inside of RevWalk for those two types. Hopefully we can drop those in the future, and then drop the Repository off the RevWalk. Change-Id: Iba983e48b663790061c43ae9ffbb77dfe6f4818e Signed-off-by: Shawn O. Pearce --- .../src/org/eclipse/jgit/junit/TestRepository.java | 2 +- .../jgit/pgm/opt/AbstractTreeIteratorHandler.java | 2 +- .../jgit/treewalk/AbstractTreeIteratorTest.java | 4 +- .../jgit/treewalk/EmptyTreeIteratorTest.java | 7 ++- .../jgit/treewalk/FileTreeIteratorTest.java | 4 +- .../jgit/dircache/DirCacheBuildIterator.java | 4 +- .../org/eclipse/jgit/dircache/DirCacheBuilder.java | 28 +++++----- .../eclipse/jgit/dircache/DirCacheIterator.java | 4 +- .../src/org/eclipse/jgit/lib/ObjectReader.java | 22 +++++++- .../src/org/eclipse/jgit/merge/Merger.java | 14 ++--- .../jgit/merge/StrategySimpleTwoWayInCore.java | 2 +- .../eclipse/jgit/revwalk/MergeBaseGenerator.java | 2 +- .../src/org/eclipse/jgit/revwalk/ObjectWalk.java | 25 +++++++-- .../org/eclipse/jgit/revwalk/PendingGenerator.java | 4 +- .../src/org/eclipse/jgit/revwalk/RevCommit.java | 3 +- .../src/org/eclipse/jgit/revwalk/RevObject.java | 2 +- .../src/org/eclipse/jgit/revwalk/RevTag.java | 2 +- .../src/org/eclipse/jgit/revwalk/RevWalk.java | 52 ++++++++++++----- .../eclipse/jgit/revwalk/RewriteTreeFilter.java | 2 +- .../org/eclipse/jgit/storage/pack/PackWriter.java | 2 +- .../org/eclipse/jgit/transport/ReceivePack.java | 2 +- .../org/eclipse/jgit/transport/RefAdvertiser.java | 11 ++-- .../jgit/treewalk/AbstractTreeIterator.java | 22 ++++---- .../eclipse/jgit/treewalk/CanonicalTreeParser.java | 65 +++++++++------------- .../eclipse/jgit/treewalk/EmptyTreeIterator.java | 4 +- .../eclipse/jgit/treewalk/FileTreeIterator.java | 4 +- .../jgit/treewalk/NameConflictTreeWalk.java | 13 ++++- .../src/org/eclipse/jgit/treewalk/TreeWalk.java | 55 +++++++++++------- 28 files changed, 221 insertions(+), 142 deletions(-) (limited to 'org.eclipse.jgit.pgm/src/org') 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 a179773d15..96b616889c 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 @@ -272,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); 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 d7b98c5098..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 @@ -123,7 +123,7 @@ public class AbstractTreeIteratorHandler extends final CanonicalTreeParser p = new CanonicalTreeParser(); 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/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/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 adb8a8d77f..ab75138434 100644 --- a/org.eclipse.jgit/src/org/eclipse/jgit/dircache/DirCacheBuilder.java +++ b/org.eclipse.jgit/src/org/eclipse/jgit/dircache/DirCacheBuilder.java @@ -164,22 +164,22 @@ public class DirCacheBuilder extends BaseDirCacheEditor { */ public void addTree(final byte[] pathPrefix, final int stage, final Repository db, final AnyObjectId tree) throws IOException { - final TreeWalk tw = new TreeWalk(db); - tw.reset(); - final ObjectReader curs = db.newObjectReader(); + final ObjectReader reader = db.newObjectReader(); try { - tw.addTree(new CanonicalTreeParser(pathPrefix, db, tree - .toObjectId(), curs)); + final TreeWalk tw = new TreeWalk(reader); + tw.reset(); + tw.addTree(new CanonicalTreeParser(pathPrefix, reader, tree + .toObjectId())); + tw.setRecursive(true); + if (tw.next()) { + final DirCacheEntry newEntry = toEntry(stage, tw); + beforeAdd(newEntry); + fastAdd(newEntry); + while (tw.next()) + fastAdd(toEntry(stage, tw)); + } } finally { - curs.release(); - } - tw.setRecursive(true); - if (tw.next()) { - final DirCacheEntry newEntry = toEntry(stage, tw); - beforeAdd(newEntry); - fastAdd(newEntry); - while (tw.next()) - fastAdd(toEntry(stage, tw)); + reader.release(); } } 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/lib/ObjectReader.java b/org.eclipse.jgit/src/org/eclipse/jgit/lib/ObjectReader.java index e9afc5043e..1af3cb2dea 100644 --- a/org.eclipse.jgit/src/org/eclipse/jgit/lib/ObjectReader.java +++ b/org.eclipse.jgit/src/org/eclipse/jgit/lib/ObjectReader.java @@ -69,8 +69,28 @@ public abstract class ObjectReader { * 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); + open(objectId, typeHint); return true; } catch (MissingObjectException notFound) { return false; 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 d95b11159c..68d60c0077 100644 --- a/org.eclipse.jgit/src/org/eclipse/jgit/merge/Merger.java +++ b/org.eclipse.jgit/src/org/eclipse/jgit/merge/Merger.java @@ -70,6 +70,9 @@ 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; @@ -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); } /** @@ -153,6 +157,7 @@ public abstract class Merger { } finally { if (inserter != null) inserter.release(); + reader.release(); } } @@ -207,12 +212,7 @@ public abstract class Merger { */ protected AbstractTreeIterator openTree(final AnyObjectId treeId) throws IncorrectObjectTypeException, IOException { - final ObjectReader curs = db.newObjectReader(); - 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 891abe0580..86ba724746 100644 --- a/org.eclipse.jgit/src/org/eclipse/jgit/merge/StrategySimpleTwoWayInCore.java +++ b/org.eclipse.jgit/src/org/eclipse/jgit/merge/StrategySimpleTwoWayInCore.java @@ -100,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(); } 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..9393e2d17e 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); } } @@ -403,7 +416,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 +432,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 14f4836d7a..a19f4d83ea 100644 --- a/org.eclipse.jgit/src/org/eclipse/jgit/revwalk/RevObject.java +++ b/org.eclipse.jgit/src/org/eclipse/jgit/revwalk/RevObject.java @@ -77,7 +77,7 @@ public abstract class RevObject extends ObjectId { final byte[] loadCanonical(final RevWalk walk) throws IOException, MissingObjectException, IncorrectObjectTypeException, CorruptObjectException { - return walk.curs.open(this, getType()).getCachedBytes(); + 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 60c8024b3c..51de7c4a51 100644 --- a/org.eclipse.jgit/src/org/eclipse/jgit/revwalk/RevWalk.java +++ b/org.eclipse.jgit/src/org/eclipse/jgit/revwalk/RevWalk.java @@ -157,9 +157,10 @@ public class RevWalk implements Iterable { private static final int APP_FLAGS = -1 & ~((1 << RESERVED_FLAGS) - 1); - final Repository db; + /** Exists ONLY to support legacy Tag and Commit objects. */ + final Repository repository; - final ObjectReader curs; + final ObjectReader reader; final MutableObjectId idBuffer; @@ -189,11 +190,29 @@ public class RevWalk implements Iterable { * 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 = db.newObjectReader(); + 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(); roots = new ArrayList(); @@ -205,13 +224,19 @@ public class RevWalk implements Iterable { 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. + *

+ * 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(); } /** @@ -720,7 +745,7 @@ public class RevWalk implements Iterable { throws MissingObjectException, IOException { RevObject r = objects.get(id); if (r == null) { - final ObjectLoader ldr = curs.open(id); + final ObjectLoader ldr = reader.open(id); final byte[] data = ldr.getCachedBytes(); final int type = ldr.getType(); switch (type) { @@ -991,7 +1016,7 @@ public class RevWalk implements Iterable { } } - curs.release(); + reader.release(); roots.clear(); queue = new DateRevQueue(); pending = new StartGenerator(this); @@ -1006,11 +1031,12 @@ public class RevWalk implements Iterable { * 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 d7e5c8028f..023ed45fd2 100644 --- a/org.eclipse.jgit/src/org/eclipse/jgit/revwalk/RewriteTreeFilter.java +++ b/org.eclipse.jgit/src/org/eclipse/jgit/revwalk/RewriteTreeFilter.java @@ -77,7 +77,7 @@ class RewriteTreeFilter extends RevFilter { private final TreeWalk pathFilter; RewriteTreeFilter(final RevWalk walker, final TreeFilter t) { - pathFilter = new TreeWalk(walker.db); + pathFilter = new TreeWalk(walker.reader); pathFilter.setFilter(t); pathFilter.setRecursive(t.shouldBeRecursive()); } 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 index c7e3c0c9b0..3fab3f7470 100644 --- a/org.eclipse.jgit/src/org/eclipse/jgit/storage/pack/PackWriter.java +++ b/org.eclipse.jgit/src/org/eclipse/jgit/storage/pack/PackWriter.java @@ -739,7 +739,7 @@ public class PackWriter { final Collection uninterestingObjects) throws MissingObjectException, IOException, IncorrectObjectTypeException { - final ObjectWalk walker = new ObjectWalk(db); + final ObjectWalk walker = new ObjectWalk(reader); walker.setRetainBody(false); walker.sort(RevSort.COMMIT_TIME_DESC); if (thin) 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 1597a35fe9..2475b9acfe 100644 --- a/org.eclipse.jgit/src/org/eclipse/jgit/transport/ReceivePack.java +++ b/org.eclipse.jgit/src/org/eclipse/jgit/transport/ReceivePack.java @@ -696,7 +696,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(); 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 532cc11a7f..df0afe73fa 100644 --- a/org.eclipse.jgit/src/org/eclipse/jgit/transport/RefAdvertiser.java +++ b/org.eclipse.jgit/src/org/eclipse/jgit/transport/RefAdvertiser.java @@ -54,6 +54,7 @@ import org.eclipse.jgit.lib.Constants; import org.eclipse.jgit.lib.ObjectId; import org.eclipse.jgit.lib.Ref; import org.eclipse.jgit.lib.RefComparator; +import org.eclipse.jgit.lib.Repository; import org.eclipse.jgit.revwalk.RevFlag; import org.eclipse.jgit.revwalk.RevObject; import org.eclipse.jgit.revwalk.RevTag; @@ -124,7 +125,7 @@ public abstract class RefAdvertiser { *

    *
  • {@link #send(Map)} *
  • {@link #advertiseHave(AnyObjectId)} - *
  • {@link #includeAdditionalHaves()} + *
  • {@link #includeAdditionalHaves(Repository)} *
* * @param deref @@ -142,7 +143,7 @@ public abstract class RefAdvertiser { *
    *
  • {@link #send(Map)} *
  • {@link #advertiseHave(AnyObjectId)} - *
  • {@link #includeAdditionalHaves()} + *
  • {@link #includeAdditionalHaves(Repository)} *
* * @param name @@ -210,12 +211,14 @@ 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 { - for (ObjectId id : walk.getRepository().getAdditionalHaves()) + public void includeAdditionalHaves(Repository src) throws IOException { + for (ObjectId id : src.getAdditionalHaves()) advertiseHave(id); } 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 73357a4dfe..e74f13e851 100644 --- a/org.eclipse.jgit/src/org/eclipse/jgit/treewalk/AbstractTreeIterator.java +++ b/org.eclipse.jgit/src/org/eclipse/jgit/treewalk/AbstractTreeIterator.java @@ -55,7 +55,6 @@ 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.ObjectReader; import org.eclipse.jgit.treewalk.filter.TreeFilter; @@ -432,8 +431,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 this. * - * @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 @@ -441,8 +440,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. @@ -460,12 +460,10 @@ public abstract class AbstractTreeIterator { * the caller would not be able to exit out of the subtree iterator * correctly and return to continue walking this. * - * @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 @@ -473,10 +471,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 ObjectReader 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 fc088d7760..8e4094a055 100644 --- a/org.eclipse.jgit/src/org/eclipse/jgit/treewalk/CanonicalTreeParser.java +++ b/org.eclipse.jgit/src/org/eclipse/jgit/treewalk/CanonicalTreeParser.java @@ -55,7 +55,6 @@ import org.eclipse.jgit.lib.FileMode; import org.eclipse.jgit.lib.MutableObjectId; import org.eclipse.jgit.lib.ObjectId; import org.eclipse.jgit.lib.ObjectReader; -import org.eclipse.jgit.lib.Repository; /** Parses raw Git trees from the canonical semi-text/semi-binary format. */ public class CanonicalTreeParser extends AbstractTreeIterator { @@ -85,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 @@ -100,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 ObjectReader 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) { @@ -130,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. @@ -146,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 ObjectReader 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; } @@ -180,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 @@ -195,22 +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 ObjectReader curs) + public void reset(final ObjectReader reader, final AnyObjectId id) throws IncorrectObjectTypeException, IOException { - reset(curs.open(id, Constants.OBJ_TREE).getCachedBytes()); + reset(reader.open(id, Constants.OBJ_TREE).getCachedBytes()); } @Override - public CanonicalTreeParser createSubtreeIterator(final Repository repo, - final MutableObjectId idBuffer, final ObjectReader 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); } /** @@ -220,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 ObjectReader 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 ObjectReader curs = repo.newObjectReader(); - 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 8dfab8aa57..7f63646b5b 100644 --- a/org.eclipse.jgit/src/org/eclipse/jgit/treewalk/FileTreeIterator.java +++ b/org.eclipse.jgit/src/org/eclipse/jgit/treewalk/FileTreeIterator.java @@ -54,7 +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.Repository; +import org.eclipse.jgit.lib.ObjectReader; import org.eclipse.jgit.util.FS; /** @@ -103,7 +103,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 aefa79c3a8..2ebabcb94d 100644 --- a/org.eclipse.jgit/src/org/eclipse/jgit/treewalk/TreeWalk.java +++ b/org.eclipse.jgit/src/org/eclipse/jgit/treewalk/TreeWalk.java @@ -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 ObjectReader curs; - private TreeFilter filter; AbstractTreeIterator[] trees; @@ -180,19 +182,34 @@ public class TreeWalk { * the repository the walker will obtain data from. */ public TreeWalk(final Repository repo) { - db = repo; - curs = repo.newObjectReader(); + 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. + *

+ * 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(); } /** @@ -320,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); @@ -367,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; } @@ -837,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; @@ -912,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; } -- cgit v1.2.3 From 515deaf7e503738b4c53c3c2dfd6d7acab3bef18 Mon Sep 17 00:00:00 2001 From: "Shawn O. Pearce" Date: Tue, 29 Jun 2010 15:12:51 -0700 Subject: Ensure RevWalk is released when done Update a number of calling sites of RevWalk to ensure the walker's internal ObjectReader is released after the walk is no longer used. Because the ObjectReader is likely to hold onto a native resource like an Inflater, we don't want to leak them outside of their useful scope. Where possible we also try to share ObjectReaders across several walk pools, or between a walker and a PackWriter. This permits the ObjectReader to actually do some caching if it felt inclined to do so. Not everything was updated, we'll probably need to come back and update even more call sites, but these are some of the biggest offenders. Test cases in particular aren't updated. My plan is to move most storage-agnostic tests onto some purely in-memory storage solution that doesn't do compression. Change-Id: I04087ec79faeea208b19848939898ad7172b6672 Signed-off-by: Shawn O. Pearce --- .../eclipse/jgit/http/server/InfoRefsServlet.java | 49 ++++++------ .../jgit/http/server/ReceivePackServlet.java | 7 +- .../jgit/http/server/UploadPackServlet.java | 7 +- .../eclipse/jgit/pgm/debug/RebuildCommitGraph.java | 1 + .../src/org/eclipse/jgit/api/CommitCommand.java | 64 +++++++-------- .../src/org/eclipse/jgit/api/MergeCommand.java | 66 ++++++++-------- .../src/org/eclipse/jgit/lib/RefUpdate.java | 14 +++- .../src/org/eclipse/jgit/lib/Repository.java | 10 ++- .../eclipse/jgit/storage/file/RefDirectory.java | 29 ++++--- .../jgit/storage/file/RefDirectoryRename.java | 3 +- .../org/eclipse/jgit/storage/pack/PackWriter.java | 43 +++++++++-- .../jgit/transport/BasePackFetchConnection.java | 6 ++ .../jgit/transport/BundleFetchConnection.java | 90 ++++++++++++---------- .../org/eclipse/jgit/transport/FetchProcess.java | 37 +++++---- .../org/eclipse/jgit/transport/PushProcess.java | 52 +++++++------ .../org/eclipse/jgit/transport/ReceivePack.java | 1 + .../src/org/eclipse/jgit/transport/UploadPack.java | 3 +- 17 files changed, 298 insertions(+), 184 deletions(-) (limited to 'org.eclipse.jgit.pgm/src/org') 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 refs = db.getAllRefs(); - refs.remove(Constants.HEAD); - adv.send(refs); - out.close(); + Map 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/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/UploadPackServlet.java b/org.eclipse.jgit.http.server/src/org/eclipse/jgit/http/server/UploadPackServlet.java index 6d0d64fc63..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(); + } } } 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 8ba3e4b90a..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 @@ -297,6 +297,7 @@ class RebuildCommitGraph extends TextBuiltin { name, id)); } } finally { + rw.release(); br.close(); } return refs; 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 1cf2fe669c..c2db140b05 100644 --- a/org.eclipse.jgit/src/org/eclipse/jgit/api/CommitCommand.java +++ b/org.eclipse.jgit/src/org/eclipse/jgit/api/CommitCommand.java @@ -160,38 +160,42 @@ public class CommitCommand extends GitCommand { .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); - 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(); + 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; } - 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)); + 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(); } } finally { odi.release(); 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 76a3bc4794..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 { // 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( 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/Repository.java b/org.eclipse.jgit/src/org/eclipse/jgit/lib/Repository.java index aeb160e521..cb663aee32 100644 --- a/org.eclipse.jgit/src/org/eclipse/jgit/lib/Repository.java +++ b/org.eclipse.jgit/src/org/eclipse/jgit/lib/Repository.java @@ -484,9 +484,17 @@ public abstract class Repository { * 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(); RevObject ref = null; - RevWalk rw = new RevWalk(this); for (int i = 0; i < rev.length; ++i) { switch (rev[i]) { case '^': diff --git a/org.eclipse.jgit/src/org/eclipse/jgit/storage/file/RefDirectory.java b/org.eclipse.jgit/src/org/eclipse/jgit/storage/file/RefDirectory.java index f7ffa3e39a..68b0270df9 100644 --- a/org.eclipse.jgit/src/org/eclipse/jgit/storage/file/RefDirectory.java +++ b/org.eclipse.jgit/src/org/eclipse/jgit/storage/file/RefDirectory.java @@ -74,6 +74,7 @@ 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; @@ -418,16 +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) { - newLeaf = new ObjectIdRef.PeeledTag(leaf.getStorage(), leaf - .getName(), leaf.getObjectId(), rw.peel(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. @@ -444,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); diff --git a/org.eclipse.jgit/src/org/eclipse/jgit/storage/file/RefDirectoryRename.java b/org.eclipse.jgit/src/org/eclipse/jgit/storage/file/RefDirectoryRename.java index b43b70f1e4..4f3efe343e 100644 --- a/org.eclipse.jgit/src/org/eclipse/jgit/storage/file/RefDirectoryRename.java +++ b/org.eclipse.jgit/src/org/eclipse/jgit/storage/file/RefDirectoryRename.java @@ -92,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); @@ -177,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/storage/pack/PackWriter.java b/org.eclipse.jgit/src/org/eclipse/jgit/storage/pack/PackWriter.java index 3fab3f7470..c851238c9c 100644 --- a/org.eclipse.jgit/src/org/eclipse/jgit/storage/pack/PackWriter.java +++ b/org.eclipse.jgit/src/org/eclipse/jgit/storage/pack/PackWriter.java @@ -63,6 +63,7 @@ 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.Config; import org.eclipse.jgit.lib.Constants; import org.eclipse.jgit.lib.CoreConfig; import org.eclipse.jgit.lib.NullProgressMonitor; @@ -181,8 +182,6 @@ public class PackWriter { // edge objects for thin packs private final ObjectIdSubclassMap edgeObjects = new ObjectIdSubclassMap(); - private final Repository db; - private final Deflater deflater; private final ObjectReader reader; @@ -218,19 +217,51 @@ public class PackWriter { * repository where objects are stored. */ public PackWriter(final Repository repo) { - this.db = repo; + this(repo, repo.newObjectReader()); + } - reader = db.newObjectReader(); + /** + * Create a writer to load objects from the specified reader. + *

+ * 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. + *

+ * 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 CoreConfig coreConfig = db.getConfig().get(CoreConfig.KEY); - this.deflater = new Deflater(coreConfig.getCompression()); + final CoreConfig coreConfig = configOf(repo).get(CoreConfig.KEY); + deflater = new Deflater(coreConfig.getCompression()); outputVersion = coreConfig.getPackIndexVersion(); } + 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. 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 8c336c5255..af18f18d8e 100644 --- a/org.eclipse.jgit/src/org/eclipse/jgit/transport/BasePackFetchConnection.java +++ b/org.eclipse.jgit/src/org/eclipse/jgit/transport/BasePackFetchConnection.java @@ -270,6 +270,12 @@ abstract class BasePackFetchConnection extends BasePackConnection implements } } + @Override + public void close() { + walk.release(); + super.close(); + } + private int maxTimeWanted(final Collection wants) { int maxTime = 0; for (final Ref r : wants) { 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 98ecc5540f..126acab48d 100644 --- a/org.eclipse.jgit/src/org/eclipse/jgit/transport/BundleFetchConnection.java +++ b/org.eclipse.jgit/src/org/eclipse/jgit/transport/BundleFetchConnection.java @@ -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 missing = new HashMap(); - final List commits = new ArrayList(); - for (final Map.Entry 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 missing = new HashMap(); + final List commits = new ArrayList(); + for (final Map.Entry 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/FetchProcess.java b/org.eclipse.jgit/src/org/eclipse/jgit/transport/FetchProcess.java index 72d73eb59b..ca68858059 100644 --- a/org.eclipse.jgit/src/org/eclipse/jgit/transport/FetchProcess.java +++ b/org.eclipse.jgit/src/org/eclipse/jgit/transport/FetchProcess.java @@ -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()) { @@ -296,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/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 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 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 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 2475b9acfe..6b0a9b6cff 100644 --- a/org.eclipse.jgit/src/org/eclipse/jgit/transport/ReceivePack.java +++ b/org.eclipse.jgit/src/org/eclipse/jgit/transport/ReceivePack.java @@ -574,6 +574,7 @@ public class ReceivePack { service(); } finally { + walk.release(); try { if (pckOut != null) pckOut.flush(); 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 7e7d4c8927..02ce251bea 100644 --- a/org.eclipse.jgit/src/org/eclipse/jgit/transport/UploadPack.java +++ b/org.eclipse.jgit/src/org/eclipse/jgit/transport/UploadPack.java @@ -296,6 +296,7 @@ public class UploadPack { pckOut = new PacketLineOut(rawOut); service(); } finally { + walk.release(); if (timer != null) { try { timer.terminate(); @@ -567,7 +568,7 @@ public class UploadPack { SideBandOutputStream.CH_PROGRESS, bufsz, rawOut)); } - final PackWriter pw = new PackWriter(db); + final PackWriter pw = new PackWriter(db, walk.getObjectReader()); try { pw.setDeltaBaseAsOffset(options.contains(OPTION_OFS_DELTA)); pw.setThin(thin); -- cgit v1.2.3 From a1d5f5b6b526d086a0963c634a38edb6789a4594 Mon Sep 17 00:00:00 2001 From: "Shawn O. Pearce" Date: Wed, 30 Jun 2010 10:39:00 -0700 Subject: Move DirCache factory methods to Repository Instead of creating the DirCache from a static factory method, use an instance method on Repository, permitting the implementation to override the method with a completely different type of DirCache reading and writing. This would better support a repository in the cloud strategy, or even just an in-memory unit test environment. Change-Id: I6399894b12d6480c4b3ac84d10775dfd1b8d13e7 Signed-off-by: Shawn O. Pearce --- .../src/org/eclipse/jgit/pgm/Rm.java | 2 +- .../org/eclipse/jgit/pgm/debug/MakeCacheTree.java | 2 +- .../org/eclipse/jgit/pgm/debug/ReadDirCache.java | 3 +- .../org/eclipse/jgit/pgm/debug/ShowCacheTree.java | 2 +- .../org/eclipse/jgit/pgm/debug/ShowDirCache.java | 2 +- .../org/eclipse/jgit/pgm/debug/WriteDirCache.java | 2 +- .../eclipse/jgit/dircache/DirCacheBasicTest.java | 20 ++++----- .../jgit/dircache/DirCacheBuilderIteratorTest.java | 2 +- .../eclipse/jgit/dircache/DirCacheBuilderTest.java | 20 ++++----- .../eclipse/jgit/dircache/DirCacheFindTest.java | 2 +- .../jgit/dircache/DirCacheIteratorTest.java | 16 +++---- .../jgit/dircache/DirCacheLargePathTest.java | 4 +- .../eclipse/jgit/dircache/DirCacheTreeTest.java | 14 +++--- .../tst/org/eclipse/jgit/merge/CherryPickTest.java | 8 ++-- .../org/eclipse/jgit/merge/SimpleMergeTest.java | 42 +++++++++--------- .../jgit/treewalk/NameConflictTreeWalkTest.java | 16 +++---- .../jgit/treewalk/PostOrderTreeWalkTest.java | 6 +-- .../treewalk/filter/PathSuffixFilterTestCase.java | 4 +- .../src/org/eclipse/jgit/api/CommitCommand.java | 2 +- .../src/org/eclipse/jgit/dircache/DirCache.java | 51 ---------------------- .../src/org/eclipse/jgit/lib/Repository.java | 48 +++++++++++++++++++- 21 files changed, 131 insertions(+), 137 deletions(-) (limited to 'org.eclipse.jgit.pgm/src/org') 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 b0cc5248c9..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 @@ -69,7 +69,7 @@ class Rm extends TextBuiltin { protected void run() throws Exception { 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/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/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/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.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/merge/CherryPickTest.java b/org.eclipse.jgit.test/tst/org/eclipse/jgit/merge/CherryPickTest.java index 1b4a11db50..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 @@ -67,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(); 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 a4ef2cd643..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 @@ -105,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(); @@ -157,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(); @@ -204,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(); @@ -237,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(); @@ -269,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(); @@ -301,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(); @@ -331,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(); 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 ad51ac2ddf..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 @@ -64,7 +64,7 @@ public class PathSuffixFilterTestCase extends RepositoryTestCase { 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 = DirCache.read(db); + final DirCache dc = db.readDirCache(); final DirCacheBuilder builder = dc.builder(); final DirCacheEntry aSthEntry = new DirCacheEntry("a.sth"); aSthEntry.setFileMode(FileMode.REGULAR_FILE); @@ -100,7 +100,7 @@ public class PathSuffixFilterTestCase extends RepositoryTestCase { 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 = DirCache.read(db); + final DirCache dc = db.readDirCache(); final DirCacheBuilder builder = dc.builder(); final DirCacheEntry aSthEntry = new DirCacheEntry("a.sth"); aSthEntry.setFileMode(FileMode.REGULAR_FILE); 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 c2db140b05..17b7113470 100644 --- a/org.eclipse.jgit/src/org/eclipse/jgit/api/CommitCommand.java +++ b/org.eclipse.jgit/src/org/eclipse/jgit/api/CommitCommand.java @@ -139,7 +139,7 @@ public class CommitCommand extends GitCommand { parents.add(0, headId); // lock the index - DirCache index = DirCache.lock(repo); + DirCache index = repo.lockDirCache(); try { ObjectInserter odi = repo.newObjectInserter(); try { 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 e5b5771d7f..cc10fad2b4 100644 --- a/org.eclipse.jgit/src/org/eclipse/jgit/dircache/DirCache.java +++ b/org.eclipse.jgit/src/org/eclipse/jgit/dircache/DirCache.java @@ -62,12 +62,10 @@ import java.util.Comparator; import org.eclipse.jgit.JGitText; import org.eclipse.jgit.errors.CorruptObjectException; -import org.eclipse.jgit.errors.NoWorkTreeException; import org.eclipse.jgit.errors.UnmergedPathException; import org.eclipse.jgit.lib.Constants; import org.eclipse.jgit.lib.ObjectId; import org.eclipse.jgit.lib.ObjectInserter; -import org.eclipse.jgit.lib.Repository; import org.eclipse.jgit.storage.file.LockFile; import org.eclipse.jgit.util.IO; import org.eclipse.jgit.util.MutableInteger; @@ -158,30 +156,6 @@ public class DirCache { return c; } - /** - * Create a new in-core index representation and read an index from disk. - *

- * 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 NoWorkTreeException - * if the repository is bare (lacks a working directory). - * @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 NoWorkTreeException, CorruptObjectException, IOException { - return read(db.getIndexFile()); - } - /** * Create a new in-core index representation, lock it, and read from disk. *

@@ -223,31 +197,6 @@ public class DirCache { return c; } - /** - * Create a new in-core index representation, lock it, and read from disk. - *

- * 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 NoWorkTreeException - * if the repository is bare (lacks a working directory). - * @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 NoWorkTreeException, CorruptObjectException, IOException { - return lock(db.getIndexFile()); - } - /** Location of the current version of the index file. */ private final File liveFile; 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 70550a8325..a60cba56fa 100644 --- a/org.eclipse.jgit/src/org/eclipse/jgit/lib/Repository.java +++ b/org.eclipse.jgit/src/org/eclipse/jgit/lib/Repository.java @@ -60,6 +60,7 @@ import java.util.concurrent.atomic.AtomicInteger; import org.eclipse.jgit.JGitText; import org.eclipse.jgit.dircache.DirCache; +import org.eclipse.jgit.errors.CorruptObjectException; import org.eclipse.jgit.errors.IncorrectObjectTypeException; import org.eclipse.jgit.errors.MissingObjectException; import org.eclipse.jgit.errors.NoWorkTreeException; @@ -887,6 +888,51 @@ public abstract class Repository { return indexFile; } + /** + * Create a new in-core index representation and read an index from disk. + *

+ * 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 the repository is bare (lacks a working directory). + * @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. + *

+ * 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 the repository is bare (lacks a working directory). + * @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; @@ -926,7 +972,7 @@ public abstract class Repository { 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; } -- cgit v1.2.3 From 08d349a27bbff4b0d3ee67eb6ddd4818e070c86e Mon Sep 17 00:00:00 2001 From: "Shawn O. Pearce" Date: Fri, 2 Jul 2010 17:01:40 -0700 Subject: amend commit: Refactor repository construction to builder class During code review, Alex raised a few comments about commit 532421d98925 ("Refactor repository construction to builder class"). Due to the size of the related series we aren't going to go back and rebase in something this minor, so resolve them as a follow-up commit instead. Change-Id: Ied52f7a8f7252743353c58d20bfc3ec498933e00 Signed-off-by: Shawn O. Pearce --- org.eclipse.jgit.pgm/src/org/eclipse/jgit/pgm/Main.java | 6 +++--- .../tst/org/eclipse/jgit/storage/file/T0003_Basic.java | 3 ++- org.eclipse.jgit/resources/org/eclipse/jgit/JGitText.properties | 2 +- org.eclipse.jgit/src/org/eclipse/jgit/JGitText.java | 2 +- .../src/org/eclipse/jgit/lib/BaseRepositoryBuilder.java | 2 +- 5 files changed, 8 insertions(+), 7 deletions(-) (limited to 'org.eclipse.jgit.pgm/src/org') 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 2da210d7e8..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 @@ -163,17 +163,17 @@ public class Main { final TextBuiltin cmd = subcommand; if (cmd.requiresRepository()) { - RepositoryBuilder frb = new RepositoryBuilder() // + RepositoryBuilder rb = new RepositoryBuilder() // .setGitDir(gitdir) // .readEnvironment() // .findGitDir(); - if (frb.getGitDir() == null) { + if (rb.getGitDir() == null) { writer.println(CLIText.get().cantFindGitDirectory); writer.flush(); System.exit(1); } - cmd.init(frb.build(), null); + cmd.init(rb.build(), null); } else { cmd.init(null, gitdir); } diff --git a/org.eclipse.jgit.test/tst/org/eclipse/jgit/storage/file/T0003_Basic.java b/org.eclipse.jgit.test/tst/org/eclipse/jgit/storage/file/T0003_Basic.java index c9013a6a01..477b0dfb50 100644 --- a/org.eclipse.jgit.test/tst/org/eclipse/jgit/storage/file/T0003_Basic.java +++ b/org.eclipse.jgit.test/tst/org/eclipse/jgit/storage/file/T0003_Basic.java @@ -53,6 +53,7 @@ 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; @@ -99,7 +100,7 @@ public class T0003_Basic extends SampleDataRepositoryTestCase { 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()); } } diff --git a/org.eclipse.jgit/resources/org/eclipse/jgit/JGitText.properties b/org.eclipse.jgit/resources/org/eclipse/jgit/JGitText.properties index aece518741..999a11a695 100644 --- a/org.eclipse.jgit/resources/org/eclipse/jgit/JGitText.properties +++ b/org.eclipse.jgit/resources/org/eclipse/jgit/JGitText.properties @@ -125,7 +125,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 diff --git a/org.eclipse.jgit/src/org/eclipse/jgit/JGitText.java b/org.eclipse.jgit/src/org/eclipse/jgit/JGitText.java index 301e411eec..707b391027 100644 --- a/org.eclipse.jgit/src/org/eclipse/jgit/JGitText.java +++ b/org.eclipse.jgit/src/org/eclipse/jgit/JGitText.java @@ -185,7 +185,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; diff --git a/org.eclipse.jgit/src/org/eclipse/jgit/lib/BaseRepositoryBuilder.java b/org.eclipse.jgit/src/org/eclipse/jgit/lib/BaseRepositoryBuilder.java index 90929e721c..92edb0325c 100644 --- a/org.eclipse.jgit/src/org/eclipse/jgit/lib/BaseRepositoryBuilder.java +++ b/org.eclipse.jgit/src/org/eclipse/jgit/lib/BaseRepositoryBuilder.java @@ -467,7 +467,7 @@ public class BaseRepositoryBuilder Date: Fri, 9 Jul 2010 11:59:55 -0700 Subject: debug-show-packdelta: Dump a pack delta to the console This is a horribly crude application, it doesn't even verify that the object its dumping is delta encoded. Its method of getting the delta is pretty abusive to the public PackWriter API, because right now we don't want to expose the real internal low-level methods actually required to do this. Change-Id: I437a17ceb98708b5603a2061126eb251e82f4ed4 Signed-off-by: Shawn O. Pearce --- org.eclipse.jgit.pgm/META-INF/MANIFEST.MF | 1 + .../services/org.eclipse.jgit.pgm.TextBuiltin | 1 + .../org/eclipse/jgit/pgm/debug/ShowPackDelta.java | 127 +++++++++++++++++++++ .../jgit/storage/pack/PackOutputStream.java | 20 +++- .../org/eclipse/jgit/storage/pack/PackWriter.java | 2 +- 5 files changed, 147 insertions(+), 4 deletions(-) create mode 100644 org.eclipse.jgit.pgm/src/org/eclipse/jgit/pgm/debug/ShowPackDelta.java (limited to 'org.eclipse.jgit.pgm/src/org') diff --git a/org.eclipse.jgit.pgm/META-INF/MANIFEST.MF b/org.eclipse.jgit.pgm/META-INF/MANIFEST.MF index 0a4376921a..d6eac232e1 100644 --- a/org.eclipse.jgit.pgm/META-INF/MANIFEST.MF +++ b/org.eclipse.jgit.pgm/META-INF/MANIFEST.MF @@ -18,6 +18,7 @@ Import-Package: org.eclipse.jgit.api;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/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/src/org/eclipse/jgit/storage/pack/PackOutputStream.java b/org.eclipse.jgit/src/org/eclipse/jgit/storage/pack/PackOutputStream.java index a93ac0516a..92e1a197cd 100644 --- a/org.eclipse.jgit/src/org/eclipse/jgit/storage/pack/PackOutputStream.java +++ b/org.eclipse.jgit/src/org/eclipse/jgit/storage/pack/PackOutputStream.java @@ -71,11 +71,25 @@ public final class PackOutputStream extends OutputStream { private byte[] copyBuffer; - PackOutputStream(final ProgressMonitor writeMonitor, - final OutputStream out, final boolean ofsDelta) { + /** + * Initialize a pack output stream. + *

+ * 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 = ofsDelta; + this.ofsDelta = pw.isDeltaBaseAsOffset(); } @Override 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 index cae552831c..d96d5ddfd2 100644 --- a/org.eclipse.jgit/src/org/eclipse/jgit/storage/pack/PackWriter.java +++ b/org.eclipse.jgit/src/org/eclipse/jgit/storage/pack/PackWriter.java @@ -718,7 +718,7 @@ public class PackWriter { searchForReuse(); final PackOutputStream out = new PackOutputStream(writeMonitor, - packStream, isDeltaBaseAsOffset()); + packStream, this); writeMonitor.beginTask(WRITING_OBJECTS_PROGRESS, getObjectsNumber()); out.writeFileHeader(PACK_VERSION_GENERATED, getObjectsNumber()); -- cgit v1.2.3