Merge branch 'rename-bug'
* rename-bug:
Fix ArrayIndexOutOfBounds on non-square exact rename matrix
Conflicts:
org.eclipse.jgit/src/org/eclipse/jgit/diff/RenameDetector.java
Change-Id: Ie0b8dd3e1ec174f79ba39dc4706bb0694cc8be29
diff --git a/org.eclipse.jgit.http.server/META-INF/MANIFEST.MF b/org.eclipse.jgit.http.server/META-INF/MANIFEST.MF
index b9aeef5..edaa1ed 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 @@
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 dff1e82..d217fe1 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.HttpServletResponse;
import org.eclipse.jgit.lib.ObjectDatabase;
-import org.eclipse.jgit.lib.ObjectDirectory;
-import org.eclipse.jgit.lib.PackFile;
+import org.eclipse.jgit.storage.file.ObjectDirectory;
+import org.eclipse.jgit.storage.file.PackFile;
/** Sends the current list of pack files, sorted most recent first. */
class InfoPacksServlet extends HttpServlet {
diff --git a/org.eclipse.jgit.http.server/src/org/eclipse/jgit/http/server/InfoRefsServlet.java b/org.eclipse.jgit.http.server/src/org/eclipse/jgit/http/server/InfoRefsServlet.java
index f667ce9..647919e 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 @@ public void doGet(final HttpServletRequest req,
final Repository db = getRepository(req);
final RevWalk walk = new RevWalk(db);
- final RevFlag ADVERTISED = walk.newFlag("ADVERTISED");
+ try {
+ final RevFlag ADVERTISED = walk.newFlag("ADVERTISED");
- final OutputStreamWriter out = new OutputStreamWriter(
- new SmartOutputStream(req, rsp), Constants.CHARSET);
- final RefAdvertiser adv = new RefAdvertiser() {
- @Override
- protected void writeOne(final CharSequence line) throws IOException {
- // Whoever decided that info/refs should use a different
- // delimiter than the native git:// protocol shouldn't
- // be allowed to design this sort of stuff. :-(
- out.append(line.toString().replace(' ', '\t'));
- }
+ final OutputStreamWriter out = new OutputStreamWriter(
+ new SmartOutputStream(req, rsp), Constants.CHARSET);
+ final RefAdvertiser adv = new RefAdvertiser() {
+ @Override
+ protected void writeOne(final CharSequence line)
+ throws IOException {
+ // Whoever decided that info/refs should use a different
+ // delimiter than the native git:// protocol shouldn't
+ // be allowed to design this sort of stuff. :-(
+ out.append(line.toString().replace(' ', '\t'));
+ }
- @Override
- protected void end() {
- // No end marker required for info/refs format.
- }
- };
- adv.init(walk, ADVERTISED);
- adv.setDerefTags(true);
+ @Override
+ protected void end() {
+ // No end marker required for info/refs format.
+ }
+ };
+ adv.init(walk, ADVERTISED);
+ adv.setDerefTags(true);
- Map<String, Ref> refs = db.getAllRefs();
- refs.remove(Constants.HEAD);
- adv.send(refs);
- out.close();
+ Map<String, Ref> refs = db.getAllRefs();
+ refs.remove(Constants.HEAD);
+ adv.send(refs);
+ out.close();
+ } finally {
+ walk.release();
+ }
}
}
diff --git a/org.eclipse.jgit.http.server/src/org/eclipse/jgit/http/server/IsLocalFilter.java b/org.eclipse.jgit.http.server/src/org/eclipse/jgit/http/server/IsLocalFilter.java
index 34edf82..019ec90 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.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 5d774a8..8486512 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.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
-import org.eclipse.jgit.lib.ObjectDirectory;
import org.eclipse.jgit.lib.Repository;
+import org.eclipse.jgit.storage.file.ObjectDirectory;
/** Sends any object from {@code GIT_DIR/objects/??/0 38}, or any pack file. */
abstract class ObjectFileServlet extends HttpServlet {
diff --git a/org.eclipse.jgit.http.server/src/org/eclipse/jgit/http/server/ReceivePackServlet.java b/org.eclipse.jgit.http.server/src/org/eclipse/jgit/http/server/ReceivePackServlet.java
index 49fd535..4bc05c1 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 @@ static class InfoRefs extends SmartServiceInfoRefs {
protected void advertise(HttpServletRequest req, Repository db,
PacketLineOutRefAdvertiser pck) throws IOException,
ServiceNotEnabledException, ServiceNotAuthorizedException {
- receivePackFactory.create(req, db).sendAdvertisedRefs(pck);
+ ReceivePack rp = receivePackFactory.create(req, db);
+ try {
+ rp.sendAdvertisedRefs(pck);
+ } finally {
+ rp.getRevWalk().release();
+ }
}
}
diff --git a/org.eclipse.jgit.http.server/src/org/eclipse/jgit/http/server/TextFileServlet.java b/org.eclipse.jgit.http.server/src/org/eclipse/jgit/http/server/TextFileServlet.java
index 5bf5546..650059b 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 @@ public void doGet(final HttpServletRequest req,
private byte[] read(final HttpServletRequest req) throws IOException {
final File gitdir = getRepository(req).getDirectory();
+ if (gitdir == null)
+ throw new FileNotFoundException(fileName);
return IO.readFully(new File(gitdir, fileName));
}
}
diff --git a/org.eclipse.jgit.http.server/src/org/eclipse/jgit/http/server/UploadPackServlet.java b/org.eclipse.jgit.http.server/src/org/eclipse/jgit/http/server/UploadPackServlet.java
index 92d41a0..602d66a 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 @@ static class InfoRefs extends SmartServiceInfoRefs {
protected void advertise(HttpServletRequest req, Repository db,
PacketLineOutRefAdvertiser pck) throws IOException,
ServiceNotEnabledException, ServiceNotAuthorizedException {
- uploadPackFactory.create(req, db).sendAdvertisedRefs(pck);
+ UploadPack up = uploadPackFactory.create(req, db);
+ try {
+ up.sendAdvertisedRefs(pck);
+ } finally {
+ up.getRevWalk().release();
+ }
}
}
@@ -107,7 +112,12 @@ public void doPost(final HttpServletRequest req,
up.setBiDirectionalPipe(false);
rsp.setContentType(RSP_TYPE);
- final SmartOutputStream out = new SmartOutputStream(req, rsp);
+ final SmartOutputStream out = new SmartOutputStream(req, rsp) {
+ @Override
+ public void flush() throws IOException {
+ doFlush();
+ }
+ };
up.upload(getInputStream(req), out, null);
out.close();
diff --git a/org.eclipse.jgit.http.server/src/org/eclipse/jgit/http/server/resolver/FileResolver.java b/org.eclipse.jgit.http.server/src/org/eclipse/jgit/http/server/resolver/FileResolver.java
index cc062db..296725b 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 @@ protected boolean isExportOk(HttpServletRequest req, String repositoryName,
Repository db) throws IOException {
if (isExportAll())
return true;
- else
+ else if (db.getDirectory() != null)
return new File(db.getDirectory(), "git-daemon-export-ok").exists();
+ else
+ return false;
}
private static boolean isUnreasonableName(final String name) {
diff --git a/org.eclipse.jgit.http.test/META-INF/MANIFEST.MF b/org.eclipse.jgit.http.test/META-INF/MANIFEST.MF
index 370bd40..21dd608 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 @@
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 47d7806..db4aa80 100644
--- a/org.eclipse.jgit.http.test/tst/org/eclipse/jgit/http/test/AdvertiseErrorTest.java
+++ b/org.eclipse.jgit.http.test/tst/org/eclipse/jgit/http/test/AdvertiseErrorTest.java
@@ -62,23 +62,24 @@
import org.eclipse.jgit.lib.NullProgressMonitor;
import org.eclipse.jgit.lib.ObjectId;
import org.eclipse.jgit.lib.Repository;
-import org.eclipse.jgit.lib.RepositoryConfig;
import org.eclipse.jgit.revwalk.RevBlob;
import org.eclipse.jgit.revwalk.RevCommit;
+import org.eclipse.jgit.storage.file.FileBasedConfig;
+import org.eclipse.jgit.storage.file.FileRepository;
import org.eclipse.jgit.transport.ReceivePack;
import org.eclipse.jgit.transport.RemoteRefUpdate;
import org.eclipse.jgit.transport.Transport;
import org.eclipse.jgit.transport.URIish;
public class AdvertiseErrorTest extends HttpTestCase {
- private Repository remoteRepository;
+ private FileRepository remoteRepository;
private URIish remoteURI;
protected void setUp() throws Exception {
super.setUp();
- final TestRepository src = createTestRepository();
+ final TestRepository<FileRepository> src = createTestRepository();
final String srcName = src.getRepository().getDirectory().getName();
ServletContextHandler app = server.addContext("/git");
@@ -114,7 +115,7 @@ public ReceivePack create(HttpServletRequest req, Repository db)
remoteRepository = src.getRepository();
remoteURI = toURIish(app, srcName);
- RepositoryConfig cfg = remoteRepository.getConfig();
+ FileBasedConfig cfg = remoteRepository.getConfig();
cfg.setBoolean("http", null, "receivepack", true);
cfg.save();
}
diff --git a/org.eclipse.jgit.http.test/tst/org/eclipse/jgit/http/test/HookMessageTest.java b/org.eclipse.jgit.http.test/tst/org/eclipse/jgit/http/test/HookMessageTest.java
index 224ea05..18f8dc9 100644
--- a/org.eclipse.jgit.http.test/tst/org/eclipse/jgit/http/test/HookMessageTest.java
+++ b/org.eclipse.jgit.http.test/tst/org/eclipse/jgit/http/test/HookMessageTest.java
@@ -64,9 +64,10 @@
import org.eclipse.jgit.lib.NullProgressMonitor;
import org.eclipse.jgit.lib.ObjectId;
import org.eclipse.jgit.lib.Repository;
-import org.eclipse.jgit.lib.RepositoryConfig;
import org.eclipse.jgit.revwalk.RevBlob;
import org.eclipse.jgit.revwalk.RevCommit;
+import org.eclipse.jgit.storage.file.FileBasedConfig;
+import org.eclipse.jgit.storage.file.FileRepository;
import org.eclipse.jgit.transport.PreReceiveHook;
import org.eclipse.jgit.transport.PushResult;
import org.eclipse.jgit.transport.ReceiveCommand;
@@ -76,14 +77,14 @@
import org.eclipse.jgit.transport.URIish;
public class HookMessageTest extends HttpTestCase {
- private Repository remoteRepository;
+ private FileRepository remoteRepository;
private URIish remoteURI;
protected void setUp() throws Exception {
super.setUp();
- final TestRepository src = createTestRepository();
+ final TestRepository<FileRepository> src = createTestRepository();
final String srcName = src.getRepository().getDirectory().getName();
ServletContextHandler app = server.addContext("/git");
@@ -124,7 +125,7 @@ public void onPreReceive(ReceivePack rp,
remoteRepository = src.getRepository();
remoteURI = toURIish(app, srcName);
- RepositoryConfig cfg = remoteRepository.getConfig();
+ FileBasedConfig cfg = remoteRepository.getConfig();
cfg.setBoolean("http", null, "receivepack", true);
cfg.save();
}
diff --git a/org.eclipse.jgit.http.test/tst/org/eclipse/jgit/http/test/HttpClientTests.java b/org.eclipse.jgit.http.test/tst/org/eclipse/jgit/http/test/HttpClientTests.java
index 729466d..4cc141b 100644
--- a/org.eclipse.jgit.http.test/tst/org/eclipse/jgit/http/test/HttpClientTests.java
+++ b/org.eclipse.jgit.http.test/tst/org/eclipse/jgit/http/test/HttpClientTests.java
@@ -66,12 +66,13 @@
import org.eclipse.jgit.lib.RefUpdate;
import org.eclipse.jgit.lib.Repository;
import org.eclipse.jgit.revwalk.RevCommit;
+import org.eclipse.jgit.storage.file.FileRepository;
import org.eclipse.jgit.transport.FetchConnection;
import org.eclipse.jgit.transport.Transport;
import org.eclipse.jgit.transport.URIish;
public class HttpClientTests extends HttpTestCase {
- private TestRepository remoteRepository;
+ private TestRepository<FileRepository> remoteRepository;
private URIish dumbAuthNoneURI;
@@ -95,7 +96,7 @@ protected void setUp() throws Exception {
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 @@ private ServletContextHandler smart(final String path) {
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 Repository open(HttpServletRequest req, String name)
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 void testListRemote_Dumb_DetachedHEAD() throws Exception {
}
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 void testListRemote_Smart_UploadPackNeedsAuth() throws Exception {
}
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 f7b3bdb..a7b51c6 100644
--- a/org.eclipse.jgit.http.test/tst/org/eclipse/jgit/http/test/SmartClientSmartServerTest.java
+++ b/org.eclipse.jgit.http.test/tst/org/eclipse/jgit/http/test/SmartClientSmartServerTest.java
@@ -79,11 +79,12 @@
import org.eclipse.jgit.lib.NullProgressMonitor;
import org.eclipse.jgit.lib.ObjectId;
import org.eclipse.jgit.lib.Ref;
-import org.eclipse.jgit.lib.ReflogReader;
import org.eclipse.jgit.lib.Repository;
-import org.eclipse.jgit.lib.RepositoryConfig;
import org.eclipse.jgit.revwalk.RevBlob;
import org.eclipse.jgit.revwalk.RevCommit;
+import org.eclipse.jgit.storage.file.FileBasedConfig;
+import org.eclipse.jgit.storage.file.FileRepository;
+import org.eclipse.jgit.storage.file.ReflogReader;
import org.eclipse.jgit.transport.FetchConnection;
import org.eclipse.jgit.transport.HttpTransport;
import org.eclipse.jgit.transport.RemoteRefUpdate;
@@ -94,7 +95,7 @@
public class SmartClientSmartServerTest extends HttpTestCase {
private static final String HDR_TRANSFER_ENCODING = "Transfer-Encoding";
- private Repository remoteRepository;
+ private FileRepository remoteRepository;
private URIish remoteURI;
@@ -107,7 +108,7 @@ public class SmartClientSmartServerTest extends HttpTestCase {
protected void setUp() throws Exception {
super.setUp();
- final TestRepository src = createTestRepository();
+ final TestRepository<FileRepository> src = createTestRepository();
final String srcName = src.getRepository().getDirectory().getName();
ServletContextHandler app = server.addContext("/git");
@@ -489,10 +490,10 @@ public void testPush_CreateBranch() throws Exception {
}
public void testPush_ChunkedEncoding() throws Exception {
- final TestRepository src = createTestRepository();
+ final TestRepository<FileRepository> src = createTestRepository();
final RevBlob Q_bin = src.blob(new TestRng("Q").nextBytes(128 * 1024));
final RevCommit Q = src.commit().add("Q", Q_bin).create();
- final Repository db = src.getRepository();
+ final FileRepository db = src.getRepository();
final String dstName = Constants.R_HEADS + "new.branch";
Transport t;
@@ -547,7 +548,7 @@ public void testPush_ChunkedEncoding() throws Exception {
}
private void enableReceivePack() throws IOException {
- final RepositoryConfig cfg = remoteRepository.getConfig();
+ final FileBasedConfig cfg = remoteRepository.getConfig();
cfg.setBoolean("http", null, "receivepack", true);
cfg.save();
}
diff --git a/org.eclipse.jgit.http.test/tst/org/eclipse/jgit/http/test/util/HttpTestCase.java b/org.eclipse.jgit.http.test/tst/org/eclipse/jgit/http/test/util/HttpTestCase.java
index e259757..313b6ad 100644
--- a/org.eclipse.jgit.http.test/tst/org/eclipse/jgit/http/test/util/HttpTestCase.java
+++ b/org.eclipse.jgit.http.test/tst/org/eclipse/jgit/http/test/util/HttpTestCase.java
@@ -61,6 +61,7 @@
import org.eclipse.jgit.lib.Repository;
import org.eclipse.jgit.revwalk.RevCommit;
import org.eclipse.jgit.revwalk.RevObject;
+import org.eclipse.jgit.storage.file.FileRepository;
import org.eclipse.jgit.transport.RefSpec;
import org.eclipse.jgit.transport.RemoteRefUpdate;
import org.eclipse.jgit.transport.URIish;
@@ -82,8 +83,9 @@ protected void tearDown() throws Exception {
super.tearDown();
}
- protected TestRepository createTestRepository() throws Exception {
- return new TestRepository(createBareRepository());
+ protected TestRepository<FileRepository> createTestRepository()
+ throws IOException {
+ return new TestRepository<FileRepository>(createBareRepository());
}
protected URIish toURIish(String path) throws URISyntaxException {
diff --git a/org.eclipse.jgit.iplog/META-INF/MANIFEST.MF b/org.eclipse.jgit.iplog/META-INF/MANIFEST.MF
index 4ff6144..31a19b5 100644
--- a/org.eclipse.jgit.iplog/META-INF/MANIFEST.MF
+++ b/org.eclipse.jgit.iplog/META-INF/MANIFEST.MF
@@ -15,6 +15,7 @@
org.eclipse.jgit.revplot;version="[0.9.0,0.10.0)",
org.eclipse.jgit.revwalk;version="[0.9.0,0.10.0)",
org.eclipse.jgit.revwalk.filter;version="[0.9.0,0.10.0)",
+ org.eclipse.jgit.storage.file;version="[0.9.0,0.10.0)",
org.eclipse.jgit.transport;version="[0.9.0,0.10.0)",
org.eclipse.jgit.treewalk;version="[0.9.0,0.10.0)",
org.eclipse.jgit.treewalk.filter;version="[0.9.0,0.10.0)",
diff --git a/org.eclipse.jgit.iplog/src/org/eclipse/jgit/iplog/IpLogGenerator.java b/org.eclipse.jgit.iplog/src/org/eclipse/jgit/iplog/IpLogGenerator.java
index f64c329..433d433 100644
--- a/org.eclipse.jgit.iplog/src/org/eclipse/jgit/iplog/IpLogGenerator.java
+++ b/org.eclipse.jgit.iplog/src/org/eclipse/jgit/iplog/IpLogGenerator.java
@@ -78,15 +78,13 @@
import org.eclipse.jgit.diff.MyersDiff;
import org.eclipse.jgit.diff.RawText;
import org.eclipse.jgit.errors.ConfigInvalidException;
-import org.eclipse.jgit.errors.MissingObjectException;
import org.eclipse.jgit.iplog.Committer.ActiveRange;
import org.eclipse.jgit.lib.BlobBasedConfig;
import org.eclipse.jgit.lib.Constants;
import org.eclipse.jgit.lib.MutableObjectId;
-import org.eclipse.jgit.lib.ObjectLoader;
+import org.eclipse.jgit.lib.ObjectReader;
import org.eclipse.jgit.lib.PersonIdent;
import org.eclipse.jgit.lib.Repository;
-import org.eclipse.jgit.lib.WindowCursor;
import org.eclipse.jgit.revwalk.RevCommit;
import org.eclipse.jgit.revwalk.RevTree;
import org.eclipse.jgit.revwalk.RevWalk;
@@ -144,7 +142,7 @@ public class IpLogGenerator {
private NameConflictTreeWalk tw;
- private final WindowCursor curs = new WindowCursor();
+ private ObjectReader curs;
private final MutableObjectId idbuf = new MutableObjectId();
@@ -184,8 +182,9 @@ public void scan(Repository repo, RevCommit startCommit, String version)
throws IOException, ConfigInvalidException {
try {
db = repo;
- rw = new RevWalk(db);
- tw = new NameConflictTreeWalk(db);
+ curs = db.newObjectReader();
+ rw = new RevWalk(curs);
+ tw = new NameConflictTreeWalk(curs);
RevCommit c = rw.parseCommit(startCommit);
@@ -194,7 +193,7 @@ public void scan(Repository repo, RevCommit startCommit, String version)
scanProjectCommits(meta.getProjects().get(0), c);
commits.add(c);
} finally {
- WindowCursor.release(curs);
+ curs.release();
db = null;
rw = null;
tw = null;
@@ -417,10 +416,7 @@ private void scanProjectCommits(Project proj, RevCommit start)
private byte[] openBlob(int side) throws IOException {
tw.getObjectId(idbuf, side);
- ObjectLoader ldr = db.openObject(curs, idbuf);
- if (ldr == null)
- throw new MissingObjectException(idbuf.copy(), Constants.OBJ_BLOB);
- return ldr.getCachedBytes();
+ return curs.open(idbuf, Constants.OBJ_BLOB).getCachedBytes();
}
/**
diff --git a/org.eclipse.jgit.iplog/src/org/eclipse/jgit/iplog/IpLogMeta.java b/org.eclipse.jgit.iplog/src/org/eclipse/jgit/iplog/IpLogMeta.java
index 89695bd..372469d 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,10 @@
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;
+import org.eclipse.jgit.util.FS;
/**
* Manages the {@code .eclipse_iplog} file in a project.
@@ -167,6 +168,9 @@ private List<Project> parseProjects(final Config cfg,
*
* @param file
* local file to update with current CQ records.
+ * @param fs
+ * the file system abstraction which will be necessary to perform
+ * certain file system operations.
* @param base
* base https:// URL of the IPzilla server.
* @param username
@@ -181,16 +185,16 @@ private List<Project> parseProjects(final Config cfg,
* the local file cannot be read, as it is not a valid
* configuration file format.
*/
- public void syncCQs(File file, URL base, String username, String password)
- throws IOException, ConfigInvalidException {
+ public void syncCQs(File file, FS fs, URL base, String username,
+ String password) throws IOException, ConfigInvalidException {
if (!file.getParentFile().exists())
file.getParentFile().mkdirs();
- LockFile lf = new LockFile(file);
+ LockFile lf = new LockFile(file, fs);
if (!lf.lock())
throw new IOException(MessageFormat.format(IpLogText.get().cannotLock, file));
try {
- FileBasedConfig cfg = new FileBasedConfig(file);
+ FileBasedConfig cfg = new FileBasedConfig(file, fs);
cfg.load();
loadFrom(cfg);
diff --git a/org.eclipse.jgit.junit/META-INF/MANIFEST.MF b/org.eclipse.jgit.junit/META-INF/MANIFEST.MF
index 051079b..7f4dcfd 100644
--- a/org.eclipse.jgit.junit/META-INF/MANIFEST.MF
+++ b/org.eclipse.jgit.junit/META-INF/MANIFEST.MF
@@ -18,6 +18,8 @@
org.eclipse.jgit.revplot;version="[0.9.0,0.10.0)",
org.eclipse.jgit.revwalk;version="[0.9.0,0.10.0)",
org.eclipse.jgit.revwalk.filter;version="[0.9.0,0.10.0)",
+ org.eclipse.jgit.storage.file;version="[0.9.0,0.10.0)",
+ org.eclipse.jgit.storage.pack;version="[0.9.0,0.10.0)",
org.eclipse.jgit.transport;version="[0.9.0,0.10.0)",
org.eclipse.jgit.treewalk;version="[0.9.0,0.10.0)",
org.eclipse.jgit.treewalk.filter;version="[0.9.0,0.10.0)",
diff --git a/org.eclipse.jgit.junit/src/org/eclipse/jgit/junit/LocalDiskRepositoryTestCase.java b/org.eclipse.jgit.junit/src/org/eclipse/jgit/junit/LocalDiskRepositoryTestCase.java
index 001deb2..47956e5 100644
--- a/org.eclipse.jgit.junit/src/org/eclipse/jgit/junit/LocalDiskRepositoryTestCase.java
+++ b/org.eclipse.jgit.junit/src/org/eclipse/jgit/junit/LocalDiskRepositoryTestCase.java
@@ -62,12 +62,14 @@
import org.eclipse.jgit.lib.AnyObjectId;
import org.eclipse.jgit.lib.Constants;
-import org.eclipse.jgit.lib.FileBasedConfig;
import org.eclipse.jgit.lib.PersonIdent;
import org.eclipse.jgit.lib.Repository;
import org.eclipse.jgit.lib.RepositoryCache;
-import org.eclipse.jgit.lib.WindowCache;
-import org.eclipse.jgit.lib.WindowCacheConfig;
+import org.eclipse.jgit.storage.file.FileBasedConfig;
+import org.eclipse.jgit.storage.file.FileRepository;
+import org.eclipse.jgit.storage.file.WindowCache;
+import org.eclipse.jgit.storage.file.WindowCacheConfig;
+import org.eclipse.jgit.util.FS;
import org.eclipse.jgit.util.IO;
import org.eclipse.jgit.util.SystemReader;
@@ -127,7 +129,7 @@ public void run() {
mockSystemReader = new MockSystemReader();
mockSystemReader.userGitConfig = new FileBasedConfig(new File(trash,
- "usergitconfig"));
+ "usergitconfig"), FS.DETECTED);
ceilTestDirectories(getCeilings());
SystemReader.setInstance(mockSystemReader);
@@ -259,7 +261,7 @@ private static void reportDeleteFailure(final String testName,
* @throws IOException
* the repository could not be created in the temporary area
*/
- protected Repository createBareRepository() throws IOException {
+ protected FileRepository createBareRepository() throws IOException {
return createRepository(true /* bare */);
}
@@ -270,7 +272,7 @@ protected Repository createBareRepository() throws IOException {
* @throws IOException
* the repository could not be created in the temporary area
*/
- protected Repository createWorkRepository() throws IOException {
+ protected FileRepository createWorkRepository() throws IOException {
return createRepository(false /* not bare */);
}
@@ -284,11 +286,11 @@ protected Repository createWorkRepository() throws IOException {
* @throws IOException
* the repository could not be created in the temporary area
*/
- private Repository createRepository(boolean bare) throws IOException {
+ private FileRepository createRepository(boolean bare) throws IOException {
String uniqueId = System.currentTimeMillis() + "_" + (testCount++);
String gitdirName = "test" + uniqueId + (bare ? "" : "/") + Constants.DOT_GIT;
File gitdir = new File(trash, gitdirName).getCanonicalFile();
- Repository db = new Repository(gitdir);
+ FileRepository db = new FileRepository(gitdir);
assertFalse(gitdir.exists());
db.create();
@@ -323,7 +325,7 @@ protected int runHook(final Repository db, final File hook,
putPersonIdent(env, "AUTHOR", author);
putPersonIdent(env, "COMMITTER", committer);
- final File cwd = db.getWorkDir();
+ final File cwd = db.getWorkTree();
final Process p = Runtime.getRuntime().exec(argv, toEnvArray(env), cwd);
p.getOutputStream().close();
p.getErrorStream().close();
diff --git a/org.eclipse.jgit.junit/src/org/eclipse/jgit/junit/MockSystemReader.java b/org.eclipse.jgit.junit/src/org/eclipse/jgit/junit/MockSystemReader.java
index c502fb6..5c2e77f 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 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;
@@ -67,7 +67,7 @@ public MockSystemReader() {
init(Constants.GIT_AUTHOR_EMAIL_KEY);
init(Constants.GIT_COMMITTER_NAME_KEY);
init(Constants.GIT_COMMITTER_EMAIL_KEY);
- userGitConfig = new FileBasedConfig(null) {
+ userGitConfig = new FileBasedConfig(null, null) {
@Override
public void load() throws IOException, ConfigInvalidException {
// Do nothing
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 59504aa..afe1c0b 100644
--- a/org.eclipse.jgit.junit/src/org/eclipse/jgit/junit/TestRepository.java
+++ b/org.eclipse.jgit.junit/src/org/eclipse/jgit/junit/TestRepository.java
@@ -73,22 +73,16 @@
import org.eclipse.jgit.lib.Commit;
import org.eclipse.jgit.lib.Constants;
import org.eclipse.jgit.lib.FileMode;
-import org.eclipse.jgit.lib.LockFile;
import org.eclipse.jgit.lib.NullProgressMonitor;
import org.eclipse.jgit.lib.ObjectChecker;
-import org.eclipse.jgit.lib.ObjectDatabase;
-import org.eclipse.jgit.lib.ObjectDirectory;
import org.eclipse.jgit.lib.ObjectId;
-import org.eclipse.jgit.lib.ObjectWriter;
-import org.eclipse.jgit.lib.PackFile;
-import org.eclipse.jgit.lib.PackWriter;
+import org.eclipse.jgit.lib.ObjectInserter;
import org.eclipse.jgit.lib.PersonIdent;
import org.eclipse.jgit.lib.Ref;
import org.eclipse.jgit.lib.RefUpdate;
import org.eclipse.jgit.lib.RefWriter;
import org.eclipse.jgit.lib.Repository;
import org.eclipse.jgit.lib.Tag;
-import org.eclipse.jgit.lib.PackIndex.MutableEntry;
import org.eclipse.jgit.revwalk.ObjectWalk;
import org.eclipse.jgit.revwalk.RevBlob;
import org.eclipse.jgit.revwalk.RevCommit;
@@ -96,11 +90,22 @@
import org.eclipse.jgit.revwalk.RevTag;
import org.eclipse.jgit.revwalk.RevTree;
import org.eclipse.jgit.revwalk.RevWalk;
+import org.eclipse.jgit.storage.file.FileRepository;
+import org.eclipse.jgit.storage.file.LockFile;
+import org.eclipse.jgit.storage.file.ObjectDirectory;
+import org.eclipse.jgit.storage.file.PackFile;
+import org.eclipse.jgit.storage.file.PackIndex.MutableEntry;
+import org.eclipse.jgit.storage.pack.PackWriter;
import org.eclipse.jgit.treewalk.TreeWalk;
import org.eclipse.jgit.treewalk.filter.PathFilterGroup;
-/** Wrapper to make creating test data easier. */
-public class TestRepository {
+/**
+ * Wrapper to make creating test data easier.
+ *
+ * @param <R>
+ * type of Repository the test data is stored on.
+ */
+public class TestRepository<R extends Repository> {
private static final PersonIdent author;
private static final PersonIdent committer;
@@ -119,11 +124,11 @@ public class TestRepository {
committer = new PersonIdent(cn, ce, now, tz);
}
- private final Repository db;
+ private final R db;
private final RevWalk pool;
- private final ObjectWriter writer;
+ private final ObjectInserter inserter;
private long now;
@@ -132,9 +137,9 @@ public class TestRepository {
*
* @param db
* the test repository to write into.
- * @throws Exception
+ * @throws IOException
*/
- public TestRepository(Repository db) throws Exception {
+ public TestRepository(R db) throws IOException {
this(db, new RevWalk(db));
}
@@ -145,17 +150,17 @@ public TestRepository(Repository db) throws Exception {
* the test repository to write into.
* @param rw
* the RevObject pool to use for object lookup.
- * @throws Exception
+ * @throws IOException
*/
- public TestRepository(Repository db, RevWalk rw) throws Exception {
+ public TestRepository(R db, RevWalk rw) throws IOException {
this.db = db;
this.pool = rw;
- this.writer = new ObjectWriter(db);
+ this.inserter = db.newObjectInserter();
this.now = 1236977987000L;
}
/** @return the repository this helper class operates against. */
- public Repository getRepository() {
+ public R getRepository() {
return db;
}
@@ -200,7 +205,14 @@ public RevBlob blob(final String content) throws Exception {
* @throws Exception
*/
public RevBlob blob(final byte[] content) throws Exception {
- return pool.lookupBlob(writer.writeBlob(content));
+ ObjectId id;
+ try {
+ id = inserter.insert(Constants.OBJ_BLOB, content);
+ inserter.flush();
+ } finally {
+ inserter.release();
+ }
+ return pool.lookupBlob(id);
}
/**
@@ -236,7 +248,14 @@ public RevTree tree(final DirCacheEntry... entries) throws Exception {
for (final DirCacheEntry e : entries)
b.add(e);
b.finish();
- return pool.lookupTree(dc.writeTree(writer));
+ ObjectId root;
+ try {
+ root = dc.writeTree(inserter);
+ inserter.flush();
+ } finally {
+ inserter.release();
+ }
+ return pool.lookupTree(root);
}
/**
@@ -253,7 +272,7 @@ public RevTree tree(final DirCacheEntry... entries) throws Exception {
*/
public RevObject get(final RevTree tree, final String path)
throws AssertionFailedError, Exception {
- final TreeWalk tw = new TreeWalk(db);
+ final TreeWalk tw = new TreeWalk(pool.getObjectReader());
tw.setFilter(PathFilterGroup.createFromStrings(Collections
.singleton(path)));
tw.reset(tree);
@@ -346,7 +365,14 @@ public RevCommit commit(final int secDelta, final RevTree tree,
c.setAuthor(new PersonIdent(author, new Date(now)));
c.setCommitter(new PersonIdent(committer, new Date(now)));
c.setMessage("");
- return pool.lookupCommit(writer.writeCommit(c));
+ ObjectId id;
+ try {
+ id = inserter.insert(Constants.OBJ_COMMIT, inserter.format(c));
+ inserter.flush();
+ } finally {
+ inserter.release();
+ }
+ return pool.lookupCommit(id);
}
/** @return a new commit builder. */
@@ -377,7 +403,14 @@ public RevTag tag(final String name, final RevObject dst) throws Exception {
t.setTag(name);
t.setTagger(new PersonIdent(committer, new Date(now)));
t.setMessage("");
- return (RevTag) pool.lookupAny(writer.writeTag(t), Constants.OBJ_TAG);
+ ObjectId id;
+ try {
+ id = inserter.insert(Constants.OBJ_TAG, inserter.format(t));
+ inserter.flush();
+ } finally {
+ inserter.release();
+ }
+ return (RevTag) pool.lookupAny(id, Constants.OBJ_TAG);
}
/**
@@ -443,25 +476,27 @@ public <T extends AnyObjectId> T update(String ref, T obj) throws Exception {
* @throws Exception
*/
public void updateServerInfo() throws Exception {
- final ObjectDatabase odb = db.getObjectDatabase();
- if (odb instanceof ObjectDirectory) {
- RefWriter rw = new RefWriter(db.getAllRefs().values()) {
+ if (db instanceof FileRepository) {
+ final FileRepository fr = (FileRepository) db;
+ RefWriter rw = new RefWriter(fr.getAllRefs().values()) {
@Override
protected void writeFile(final String name, final byte[] bin)
throws IOException {
- TestRepository.this.writeFile(name, bin);
+ File path = new File(fr.getDirectory(), name);
+ TestRepository.this.writeFile(path, bin);
}
};
rw.writePackedRefs();
rw.writeInfoRefs();
final StringBuilder w = new StringBuilder();
- for (PackFile p : ((ObjectDirectory) odb).getPacks()) {
+ for (PackFile p : fr.getObjectDatabase().getPacks()) {
w.append("P ");
w.append(p.getPackFile().getName());
w.append('\n');
}
- writeFile("objects/info/packs", Constants.encodeASCII(w.toString()));
+ writeFile(new File(new File(fr.getObjectDatabase().getDirectory(),
+ "info"), "packs"), Constants.encodeASCII(w.toString()));
}
}
@@ -528,7 +563,7 @@ public void fsck(RevObject... tips) throws MissingObjectException,
if (o == null)
break;
- final byte[] bin = db.openObject(o).getCachedBytes();
+ final byte[] bin = db.open(o, o.getType()).getCachedBytes();
oc.checkCommit(bin);
assertHash(o, bin);
}
@@ -538,7 +573,7 @@ public void fsck(RevObject... tips) throws MissingObjectException,
if (o == null)
break;
- final byte[] bin = db.openObject(o).getCachedBytes();
+ final byte[] bin = db.open(o, o.getType()).getCachedBytes();
oc.check(o.getType(), bin);
assertHash(o, bin);
}
@@ -563,38 +598,46 @@ private static void assertHash(RevObject id, byte[] bin) {
* @throws Exception
*/
public void packAndPrune() throws Exception {
- final ObjectDirectory odb = (ObjectDirectory) db.getObjectDatabase();
- final PackWriter pw = new PackWriter(db, NullProgressMonitor.INSTANCE);
+ if (db.getObjectDatabase() instanceof ObjectDirectory) {
+ ObjectDirectory odb = (ObjectDirectory) db.getObjectDatabase();
+ NullProgressMonitor m = NullProgressMonitor.INSTANCE;
- Set<ObjectId> all = new HashSet<ObjectId>();
- for (Ref r : db.getAllRefs().values())
- all.add(r.getObjectId());
- pw.preparePack(all, Collections.<ObjectId> emptySet());
+ final File pack, idx;
+ PackWriter pw = new PackWriter(db);
+ try {
+ Set<ObjectId> all = new HashSet<ObjectId>();
+ for (Ref r : db.getAllRefs().values())
+ all.add(r.getObjectId());
+ pw.preparePack(m, all, Collections.<ObjectId> emptySet());
- final ObjectId name = pw.computeName();
- OutputStream out;
+ 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 = nameFor(odb, name, ".pack");
+ out = new BufferedOutputStream(new FileOutputStream(pack));
+ try {
+ pw.writePack(m, m, out);
+ } finally {
+ out.close();
+ }
+ pack.setReadOnly();
+
+ idx = nameFor(odb, name, ".idx");
+ out = new BufferedOutputStream(new FileOutputStream(idx));
+ try {
+ pw.writeIndex(out);
+ } finally {
+ out.close();
+ }
+ idx.setReadOnly();
+ } finally {
+ pw.release();
+ }
+
+ odb.openPack(pack, idx);
+ updateServerInfo();
+ prunePacked(odb);
}
- pack.setReadOnly();
-
- final File idx = nameFor(odb, name, ".idx");
- out = new BufferedOutputStream(new FileOutputStream(idx));
- try {
- pw.writeIndex(out);
- } finally {
- out.close();
- }
- idx.setReadOnly();
-
- odb.openPack(pack, idx);
- updateServerInfo();
- prunePacked(odb);
}
private void prunePacked(ObjectDirectory odb) {
@@ -609,10 +652,9 @@ private static File nameFor(ObjectDirectory odb, ObjectId name, String t) {
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);
- final LockFile lck = new LockFile(p);
+ private void writeFile(final File p, final byte[] bin) throws IOException,
+ ObjectWritingException {
+ final LockFile lck = new LockFile(p, db.getFS());
if (!lck.lock())
throw new ObjectWritingException("Can't write " + p);
try {
@@ -711,7 +753,8 @@ public CommitBuilder parent(RevCommit p) throws Exception {
if (parents.isEmpty()) {
DirCacheBuilder b = tree.builder();
parseBody(p);
- b.addTree(new byte[0], DirCacheEntry.STAGE_0, db, p.getTree());
+ b.addTree(new byte[0], DirCacheEntry.STAGE_0, pool
+ .getObjectReader(), p.getTree());
b.finish();
}
parents.add(p);
@@ -769,13 +812,21 @@ public RevCommit create() throws Exception {
TestRepository.this.tick(tick);
final Commit c = new Commit(db);
- c.setTreeId(pool.lookupTree(tree.writeTree(writer)));
c.setParentIds(parents.toArray(new RevCommit[parents.size()]));
c.setAuthor(new PersonIdent(author, new Date(now)));
c.setCommitter(new PersonIdent(committer, new Date(now)));
c.setMessage(message);
- self = pool.lookupCommit(writer.writeCommit(c));
+ ObjectId commitId;
+ try {
+ c.setTreeId(tree.writeTree(inserter));
+ commitId = inserter.insert(Constants.OBJ_COMMIT, inserter
+ .format(c));
+ inserter.flush();
+ } finally {
+ inserter.release();
+ }
+ self = pool.lookupCommit(commitId);
if (branch != null)
branch.update(self);
diff --git a/org.eclipse.jgit.pgm/META-INF/MANIFEST.MF b/org.eclipse.jgit.pgm/META-INF/MANIFEST.MF
index 8628896..d6eac23 100644
--- a/org.eclipse.jgit.pgm/META-INF/MANIFEST.MF
+++ b/org.eclipse.jgit.pgm/META-INF/MANIFEST.MF
@@ -17,6 +17,8 @@
org.eclipse.jgit.revplot;version="[0.9.0,0.10.0)",
org.eclipse.jgit.revwalk;version="[0.9.0,0.10.0)",
org.eclipse.jgit.revwalk.filter;version="[0.9.0,0.10.0)",
+ org.eclipse.jgit.storage.file;version="[0.9.0,0.10.0)",
+ org.eclipse.jgit.storage.pack;version="[0.9.0,0.10.0)",
org.eclipse.jgit.transport;version="[0.9.0,0.10.0)",
org.eclipse.jgit.treewalk;version="[0.9.0,0.10.0)",
org.eclipse.jgit.treewalk.filter;version="[0.9.0,0.10.0)",
diff --git a/org.eclipse.jgit.pgm/META-INF/services/org.eclipse.jgit.pgm.TextBuiltin b/org.eclipse.jgit.pgm/META-INF/services/org.eclipse.jgit.pgm.TextBuiltin
index 0d4a140..075cade 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.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/resources/org/eclipse/jgit/pgm/CLIText.properties b/org.eclipse.jgit.pgm/resources/org/eclipse/jgit/pgm/CLIText.properties
index e879d6b..2fff6d4 100644
--- a/org.eclipse.jgit.pgm/resources/org/eclipse/jgit/pgm/CLIText.properties
+++ b/org.eclipse.jgit.pgm/resources/org/eclipse/jgit/pgm/CLIText.properties
@@ -14,6 +14,7 @@
branchIsNotAnAncestorOfYourCurrentHEAD=The branch '{0}' is not an ancestor of your current HEAD.\nIf you are sure you want to delete it, run 'jgit branch -D {0}'.
branchNotFound=branch '{0}' not found.
cacheTreePathInfo="{0}": {1} entries, {2} children
+configFileNotFound=configuration file {0} not found
cannotBeRenamed={0} cannot be renamed
cannotChekoutNoHeadsAdvertisedByRemote=cannot checkout; no HEAD advertised by remote
cannotCreateCommand=Cannot create command {0}
@@ -61,6 +62,7 @@
metaVar_command=command
metaVar_commitOrTag=COMMIT|TAG
metaVar_commitish=commit-ish
+metaVar_configFile=FILE
metaVar_connProp=conn.prop
metaVar_directory=DIRECTORY
metaVar_file=FILE
@@ -138,6 +140,7 @@
usage_beMoreVerbose=be more verbose
usage_beVerbose=be verbose
usage_cloneRepositoryIntoNewDir=Clone a repository into a new directory
+usage_configFile=configuration file
usage_configureTheServiceInDaemonServicename=configure the service in daemon.servicename
usage_deleteBranchEvenIfNotMerged=delete branch (even if not merged)
usage_deleteFullyMergedBranch=delete fully merged branch
diff --git a/org.eclipse.jgit.pgm/src/org/eclipse/jgit/pgm/CLIText.java b/org.eclipse.jgit.pgm/src/org/eclipse/jgit/pgm/CLIText.java
index bae895c..14dcb1f 100644
--- a/org.eclipse.jgit.pgm/src/org/eclipse/jgit/pgm/CLIText.java
+++ b/org.eclipse.jgit.pgm/src/org/eclipse/jgit/pgm/CLIText.java
@@ -67,6 +67,7 @@ public static CLIText get() {
/***/ public String branchIsNotAnAncestorOfYourCurrentHEAD;
/***/ public String branchNotFound;
/***/ public String cacheTreePathInfo;
+ /***/ public String configFileNotFound;
/***/ public String cannotBeRenamed;
/***/ public String cannotChekoutNoHeadsAdvertisedByRemote;
/***/ public String cannotCreateCommand;
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 b0f51ec..22302bb 100644
--- a/org.eclipse.jgit.pgm/src/org/eclipse/jgit/pgm/Clone.java
+++ b/org.eclipse.jgit.pgm/src/org/eclipse/jgit/pgm/Clone.java
@@ -61,10 +61,10 @@
import org.eclipse.jgit.lib.Ref;
import org.eclipse.jgit.lib.RefComparator;
import org.eclipse.jgit.lib.RefUpdate;
-import org.eclipse.jgit.lib.Repository;
import org.eclipse.jgit.lib.TextProgressMonitor;
import org.eclipse.jgit.lib.Tree;
import org.eclipse.jgit.lib.WorkDirCheckout;
+import org.eclipse.jgit.storage.file.FileRepository;
import org.eclipse.jgit.transport.FetchResult;
import org.eclipse.jgit.transport.RefSpec;
import org.eclipse.jgit.transport.RemoteConfig;
@@ -82,6 +82,8 @@ class Clone extends AbstractFetchCommand {
@Argument(index = 1, metaVar = "metaVar_directory")
private String localName;
+ private FileRepository dst;
+
@Override
protected final boolean requiresRepository() {
return false;
@@ -103,10 +105,11 @@ protected void run() throws Exception {
if (gitdir == null)
gitdir = new File(localName, Constants.DOT_GIT);
- db = new Repository(gitdir);
- db.create();
- db.getConfig().setBoolean("core", null, "bare", false);
- db.getConfig().save();
+ dst = new FileRepository(gitdir);
+ dst.create();
+ dst.getConfig().setBoolean("core", null, "bare", false);
+ dst.getConfig().save();
+ db = dst;
out.format(CLIText.get().initializedEmptyGitRepositoryIn, gitdir.getAbsolutePath());
out.println();
@@ -120,13 +123,13 @@ protected void run() throws Exception {
private void saveRemote(final URIish uri) throws URISyntaxException,
IOException {
- final RemoteConfig rc = new RemoteConfig(db.getConfig(), remoteName);
+ final RemoteConfig rc = new RemoteConfig(dst.getConfig(), remoteName);
rc.addURI(uri);
rc.addFetchRefSpec(new RefSpec().setForceUpdate(true)
.setSourceDestination(Constants.R_HEADS + "*",
Constants.R_REMOTES + remoteName + "/*"));
- rc.update(db.getConfig());
- db.getConfig().save();
+ rc.update(dst.getConfig());
+ dst.getConfig().save();
}
private FetchResult runFetch() throws NotSupportedException,
@@ -180,7 +183,7 @@ private void doCheckout(final Ref branch) throws IOException {
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/Daemon.java b/org.eclipse.jgit.pgm/src/org/eclipse/jgit/pgm/Daemon.java
index f015a9e..3cca87a 100644
--- a/org.eclipse.jgit.pgm/src/org/eclipse/jgit/pgm/Daemon.java
+++ b/org.eclipse.jgit.pgm/src/org/eclipse/jgit/pgm/Daemon.java
@@ -48,13 +48,22 @@
import java.text.MessageFormat;
import java.util.ArrayList;
import java.util.List;
+import java.util.concurrent.Executors;
+import org.eclipse.jgit.storage.file.FileBasedConfig;
+import org.eclipse.jgit.storage.file.WindowCache;
+import org.eclipse.jgit.storage.file.WindowCacheConfig;
+import org.eclipse.jgit.storage.pack.PackConfig;
+import org.eclipse.jgit.transport.DaemonService;
+import org.eclipse.jgit.util.FS;
import org.kohsuke.args4j.Argument;
import org.kohsuke.args4j.Option;
-import org.eclipse.jgit.transport.DaemonService;
@Command(common = true, usage = "usage_exportRepositoriesOverGit")
class Daemon extends TextBuiltin {
+ @Option(name = "--config-file", metaVar = "metaVar_configFile", usage = "usage_configFile")
+ File configFile;
+
@Option(name = "--port", metaVar = "metaVar_port", usage = "usage_portNumberToListenOn")
int port = org.eclipse.jgit.transport.Daemon.DEFAULT_PORT;
@@ -89,12 +98,38 @@ protected boolean requiresRepository() {
@Override
protected void run() throws Exception {
+ PackConfig packConfig = new PackConfig();
+
+ if (configFile != null) {
+ if (!configFile.exists()) {
+ throw die(MessageFormat.format(
+ CLIText.get().configFileNotFound, //
+ configFile.getAbsolutePath()));
+ }
+
+ FileBasedConfig cfg = new FileBasedConfig(configFile, FS.DETECTED);
+ cfg.load();
+
+ WindowCacheConfig wcc = new WindowCacheConfig();
+ wcc.fromConfig(cfg);
+ WindowCache.reconfigure(wcc);
+
+ packConfig.fromConfig(cfg);
+ }
+
+ int threads = packConfig.getThreads();
+ if (threads <= 0)
+ threads = Runtime.getRuntime().availableProcessors();
+ if (1 < threads)
+ packConfig.setExecutor(Executors.newFixedThreadPool(threads));
+
final org.eclipse.jgit.transport.Daemon d;
d = new org.eclipse.jgit.transport.Daemon(
host != null ? new InetSocketAddress(host, port)
: new InetSocketAddress(port));
d.setExportAll(exportAll);
+ d.setPackConfig(packConfig);
if (0 <= timeout)
d.setTimeout(timeout);
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 77ed730..ebdb74f 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
@@ -153,22 +153,22 @@ static void nameStatus(PrintWriter out, List<DiffEntry> files) {
for (DiffEntry ent : files) {
switch (ent.getChangeType()) {
case ADD:
- out.println("A\t" + ent.getNewName());
+ out.println("A\t" + ent.getNewPath());
break;
case DELETE:
- out.println("D\t" + ent.getOldName());
+ out.println("D\t" + ent.getOldPath());
break;
case MODIFY:
- out.println("M\t" + ent.getNewName());
+ out.println("M\t" + ent.getNewPath());
break;
case COPY:
out.format("C%1$03d\t%2$s\t%3$s", ent.getScore(), //
- ent.getOldName(), ent.getNewName());
+ ent.getOldPath(), ent.getNewPath());
out.println();
break;
case RENAME:
out.format("R%1$03d\t%2$s\t%3$s", ent.getScore(), //
- ent.getOldName(), ent.getNewName());
+ ent.getOldPath(), ent.getNewPath());
out.println();
break;
}
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 3dfd8ff..ae11f67 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 @@ protected RevWalk createWalk() {
}
private String repoName() {
- final File f = db.getDirectory();
- String n = f.getName();
+ final File gitDir = db.getDirectory();
+ if (gitDir == null)
+ return db.toString();
+ String n = gitDir.getName();
if (Constants.DOT_GIT.equals(n))
- n = f.getParentFile().getName();
+ n = gitDir.getParentFile().getName();
return n;
}
}
diff --git a/org.eclipse.jgit.pgm/src/org/eclipse/jgit/pgm/IndexPack.java b/org.eclipse.jgit.pgm/src/org/eclipse/jgit/pgm/IndexPack.java
index 35fd2a5..640c8ef 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 org.kohsuke.args4j.Argument;
import org.kohsuke.args4j.Option;
+import org.eclipse.jgit.lib.CoreConfig;
import org.eclipse.jgit.lib.TextProgressMonitor;
class IndexPack extends TextBuiltin {
@@ -64,7 +65,8 @@ class IndexPack extends TextBuiltin {
@Override
protected void run() throws Exception {
if (indexVersion == -1)
- indexVersion = db.getConfig().getCore().getPackIndexVersion();
+ indexVersion = db.getConfig().get(CoreConfig.KEY)
+ .getPackIndexVersion();
final BufferedInputStream in;
final org.eclipse.jgit.transport.IndexPack ip;
in = new BufferedInputStream(System.in);
diff --git a/org.eclipse.jgit.pgm/src/org/eclipse/jgit/pgm/Init.java b/org.eclipse.jgit.pgm/src/org/eclipse/jgit/pgm/Init.java
index d8c7bdf..c5a696a 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 org.kohsuke.args4j.Option;
import org.eclipse.jgit.lib.Constants;
-import org.eclipse.jgit.lib.Repository;
+import org.eclipse.jgit.storage.file.FileRepository;
@Command(common = true, usage = "usage_CreateAnEmptyGitRepository")
class Init extends TextBuiltin {
@@ -66,7 +66,7 @@ protected final boolean requiresRepository() {
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/Log.java b/org.eclipse.jgit.pgm/src/org/eclipse/jgit/pgm/Log.java
index 48a0591..fbc019f 100644
--- a/org.eclipse.jgit.pgm/src/org/eclipse/jgit/pgm/Log.java
+++ b/org.eclipse.jgit.pgm/src/org/eclipse/jgit/pgm/Log.java
@@ -242,7 +242,7 @@ private boolean isAdd(List<DiffEntry> files) {
String oldPath = ((FollowFilter) pathFilter).getPath();
for (DiffEntry ent : files) {
if (ent.getChangeType() == ChangeType.ADD
- && ent.getNewName().equals(oldPath))
+ && ent.getNewPath().equals(oldPath))
return true;
}
return false;
@@ -251,8 +251,8 @@ private boolean isAdd(List<DiffEntry> files) {
private List<DiffEntry> updateFollowFilter(List<DiffEntry> files) {
String oldPath = ((FollowFilter) pathFilter).getPath();
for (DiffEntry ent : files) {
- if (isRename(ent) && ent.getNewName().equals(oldPath)) {
- pathFilter = FollowFilter.create(ent.getOldName());
+ if (isRename(ent) && ent.getNewPath().equals(oldPath)) {
+ pathFilter = FollowFilter.create(ent.getOldPath());
return Collections.singletonList(ent);
}
}
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 306ac81..ab11062 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.URL;
import java.text.MessageFormat;
import java.util.ArrayList;
-import java.util.Arrays;
-import java.util.HashSet;
import java.util.List;
-import java.util.Set;
import org.eclipse.jgit.awtui.AwtAuthenticator;
import org.eclipse.jgit.awtui.AwtSshSessionFactory;
import org.eclipse.jgit.errors.TransportException;
-import org.eclipse.jgit.lib.Constants;
-import org.eclipse.jgit.lib.Repository;
+import org.eclipse.jgit.lib.RepositoryBuilder;
import org.eclipse.jgit.pgm.opt.CmdLineParser;
import org.eclipse.jgit.pgm.opt.SubcommandHandler;
import org.eclipse.jgit.util.CachedAuthenticator;
-import org.eclipse.jgit.util.SystemReader;
import org.kohsuke.args4j.Argument;
import org.kohsuke.args4j.CmdLineException;
import org.kohsuke.args4j.ExampleMode;
@@ -168,51 +163,17 @@ private void execute(final String[] argv) throws Exception {
final TextBuiltin cmd = subcommand;
if (cmd.requiresRepository()) {
- if (gitdir == null) {
- String gitDirEnv = SystemReader.getInstance().getenv(Constants.GIT_DIR_KEY);
- if (gitDirEnv != null)
- gitdir = new File(gitDirEnv);
- }
- if (gitdir == null)
- gitdir = findGitDir();
-
- File gitworktree;
- String gitWorkTreeEnv = SystemReader.getInstance().getenv(Constants.GIT_WORK_TREE_KEY);
- if (gitWorkTreeEnv != null)
- gitworktree = new File(gitWorkTreeEnv);
- else
- gitworktree = null;
-
- File indexfile;
- String indexFileEnv = SystemReader.getInstance().getenv(Constants.GIT_INDEX_KEY);
- if (indexFileEnv != null)
- indexfile = new File(indexFileEnv);
- else
- indexfile = null;
-
- File objectdir;
- String objectDirEnv = SystemReader.getInstance().getenv(Constants.GIT_OBJECT_DIRECTORY_KEY);
- if (objectDirEnv != null)
- objectdir = new File(objectDirEnv);
- else
- objectdir = null;
-
- File[] altobjectdirs;
- String altObjectDirEnv = SystemReader.getInstance().getenv(Constants.GIT_ALTERNATE_OBJECT_DIRECTORIES_KEY);
- if (altObjectDirEnv != null) {
- String[] parserdAltObjectDirEnv = altObjectDirEnv.split(File.pathSeparator);
- altobjectdirs = new File[parserdAltObjectDirEnv.length];
- for (int i = 0; i < parserdAltObjectDirEnv.length; i++)
- altobjectdirs[i] = new File(parserdAltObjectDirEnv[i]);
- } else
- altobjectdirs = null;
-
- if (gitdir == null || !gitdir.isDirectory()) {
+ RepositoryBuilder rb = new RepositoryBuilder() //
+ .setGitDir(gitdir) //
+ .readEnvironment() //
+ .findGitDir();
+ if (rb.getGitDir() == null) {
writer.println(CLIText.get().cantFindGitDirectory);
writer.flush();
System.exit(1);
}
- cmd.init(new Repository(gitdir, gitworktree, objectdir, altobjectdirs, indexfile), gitdir);
+
+ cmd.init(rb.build(), null);
} else {
cmd.init(null, gitdir);
}
@@ -224,27 +185,6 @@ private void execute(final String[] argv) throws Exception {
}
}
- private static File findGitDir() {
- Set<String> ceilingDirectories = new HashSet<String>();
- String ceilingDirectoriesVar = SystemReader.getInstance().getenv(
- Constants.GIT_CEILING_DIRECTORIES_KEY);
- if (ceilingDirectoriesVar != null) {
- ceilingDirectories.addAll(Arrays.asList(ceilingDirectoriesVar
- .split(File.pathSeparator)));
- }
- File current = new File("").getAbsoluteFile();
- while (current != null) {
- final File gitDir = new File(current, Constants.DOT_GIT);
- if (gitDir.isDirectory())
- return gitDir;
- current = current.getParentFile();
- if (current != null
- && ceilingDirectories.contains(current.getPath()))
- break;
- }
- return null;
- }
-
private static boolean installConsole() {
try {
install("org.eclipse.jgit.console.ConsoleAuthenticator");
diff --git a/org.eclipse.jgit.pgm/src/org/eclipse/jgit/pgm/ReceivePack.java b/org.eclipse.jgit.pgm/src/org/eclipse/jgit/pgm/ReceivePack.java
index 09a9f2b..7a27617 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 @@
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 @@ protected final boolean requiresRepository() {
protected void run() throws Exception {
final org.eclipse.jgit.transport.ReceivePack rp;
- if (new File(dstGitdir, Constants.DOT_GIT).isDirectory())
- dstGitdir = new File(dstGitdir, Constants.DOT_GIT);
- db = new Repository(dstGitdir);
- if (!db.getObjectsDirectory().isDirectory())
- throw die(MessageFormat.format(CLIText.get().notAGitRepository, dstGitdir.getPath()));
+ try {
+ FileKey key = FileKey.lenient(dstGitdir, FS.DETECTED);
+ db = key.open(true /* must exist */);
+ } catch (RepositoryNotFoundException notFound) {
+ throw die(MessageFormat.format(CLIText.get().notAGitRepository,
+ dstGitdir.getPath()));
+ }
+
rp = new org.eclipse.jgit.transport.ReceivePack(db);
rp.receive(System.in, System.out, System.err);
}
diff --git a/org.eclipse.jgit.pgm/src/org/eclipse/jgit/pgm/Rm.java b/org.eclipse.jgit.pgm/src/org/eclipse/jgit/pgm/Rm.java
index 1b8711d..9f577ff 100644
--- a/org.eclipse.jgit.pgm/src/org/eclipse/jgit/pgm/Rm.java
+++ b/org.eclipse.jgit.pgm/src/org/eclipse/jgit/pgm/Rm.java
@@ -67,9 +67,9 @@ class Rm extends TextBuiltin {
@Override
protected void run() throws Exception {
- root = db.getWorkDir();
+ root = db.getWorkTree();
- final DirCache dirc = DirCache.lock(db);
+ final DirCache dirc = db.lockDirCache();
final DirCacheBuilder edit = dirc.builder();
final TreeWalk walk = new TreeWalk(db);
diff --git a/org.eclipse.jgit.pgm/src/org/eclipse/jgit/pgm/Tag.java b/org.eclipse.jgit.pgm/src/org/eclipse/jgit/pgm/Tag.java
index 63d26ea..c798950 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 @@
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 @@ protected void run() throws Exception {
, tagName.substring(Constants.R_TAGS.length())));
}
- final ObjectLoader ldr = db.openObject(object);
- if (ldr == null)
- throw new MissingObjectException(object, "any");
+ final ObjectLoader ldr = db.open(object);
org.eclipse.jgit.lib.Tag tag = new org.eclipse.jgit.lib.Tag(db);
tag.setObjId(object);
diff --git a/org.eclipse.jgit.pgm/src/org/eclipse/jgit/pgm/UploadPack.java b/org.eclipse.jgit.pgm/src/org/eclipse/jgit/pgm/UploadPack.java
index 52d2488..d4e2bce 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 @@
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 @@ protected final boolean requiresRepository() {
@Override
protected void run() throws Exception {
- final org.eclipse.jgit.transport.UploadPack rp;
+ final org.eclipse.jgit.transport.UploadPack up;
- if (new File(srcGitdir, Constants.DOT_GIT).isDirectory())
- srcGitdir = new File(srcGitdir, Constants.DOT_GIT);
- db = new Repository(srcGitdir);
- if (!db.getObjectsDirectory().isDirectory())
- throw die(MessageFormat.format(CLIText.get().notAGitRepository, srcGitdir.getPath()));
- rp = new org.eclipse.jgit.transport.UploadPack(db);
+ try {
+ FileKey key = FileKey.lenient(srcGitdir, FS.DETECTED);
+ db = key.open(true /* must exist */);
+ } catch (RepositoryNotFoundException notFound) {
+ throw die(MessageFormat.format(CLIText.get().notAGitRepository,
+ srcGitdir.getPath()));
+ }
+
+ up = new org.eclipse.jgit.transport.UploadPack(db);
if (0 <= timeout)
- rp.setTimeout(timeout);
- rp.upload(System.in, System.out, System.err);
+ up.setTimeout(timeout);
+ up.upload(System.in, System.out, System.err);
}
}
diff --git a/org.eclipse.jgit.pgm/src/org/eclipse/jgit/pgm/Version.java b/org.eclipse.jgit.pgm/src/org/eclipse/jgit/pgm/Version.java
index 5bad4ef..9b51b87 100644
--- a/org.eclipse.jgit.pgm/src/org/eclipse/jgit/pgm/Version.java
+++ b/org.eclipse.jgit.pgm/src/org/eclipse/jgit/pgm/Version.java
@@ -55,4 +55,9 @@ protected void run() throws Exception {
out.println(MessageFormat.format(CLIText.get().jgitVersion, pkg.getImplementationVersion()));
}
+
+ @Override
+ protected final boolean requiresRepository() {
+ return false;
+ }
}
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 d772ffe..709b45a 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 @@
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 2a1079b..0ca0508 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 @@
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 @@ protected void run() throws Exception {
final int cnt = 100;
final long start = System.currentTimeMillis();
for (int i = 0; i < cnt; i++)
- DirCache.read(db);
+ db.readDirCache();
final long end = System.currentTimeMillis();
out.print(" ");
out.println(MessageFormat.format(CLIText.get().averageMSPerRead, (end - start) / cnt));
diff --git a/org.eclipse.jgit.pgm/src/org/eclipse/jgit/pgm/debug/RebuildCommitGraph.java b/org.eclipse.jgit.pgm/src/org/eclipse/jgit/pgm/debug/RebuildCommitGraph.java
index 1681dbc..b8e2a8f 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.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.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.
@@ -227,7 +227,7 @@ private void detachHead() throws IOException {
final ObjectId id = db.resolve(Constants.HEAD);
if (!ObjectId.isId(head) && id != null) {
final LockFile lf;
- lf = new LockFile(new File(db.getDirectory(), Constants.HEAD));
+ lf = new LockFile(new File(db.getDirectory(), Constants.HEAD), db.getFS());
if (!lf.lock())
throw new IOException(MessageFormat.format(CLIText.get().cannotLock, Constants.HEAD));
lf.write(id);
@@ -254,7 +254,7 @@ private void recreateRefs() throws Exception {
protected void writeFile(final String name, final byte[] content)
throws IOException {
final File file = new File(db.getDirectory(), name);
- final LockFile lck = new LockFile(file);
+ final LockFile lck = new LockFile(file, db.getFS());
if (!lck.lock())
throw new ObjectWritingException(MessageFormat.format(CLIText.get().cantWrite, file));
try {
@@ -297,6 +297,7 @@ private Map<String, Ref> computeNewRefs() throws IOException {
name, id));
}
} finally {
+ rw.release();
br.close();
}
return refs;
diff --git a/org.eclipse.jgit.pgm/src/org/eclipse/jgit/pgm/debug/ShowCacheTree.java b/org.eclipse.jgit.pgm/src/org/eclipse/jgit/pgm/debug/ShowCacheTree.java
index 09796ed..c49aefb 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 @@
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 854596c..a94d37f 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 @@ protected void run() throws Exception {
final SimpleDateFormat fmt;
fmt = new SimpleDateFormat("yyyyMMdd,HHmmss.SSS");
- final DirCache cache = DirCache.read(db);
+ final DirCache cache = db.readDirCache();
for (int i = 0; i < cache.getEntryCount(); i++) {
final DirCacheEntry ent = cache.getEntry(i);
final FileMode mode = FileMode.fromBits(ent.getRawMode());
diff --git a/org.eclipse.jgit.pgm/src/org/eclipse/jgit/pgm/debug/ShowPackDelta.java b/org.eclipse.jgit.pgm/src/org/eclipse/jgit/pgm/debug/ShowPackDelta.java
new file mode 100644
index 0000000..1718ef3
--- /dev/null
+++ b/org.eclipse.jgit.pgm/src/org/eclipse/jgit/pgm/debug/ShowPackDelta.java
@@ -0,0 +1,127 @@
+/*
+ * Copyright (C) 2010, Google Inc.
+ * and other copyright owners as documented in the project's IP log.
+ *
+ * This program and the accompanying materials are made available
+ * under the terms of the Eclipse Distribution License v1.0 which
+ * accompanies this distribution, is reproduced below, and is
+ * available at http://www.eclipse.org/org/documents/edl-v10.php
+ *
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or
+ * without modification, are permitted provided that the following
+ * conditions are met:
+ *
+ * - Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ *
+ * - Redistributions in binary form must reproduce the above
+ * copyright notice, this list of conditions and the following
+ * disclaimer in the documentation and/or other materials provided
+ * with the distribution.
+ *
+ * - Neither the name of the Eclipse Foundation, Inc. nor the
+ * names of its contributors may be used to endorse or promote
+ * products derived from this software without specific prior
+ * written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND
+ * CONTRIBUTORS "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES,
+ * INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
+ * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR
+ * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
+ * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT
+ * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
+ * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER
+ * CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT,
+ * STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
+ * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF
+ * ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ */
+
+package org.eclipse.jgit.pgm.debug;
+
+import java.io.ByteArrayInputStream;
+import java.io.ByteArrayOutputStream;
+import java.io.IOException;
+import java.util.zip.InflaterInputStream;
+
+import org.eclipse.jgit.errors.MissingObjectException;
+import org.eclipse.jgit.errors.StoredObjectRepresentationNotAvailableException;
+import org.eclipse.jgit.lib.NullProgressMonitor;
+import org.eclipse.jgit.lib.ObjectId;
+import org.eclipse.jgit.lib.ObjectReader;
+import org.eclipse.jgit.pgm.TextBuiltin;
+import org.eclipse.jgit.revwalk.RevObject;
+import org.eclipse.jgit.revwalk.RevWalk;
+import org.eclipse.jgit.storage.pack.BinaryDelta;
+import org.eclipse.jgit.storage.pack.ObjectReuseAsIs;
+import org.eclipse.jgit.storage.pack.ObjectToPack;
+import org.eclipse.jgit.storage.pack.PackOutputStream;
+import org.eclipse.jgit.storage.pack.PackWriter;
+import org.eclipse.jgit.storage.pack.StoredObjectRepresentation;
+import org.eclipse.jgit.util.TemporaryBuffer;
+import org.kohsuke.args4j.Argument;
+
+class ShowPackDelta extends TextBuiltin {
+ @Argument(index = 0)
+ private ObjectId objectId;
+
+ @Override
+ protected void run() throws Exception {
+ ObjectReader reader = db.newObjectReader();
+ RevObject obj = new RevWalk(reader).parseAny(objectId);
+ byte[] delta = getDelta(reader, obj);
+
+ // We're crossing our fingers that this will be a delta. Double
+ // check the size field in the header, it should match.
+ //
+ long size = reader.getObjectSize(obj, obj.getType());
+ try {
+ if (BinaryDelta.getResultSize(delta) != size)
+ throw die("Object " + obj.name() + " is not a delta");
+ } catch (ArrayIndexOutOfBoundsException bad) {
+ throw die("Object " + obj.name() + " is not a delta");
+ }
+
+ out.println(BinaryDelta.format(delta));
+ }
+
+ private byte[] getDelta(ObjectReader reader, RevObject obj)
+ throws IOException, MissingObjectException,
+ StoredObjectRepresentationNotAvailableException {
+ ObjectReuseAsIs asis = (ObjectReuseAsIs) reader;
+ ObjectToPack target = asis.newObjectToPack(obj);
+
+ PackWriter pw = new PackWriter(reader) {
+ @Override
+ public void select(ObjectToPack otp, StoredObjectRepresentation next) {
+ otp.select(next);
+ }
+ };
+
+ ByteArrayOutputStream buf = new ByteArrayOutputStream();
+ asis.selectObjectRepresentation(pw, target);
+ asis.copyObjectAsIs(new PackOutputStream(NullProgressMonitor.INSTANCE,
+ buf, pw), target);
+
+ // At this point the object header has no delta information,
+ // because it was output as though it were a whole object.
+ // Skip over the header and inflate.
+ //
+ byte[] bufArray = buf.toByteArray();
+ int ptr = 0;
+ while ((bufArray[ptr] & 0x80) != 0)
+ ptr++;
+ ptr++;
+
+ TemporaryBuffer.Heap raw = new TemporaryBuffer.Heap(bufArray.length);
+ InflaterInputStream inf = new InflaterInputStream(
+ new ByteArrayInputStream(bufArray, ptr, bufArray.length));
+ raw.copy(inf);
+ inf.close();
+ return raw.toByteArray();
+ }
+}
diff --git a/org.eclipse.jgit.pgm/src/org/eclipse/jgit/pgm/debug/WriteDirCache.java b/org.eclipse.jgit.pgm/src/org/eclipse/jgit/pgm/debug/WriteDirCache.java
index cee5966..142dbee 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 @@
class WriteDirCache extends TextBuiltin {
@Override
protected void run() throws Exception {
- final DirCache cache = DirCache.read(db);
+ final DirCache cache = db.readDirCache();
if (!cache.lock())
throw die(CLIText.get().failedToLockIndex);
cache.read();
diff --git a/org.eclipse.jgit.pgm/src/org/eclipse/jgit/pgm/eclipse/Iplog.java b/org.eclipse.jgit.pgm/src/org/eclipse/jgit/pgm/eclipse/Iplog.java
index e13bb1f..84859a8 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 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.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;
@@ -98,7 +98,7 @@ else if (version == null)
if (output != null) {
if (!output.getParentFile().exists())
output.getParentFile().mkdirs();
- LockFile lf = new LockFile(output);
+ LockFile lf = new LockFile(output, db.getFS());
if (!lf.lock())
throw die(MessageFormat.format(CLIText.get().cannotLock, output));
try {
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 4f0e338..6653209 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,9 +91,9 @@ protected void run() throws Exception {
}
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);
+ meta.syncCQs(output, db.getFS(), ipzilla, username, password);
}
}
diff --git a/org.eclipse.jgit.pgm/src/org/eclipse/jgit/pgm/opt/AbstractTreeIteratorHandler.java b/org.eclipse.jgit.pgm/src/org/eclipse/jgit/pgm/opt/AbstractTreeIteratorHandler.java
index 2043ac2..7126982 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.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;
@@ -103,7 +103,7 @@ public int parseArguments(final Parameters params) throws CmdLineException {
if (new File(name).isFile()) {
final DirCache dirc;
try {
- dirc = DirCache.read(new File(name));
+ dirc = DirCache.read(new File(name), FS.DETECTED);
} catch (IOException e) {
throw new CmdLineException(MessageFormat.format(CLIText.get().notAnIndexFile, name), e);
}
@@ -121,9 +121,9 @@ public int parseArguments(final Parameters params) throws CmdLineException {
throw new CmdLineException(MessageFormat.format(CLIText.get().notATree, name));
final CanonicalTreeParser p = new CanonicalTreeParser();
- final WindowCursor curs = new WindowCursor();
+ final ObjectReader curs = clp.getRepository().newObjectReader();
try {
- p.reset(clp.getRepository(), clp.getRevWalk().parseTree(id), curs);
+ p.reset(curs, clp.getRevWalk().parseTree(id));
} catch (MissingObjectException e) {
throw new CmdLineException(MessageFormat.format(CLIText.get().notATree, name));
} catch (IncorrectObjectTypeException e) {
diff --git a/org.eclipse.jgit.test/META-INF/MANIFEST.MF b/org.eclipse.jgit.test/META-INF/MANIFEST.MF
index 3aaa8a4..d6128af 100644
--- a/org.eclipse.jgit.test/META-INF/MANIFEST.MF
+++ b/org.eclipse.jgit.test/META-INF/MANIFEST.MF
@@ -29,6 +29,8 @@
org.eclipse.jgit.revplot;version="[0.9.0,0.10.0)",
org.eclipse.jgit.revwalk;version="[0.9.0,0.10.0)",
org.eclipse.jgit.revwalk.filter;version="[0.9.0,0.10.0)",
+ org.eclipse.jgit.storage.file;version="[0.9.0,0.10.0)",
+ org.eclipse.jgit.storage.pack;version="[0.9.0,0.10.0)",
org.eclipse.jgit.transport;version="[0.9.0,0.10.0)",
org.eclipse.jgit.treewalk;version="[0.9.0,0.10.0)",
org.eclipse.jgit.treewalk.filter;version="[0.9.0,0.10.0)",
diff --git a/org.eclipse.jgit.test/exttst/org/eclipse/jgit/lib/T0007_GitIndexTest.java b/org.eclipse.jgit.test/exttst/org/eclipse/jgit/lib/T0007_GitIndexTest.java
index dd3b51e..c5591b9bf 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 void run() {
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/exttst/org/eclipse/jgit/patch/EGitPatchHistoryTest.java b/org.eclipse.jgit.test/exttst/org/eclipse/jgit/patch/EGitPatchHistoryTest.java
index ae9fb0e..ff442b6 100644
--- a/org.eclipse.jgit.test/exttst/org/eclipse/jgit/patch/EGitPatchHistoryTest.java
+++ b/org.eclipse.jgit.test/exttst/org/eclipse/jgit/patch/EGitPatchHistoryTest.java
@@ -108,9 +108,9 @@ void onCommit(String cid, byte[] buf) {
for (final FileHeader fh : p.getFiles()) {
final String fileName;
if (fh.getChangeType() != FileHeader.ChangeType.DELETE)
- fileName = fh.getNewName();
+ fileName = fh.getNewPath();
else
- fileName = fh.getOldName();
+ fileName = fh.getOldPath();
final StatInfo s = files.remove(fileName);
final String nid = fileName + " in " + cid;
assertNotNull("No " + nid, s);
diff --git a/org.eclipse.jgit.test/tst-rsrc/org/eclipse/jgit/test/resources/excludeTest/.gitignore b/org.eclipse.jgit.test/tst-rsrc/org/eclipse/jgit/test/resources/excludeTest/.gitignore
deleted file mode 100644
index b3f6bc9..0000000
--- a/org.eclipse.jgit.test/tst-rsrc/org/eclipse/jgit/test/resources/excludeTest/.gitignore
+++ /dev/null
@@ -1 +0,0 @@
-!/notignored
diff --git a/org.eclipse.jgit.test/tst-rsrc/org/eclipse/jgit/test/resources/excludeTest/new/.gitignore b/org.eclipse.jgit.test/tst-rsrc/org/eclipse/jgit/test/resources/excludeTest/new/.gitignore
deleted file mode 100644
index 09b8574..0000000
--- a/org.eclipse.jgit.test/tst-rsrc/org/eclipse/jgit/test/resources/excludeTest/new/.gitignore
+++ /dev/null
@@ -1 +0,0 @@
-notarealfile
diff --git a/org.eclipse.jgit.test/tst-rsrc/org/eclipse/jgit/test/resources/excludeTest/new/a/.gitignore b/org.eclipse.jgit.test/tst-rsrc/org/eclipse/jgit/test/resources/excludeTest/new/a/.gitignore
deleted file mode 100644
index e69de29..0000000
--- a/org.eclipse.jgit.test/tst-rsrc/org/eclipse/jgit/test/resources/excludeTest/new/a/.gitignore
+++ /dev/null
diff --git a/org.eclipse.jgit.test/tst-rsrc/org/eclipse/jgit/test/resources/excludeTest/new/a/b1/.gitignore b/org.eclipse.jgit.test/tst-rsrc/org/eclipse/jgit/test/resources/excludeTest/new/a/b1/.gitignore
deleted file mode 100644
index 82b0f5d..0000000
--- a/org.eclipse.jgit.test/tst-rsrc/org/eclipse/jgit/test/resources/excludeTest/new/a/b1/.gitignore
+++ /dev/null
@@ -1 +0,0 @@
-/c
diff --git a/org.eclipse.jgit.test/tst-rsrc/org/eclipse/jgit/test/resources/excludeTest/new/a/b1/test.stp b/org.eclipse.jgit.test/tst-rsrc/org/eclipse/jgit/test/resources/excludeTest/new/a/b1/test.stp
deleted file mode 100644
index e69de29..0000000
--- a/org.eclipse.jgit.test/tst-rsrc/org/eclipse/jgit/test/resources/excludeTest/new/a/b1/test.stp
+++ /dev/null
diff --git a/org.eclipse.jgit.test/tst-rsrc/org/eclipse/jgit/test/resources/excludeTest/new/a/b2/c/.gitignore b/org.eclipse.jgit.test/tst-rsrc/org/eclipse/jgit/test/resources/excludeTest/new/a/b2/c/.gitignore
deleted file mode 100644
index 3c6cf10..0000000
--- a/org.eclipse.jgit.test/tst-rsrc/org/eclipse/jgit/test/resources/excludeTest/new/a/b2/c/.gitignore
+++ /dev/null
@@ -1 +0,0 @@
-/notarealfile2
diff --git a/org.eclipse.jgit.test/tst-rsrc/org/eclipse/jgit/test/resources/excludeTest/new/a/b2/c/test.stp b/org.eclipse.jgit.test/tst-rsrc/org/eclipse/jgit/test/resources/excludeTest/new/a/b2/c/test.stp
deleted file mode 100644
index e69de29..0000000
--- a/org.eclipse.jgit.test/tst-rsrc/org/eclipse/jgit/test/resources/excludeTest/new/a/b2/c/test.stp
+++ /dev/null
diff --git a/org.eclipse.jgit.test/tst-rsrc/org/eclipse/jgit/test/resources/excludeTest/notignored b/org.eclipse.jgit.test/tst-rsrc/org/eclipse/jgit/test/resources/excludeTest/notignored
deleted file mode 100644
index e69de29..0000000
--- a/org.eclipse.jgit.test/tst-rsrc/org/eclipse/jgit/test/resources/excludeTest/notignored
+++ /dev/null
diff --git a/org.eclipse.jgit.test/tst-rsrc/org/eclipse/jgit/test/resources/excludeTest/test.stp b/org.eclipse.jgit.test/tst-rsrc/org/eclipse/jgit/test/resources/excludeTest/test.stp
deleted file mode 100644
index e69de29..0000000
--- a/org.eclipse.jgit.test/tst-rsrc/org/eclipse/jgit/test/resources/excludeTest/test.stp
+++ /dev/null
diff --git a/org.eclipse.jgit.test/tst/org/eclipse/jgit/api/AddCommandTest.java b/org.eclipse.jgit.test/tst/org/eclipse/jgit/api/AddCommandTest.java
index 5eec1eb..a7d0984 100644
--- a/org.eclipse.jgit.test/tst/org/eclipse/jgit/api/AddCommandTest.java
+++ b/org.eclipse.jgit.test/tst/org/eclipse/jgit/api/AddCommandTest.java
@@ -44,15 +44,17 @@
package org.eclipse.jgit.api;
import java.io.File;
+import java.io.FileInputStream;
import java.io.IOException;
import java.io.PrintWriter;
import org.eclipse.jgit.dircache.DirCache;
import org.eclipse.jgit.dircache.DirCacheBuilder;
import org.eclipse.jgit.dircache.DirCacheEntry;
+import org.eclipse.jgit.lib.Constants;
import org.eclipse.jgit.lib.FileMode;
import org.eclipse.jgit.lib.ObjectId;
-import org.eclipse.jgit.lib.ObjectWriter;
+import org.eclipse.jgit.lib.ObjectInserter;
import org.eclipse.jgit.lib.RepositoryTestCase;
public class AddCommandTest extends RepositoryTestCase {
@@ -78,7 +80,7 @@ public void testAddNonExistingSingleFile() throws NoFilepatternException {
}
public void testAddExistingSingleFile() throws IOException, NoFilepatternException {
- File file = new File(db.getWorkDir(), "a.txt");
+ File file = new File(db.getWorkTree(), "a.txt");
file.createNewFile();
PrintWriter writer = new PrintWriter(file);
writer.print("content");
@@ -86,20 +88,16 @@ public void testAddExistingSingleFile() throws IOException, NoFilepatternExcepti
Git git = new Git(db);
- DirCache dc = git.add().addFilepattern("a.txt").call();
+ git.add().addFilepattern("a.txt").call();
- assertEquals(1, dc.getEntryCount());
- assertEquals("a.txt", dc.getEntry(0).getPathString());
- assertNotNull(dc.getEntry(0).getObjectId());
- assertEquals(file.lastModified(), dc.getEntry(0).getLastModified());
- assertEquals(file.length(), dc.getEntry(0).getLength());
- assertEquals(FileMode.REGULAR_FILE, dc.getEntry(0).getFileMode());
- assertEquals(0, dc.getEntry(0).getStage());
+ assertEquals(
+ "[a.txt, mode:100644, sha1:6b584e8ece562ebffc15d38808cd6b98fc3d97ea]",
+ indexState(CONTENT_ID));
}
public void testAddExistingSingleFileInSubDir() throws IOException, NoFilepatternException {
- new File(db.getWorkDir(), "sub").mkdir();
- File file = new File(db.getWorkDir(), "sub/a.txt");
+ new File(db.getWorkTree(), "sub").mkdir();
+ File file = new File(db.getWorkTree(), "sub/a.txt");
file.createNewFile();
PrintWriter writer = new PrintWriter(file);
writer.print("content");
@@ -107,19 +105,15 @@ public void testAddExistingSingleFileInSubDir() throws IOException, NoFilepatter
Git git = new Git(db);
- DirCache dc = git.add().addFilepattern("sub/a.txt").call();
+ git.add().addFilepattern("sub/a.txt").call();
- assertEquals(1, dc.getEntryCount());
- assertEquals("sub/a.txt", dc.getEntry(0).getPathString());
- assertNotNull(dc.getEntry(0).getObjectId());
- assertEquals(file.lastModified(), dc.getEntry(0).getLastModified());
- assertEquals(file.length(), dc.getEntry(0).getLength());
- assertEquals(FileMode.REGULAR_FILE, dc.getEntry(0).getFileMode());
- assertEquals(0, dc.getEntry(0).getStage());
+ assertEquals(
+ "[sub/a.txt, mode:100644, sha1:6b584e8ece562ebffc15d38808cd6b98fc3d97ea]",
+ indexState(CONTENT_ID));
}
public void testAddExistingSingleFileTwice() throws IOException, NoFilepatternException {
- File file = new File(db.getWorkDir(), "a.txt");
+ File file = new File(db.getWorkTree(), "a.txt");
file.createNewFile();
PrintWriter writer = new PrintWriter(file);
writer.print("content");
@@ -128,7 +122,7 @@ public void testAddExistingSingleFileTwice() throws IOException, NoFilepatternEx
Git git = new Git(db);
DirCache dc = git.add().addFilepattern("a.txt").call();
- ObjectId id1 = dc.getEntry(0).getObjectId();
+ dc.getEntry(0).getObjectId();
writer = new PrintWriter(file);
writer.print("other content");
@@ -136,14 +130,13 @@ public void testAddExistingSingleFileTwice() throws IOException, NoFilepatternEx
dc = git.add().addFilepattern("a.txt").call();
- assertEquals(1, dc.getEntryCount());
- assertEquals("a.txt", dc.getEntry(0).getPathString());
- assertNotSame(id1, dc.getEntry(0).getObjectId());
- assertEquals(0, dc.getEntry(0).getStage());
+ assertEquals(
+ "[a.txt, mode:100644, sha1:4f41554f6e0045ef53848fc0c3f33b6a9abc24a9]",
+ indexState(CONTENT_ID));
}
public void testAddExistingSingleFileTwiceWithCommit() throws Exception {
- File file = new File(db.getWorkDir(), "a.txt");
+ File file = new File(db.getWorkTree(), "a.txt");
file.createNewFile();
PrintWriter writer = new PrintWriter(file);
writer.print("content");
@@ -152,7 +145,7 @@ public void testAddExistingSingleFileTwiceWithCommit() throws Exception {
Git git = new Git(db);
DirCache dc = git.add().addFilepattern("a.txt").call();
- ObjectId id1 = dc.getEntry(0).getObjectId();
+ dc.getEntry(0).getObjectId();
git.commit().setMessage("commit a.txt").call();
@@ -162,14 +155,13 @@ public void testAddExistingSingleFileTwiceWithCommit() throws Exception {
dc = git.add().addFilepattern("a.txt").call();
- assertEquals(1, dc.getEntryCount());
- assertEquals("a.txt", dc.getEntry(0).getPathString());
- assertNotSame(id1, dc.getEntry(0).getObjectId());
- assertEquals(0, dc.getEntry(0).getStage());
+ assertEquals(
+ "[a.txt, mode:100644, sha1:4f41554f6e0045ef53848fc0c3f33b6a9abc24a9]",
+ indexState(CONTENT_ID));
}
public void testAddRemovedFile() throws Exception {
- File file = new File(db.getWorkDir(), "a.txt");
+ File file = new File(db.getWorkTree(), "a.txt");
file.createNewFile();
PrintWriter writer = new PrintWriter(file);
writer.print("content");
@@ -178,20 +170,19 @@ public void testAddRemovedFile() throws Exception {
Git git = new Git(db);
DirCache dc = git.add().addFilepattern("a.txt").call();
- ObjectId id1 = dc.getEntry(0).getObjectId();
+ dc.getEntry(0).getObjectId();
file.delete();
// is supposed to do nothing
dc = git.add().addFilepattern("a.txt").call();
- assertEquals(1, dc.getEntryCount());
- assertEquals("a.txt", dc.getEntry(0).getPathString());
- assertEquals(id1, dc.getEntry(0).getObjectId());
- assertEquals(0, dc.getEntry(0).getStage());
+ assertEquals(
+ "[a.txt, mode:100644, sha1:6b584e8ece562ebffc15d38808cd6b98fc3d97ea]",
+ indexState(CONTENT_ID));
}
public void testAddRemovedCommittedFile() throws Exception {
- File file = new File(db.getWorkDir(), "a.txt");
+ File file = new File(db.getWorkTree(), "a.txt");
file.createNewFile();
PrintWriter writer = new PrintWriter(file);
writer.print("content");
@@ -202,118 +193,275 @@ public void testAddRemovedCommittedFile() throws Exception {
git.commit().setMessage("commit a.txt").call();
- ObjectId id1 = dc.getEntry(0).getObjectId();
+ dc.getEntry(0).getObjectId();
file.delete();
// is supposed to do nothing
dc = git.add().addFilepattern("a.txt").call();
- assertEquals(1, dc.getEntryCount());
- assertEquals("a.txt", dc.getEntry(0).getPathString());
- assertEquals(id1, dc.getEntry(0).getObjectId());
- assertEquals(0, dc.getEntry(0).getStage());
+ assertEquals(
+ "[a.txt, mode:100644, sha1:6b584e8ece562ebffc15d38808cd6b98fc3d97ea]",
+ indexState(CONTENT_ID));
}
public void testAddWithConflicts() throws Exception {
// prepare conflict
- File file = new File(db.getWorkDir(), "a.txt");
+ File file = new File(db.getWorkTree(), "a.txt");
file.createNewFile();
PrintWriter writer = new PrintWriter(file);
writer.print("content");
writer.close();
- File file2 = new File(db.getWorkDir(), "b.txt");
+ File file2 = new File(db.getWorkTree(), "b.txt");
file2.createNewFile();
writer = new PrintWriter(file2);
writer.print("content b");
writer.close();
- ObjectWriter ow = new ObjectWriter(db);
- DirCache dc = DirCache.lock(db);
+ ObjectInserter newObjectInserter = db.newObjectInserter();
+ DirCache dc = db.lockDirCache();
DirCacheBuilder builder = dc.builder();
- addEntryToBuilder("b.txt", file2, ow, builder, 0);
- addEntryToBuilder("a.txt", file, ow, builder, 1);
+ addEntryToBuilder("b.txt", file2, newObjectInserter, builder, 0);
+ addEntryToBuilder("a.txt", file, newObjectInserter, builder, 1);
writer = new PrintWriter(file);
writer.print("other content");
writer.close();
- addEntryToBuilder("a.txt", file, ow, builder, 3);
+ addEntryToBuilder("a.txt", file, newObjectInserter, builder, 3);
writer = new PrintWriter(file);
writer.print("our content");
writer.close();
- ObjectId id1 = addEntryToBuilder("a.txt", file, ow, builder, 2)
+ addEntryToBuilder("a.txt", file, newObjectInserter, builder, 2)
.getObjectId();
builder.commit();
- assertEquals(4, dc.getEntryCount());
+ assertEquals(
+ "[a.txt, mode:100644, stage:1, sha1:6b584e8ece562ebffc15d38808cd6b98fc3d97ea]" +
+ "[a.txt, mode:100644, stage:2, sha1:b9f89ff733bdaf49e02711535867bb821f9db55e]" +
+ "[a.txt, mode:100644, stage:3, sha1:4f41554f6e0045ef53848fc0c3f33b6a9abc24a9]" +
+ "[b.txt, mode:100644, sha1:50e9cdb03f9719261dd39d7f2920b906db3711a3]",
+ indexState(CONTENT_ID));
// now the test begins
Git git = new Git(db);
dc = git.add().addFilepattern("a.txt").call();
- assertEquals(2, dc.getEntryCount());
- assertEquals("a.txt", dc.getEntry("a.txt").getPathString());
- assertEquals(id1, dc.getEntry("a.txt").getObjectId());
- assertEquals(0, dc.getEntry("a.txt").getStage());
- assertEquals(0, dc.getEntry("b.txt").getStage());
+ assertEquals(
+ "[a.txt, mode:100644, sha1:b9f89ff733bdaf49e02711535867bb821f9db55e]" +
+ "[b.txt, mode:100644, sha1:50e9cdb03f9719261dd39d7f2920b906db3711a3]",
+ indexState(CONTENT_ID));
}
public void testAddTwoFiles() throws Exception {
- File file = new File(db.getWorkDir(), "a.txt");
+ File file = new File(db.getWorkTree(), "a.txt");
file.createNewFile();
PrintWriter writer = new PrintWriter(file);
writer.print("content");
writer.close();
- File file2 = new File(db.getWorkDir(), "b.txt");
+ File file2 = new File(db.getWorkTree(), "b.txt");
file2.createNewFile();
writer = new PrintWriter(file2);
writer.print("content b");
writer.close();
Git git = new Git(db);
- DirCache dc = git.add().addFilepattern("a.txt").addFilepattern("b.txt").call();
- assertEquals("a.txt", dc.getEntry("a.txt").getPathString());
- assertEquals("b.txt", dc.getEntry("b.txt").getPathString());
- assertNotNull(dc.getEntry("a.txt").getObjectId());
- assertNotNull(dc.getEntry("b.txt").getObjectId());
- assertEquals(0, dc.getEntry("a.txt").getStage());
- assertEquals(0, dc.getEntry("b.txt").getStage());
+ git.add().addFilepattern("a.txt").addFilepattern("b.txt").call();
+ assertEquals(
+ "[a.txt, mode:100644, sha1:6b584e8ece562ebffc15d38808cd6b98fc3d97ea]" +
+ "[b.txt, mode:100644, sha1:50e9cdb03f9719261dd39d7f2920b906db3711a3]",
+ indexState(CONTENT_ID));
}
public void testAddFolder() throws Exception {
- new File(db.getWorkDir(), "sub").mkdir();
- File file = new File(db.getWorkDir(), "sub/a.txt");
+ new File(db.getWorkTree(), "sub").mkdir();
+ File file = new File(db.getWorkTree(), "sub/a.txt");
file.createNewFile();
PrintWriter writer = new PrintWriter(file);
writer.print("content");
writer.close();
- File file2 = new File(db.getWorkDir(), "sub/b.txt");
+ File file2 = new File(db.getWorkTree(), "sub/b.txt");
file2.createNewFile();
writer = new PrintWriter(file2);
writer.print("content b");
writer.close();
Git git = new Git(db);
- DirCache dc = git.add().addFilepattern("sub").call();
- assertEquals("sub/a.txt", dc.getEntry("sub/a.txt").getPathString());
- assertEquals("sub/b.txt", dc.getEntry("sub/b.txt").getPathString());
- assertNotNull(dc.getEntry("sub/a.txt").getObjectId());
- assertNotNull(dc.getEntry("sub/b.txt").getObjectId());
- assertEquals(0, dc.getEntry("sub/a.txt").getStage());
- assertEquals(0, dc.getEntry("sub/b.txt").getStage());
+ git.add().addFilepattern("sub").call();
+ assertEquals(
+ "[sub/a.txt, mode:100644, sha1:6b584e8ece562ebffc15d38808cd6b98fc3d97ea]" +
+ "[sub/b.txt, mode:100644, sha1:50e9cdb03f9719261dd39d7f2920b906db3711a3]",
+ indexState(CONTENT_ID));
+ }
+
+ public void testAddIgnoredFile() throws Exception {
+ new File(db.getWorkTree(), "sub").mkdir();
+ File file = new File(db.getWorkTree(), "sub/a.txt");
+ file.createNewFile();
+ PrintWriter writer = new PrintWriter(file);
+ writer.print("content");
+ writer.close();
+
+ File ignoreFile = new File(db.getWorkTree(), ".gitignore");
+ ignoreFile.createNewFile();
+ writer = new PrintWriter(ignoreFile);
+ writer.print("sub/b.txt");
+ writer.close();
+
+ File file2 = new File(db.getWorkTree(), "sub/b.txt");
+ file2.createNewFile();
+ writer = new PrintWriter(file2);
+ writer.print("content b");
+ writer.close();
+
+ Git git = new Git(db);
+ git.add().addFilepattern("sub").call();
+
+ assertEquals(
+ "[sub/a.txt, mode:100644, sha1:6b584e8ece562ebffc15d38808cd6b98fc3d97ea]",
+ indexState(CONTENT_ID));
+ }
+
+ public void testAddWholeRepo() throws Exception {
+ new File(db.getWorkTree(), "sub").mkdir();
+ File file = new File(db.getWorkTree(), "sub/a.txt");
+ file.createNewFile();
+ PrintWriter writer = new PrintWriter(file);
+ writer.print("content");
+ writer.close();
+
+ File file2 = new File(db.getWorkTree(), "sub/b.txt");
+ file2.createNewFile();
+ writer = new PrintWriter(file2);
+ writer.print("content b");
+ writer.close();
+
+ Git git = new Git(db);
+ git.add().addFilepattern(".").call();
+ assertEquals(
+ "[sub/a.txt, mode:100644, sha1:6b584e8ece562ebffc15d38808cd6b98fc3d97ea]" +
+ "[sub/b.txt, mode:100644, sha1:50e9cdb03f9719261dd39d7f2920b906db3711a3]",
+ indexState(CONTENT_ID));
+ }
+
+ // the same three cases as in testAddWithParameterUpdate
+ // file a exists in workdir and in index -> added
+ // file b exists not in workdir but in index -> unchanged
+ // file c exists in workdir but not in index -> added
+ public void testAddWithoutParameterUpdate() throws Exception {
+ new File(db.getWorkTree(), "sub").mkdir();
+ File file = new File(db.getWorkTree(), "sub/a.txt");
+ file.createNewFile();
+ PrintWriter writer = new PrintWriter(file);
+ writer.print("content");
+ writer.close();
+
+ File file2 = new File(db.getWorkTree(), "sub/b.txt");
+ file2.createNewFile();
+ writer = new PrintWriter(file2);
+ writer.print("content b");
+ writer.close();
+
+ Git git = new Git(db);
+ git.add().addFilepattern("sub").call();
+
+ assertEquals(
+ "[sub/a.txt, mode:100644, sha1:6b584e8ece562ebffc15d38808cd6b98fc3d97ea]" +
+ "[sub/b.txt, mode:100644, sha1:50e9cdb03f9719261dd39d7f2920b906db3711a3]",
+ indexState(CONTENT_ID));
+
+ git.commit().setMessage("commit").call();
+
+ // new unstaged file sub/c.txt
+ File file3 = new File(db.getWorkTree(), "sub/c.txt");
+ file3.createNewFile();
+ writer = new PrintWriter(file3);
+ writer.print("content c");
+ writer.close();
+
+ // file sub/a.txt is modified
+ writer = new PrintWriter(file);
+ writer.print("modified content");
+ writer.close();
+
+ // file sub/b.txt is deleted
+ file2.delete();
+
+ git.add().addFilepattern("sub").call();
+ // change in sub/a.txt is staged
+ // deletion of sub/b.txt is not staged
+ // sub/c.txt is staged
+ assertEquals(
+ "[sub/a.txt, mode:100644, sha1:268af4e306cfcf6e79edd50fed9c553d211f68e3]" +
+ "[sub/b.txt, mode:100644, sha1:50e9cdb03f9719261dd39d7f2920b906db3711a3]" +
+ "[sub/c.txt, mode:100644, sha1:fa08654474ae2ddc4f61ee3a43d017ba65a439c3]",
+ indexState(CONTENT_ID));
+ }
+
+ // file a exists in workdir and in index -> added
+ // file b exists not in workdir but in index -> deleted
+ // file c exists in workdir but not in index -> unchanged
+ public void testAddWithParameterUpdate() throws Exception {
+ new File(db.getWorkTree(), "sub").mkdir();
+ File file = new File(db.getWorkTree(), "sub/a.txt");
+ file.createNewFile();
+ PrintWriter writer = new PrintWriter(file);
+ writer.print("content");
+ writer.close();
+
+ File file2 = new File(db.getWorkTree(), "sub/b.txt");
+ file2.createNewFile();
+ writer = new PrintWriter(file2);
+ writer.print("content b");
+ writer.close();
+
+ Git git = new Git(db);
+ git.add().addFilepattern("sub").call();
+
+ assertEquals(
+ "[sub/a.txt, mode:100644, sha1:6b584e8ece562ebffc15d38808cd6b98fc3d97ea]" +
+ "[sub/b.txt, mode:100644, sha1:50e9cdb03f9719261dd39d7f2920b906db3711a3]",
+ indexState(CONTENT_ID));
+
+ git.commit().setMessage("commit").call();
+
+ // new unstaged file sub/c.txt
+ File file3 = new File(db.getWorkTree(), "sub/c.txt");
+ file3.createNewFile();
+ writer = new PrintWriter(file3);
+ writer.print("content c");
+ writer.close();
+
+ // file sub/a.txt is modified
+ writer = new PrintWriter(file);
+ writer.print("modified content");
+ writer.close();
+
+ file2.delete();
+
+ // change in sub/a.txt is staged
+ // deletion of sub/b.txt is staged
+ // sub/c.txt is not staged
+ git.add().addFilepattern("sub").setUpdate(true).call();
+ // change in sub/a.txt is staged
+ assertEquals(
+ "[sub/a.txt, mode:100644, sha1:268af4e306cfcf6e79edd50fed9c553d211f68e3]",
+ indexState(CONTENT_ID));
}
private DirCacheEntry addEntryToBuilder(String path, File file,
- ObjectWriter ow, DirCacheBuilder builder, int stage)
+ ObjectInserter newObjectInserter, DirCacheBuilder builder, int stage)
throws IOException {
- ObjectId id = ow.writeBlob(file);
+ FileInputStream inputStream = new FileInputStream(file);
+ ObjectId id = newObjectInserter.insert(
+ Constants.OBJ_BLOB, file.length(), inputStream);
+ inputStream.close();
DirCacheEntry entry = new DirCacheEntry(path, stage);
entry.setObjectId(id);
entry.setFileMode(FileMode.REGULAR_FILE);
diff --git a/org.eclipse.jgit.test/tst/org/eclipse/jgit/api/CommitAndLogCommandTests.java b/org.eclipse.jgit.test/tst/org/eclipse/jgit/api/CommitAndLogCommandTests.java
index a62045d..cf30039 100644
--- a/org.eclipse.jgit.test/tst/org/eclipse/jgit/api/CommitAndLogCommandTests.java
+++ b/org.eclipse.jgit.test/tst/org/eclipse/jgit/api/CommitAndLogCommandTests.java
@@ -45,6 +45,7 @@
import java.io.File;
import java.io.FileWriter;
import java.io.IOException;
+import java.io.PrintWriter;
import org.eclipse.jgit.errors.UnmergedPathException;
import org.eclipse.jgit.lib.Constants;
@@ -53,6 +54,7 @@
import org.eclipse.jgit.lib.RefUpdate;
import org.eclipse.jgit.lib.RepositoryTestCase;
import org.eclipse.jgit.revwalk.RevCommit;
+import org.eclipse.jgit.treewalk.TreeWalk;
public class CommitAndLogCommandTests extends RepositoryTestCase {
public void testSomeCommits() throws NoHeadException, NoMessageException,
@@ -151,4 +153,36 @@ public void testMergeEmptyBranches() throws IOException, NoHeadException,
assertEquals(parents[1], second);
assertTrue(parents.length==2);
}
+
+ public void testAddUnstagedChanges() throws IOException, NoHeadException,
+ NoMessageException, ConcurrentRefUpdateException,
+ JGitInternalException, WrongRepositoryStateException,
+ NoFilepatternException {
+ File file = new File(db.getWorkTree(), "a.txt");
+ file.createNewFile();
+ PrintWriter writer = new PrintWriter(file);
+ writer.print("content");
+ writer.close();
+
+ Git git = new Git(db);
+ git.add().addFilepattern("a.txt").call();
+ RevCommit commit = git.commit().setMessage("initial commit").call();
+ TreeWalk tw = TreeWalk.forPath(db, "a.txt", commit.getTree());
+ assertEquals("6b584e8ece562ebffc15d38808cd6b98fc3d97ea",
+ tw.getObjectId(0).getName());
+
+ writer = new PrintWriter(file);
+ writer.print("content2");
+ writer.close();
+ commit = git.commit().setMessage("second commit").call();
+ tw = TreeWalk.forPath(db, "a.txt", commit.getTree());
+ assertEquals("6b584e8ece562ebffc15d38808cd6b98fc3d97ea",
+ tw.getObjectId(0).getName());
+
+ commit = git.commit().setAll(true).setMessage("third commit")
+ .setAll(true).call();
+ tw = TreeWalk.forPath(db, "a.txt", commit.getTree());
+ assertEquals("db00fd65b218578127ea51f3dffac701f12f486a",
+ tw.getObjectId(0).getName());
+ }
}
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 c965c67..773d2f0 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 void testFastForwardWithFiles() throws Exception {
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 void testMultipleHeads() throws Exception {
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 @@ private void createBranch(ObjectId objectId, String branchName) throws IOExcepti
}
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 @@ private void addNewFileToIndex(String filename) throws IOException,
File writeTrashFile = writeTrashFile(filename, filename);
GitIndex index = db.getIndex();
- Entry entry = index.add(db.getWorkDir(), writeTrashFile);
+ Entry entry = index.add(db.getWorkTree(), writeTrashFile);
entry.update(writeTrashFile);
index.write();
}
diff --git a/org.eclipse.jgit.test/tst/org/eclipse/jgit/diff/MyersDiffPerformanceTest.java b/org.eclipse.jgit.test/tst/org/eclipse/jgit/diff/MyersDiffPerformanceTest.java
index fe63e3d..37a9a4d 100644
--- a/org.eclipse.jgit.test/tst/org/eclipse/jgit/diff/MyersDiffPerformanceTest.java
+++ b/org.eclipse.jgit.test/tst/org/eclipse/jgit/diff/MyersDiffPerformanceTest.java
@@ -60,7 +60,7 @@
* time for computing the diff between a and b should depend on the product of
* a.length+b.length and the number of found differences. The tests compute
* diffs between chunks of different length, measure the needed time and check
- * that time/(N*D) does not differ more than a certain factor (currently 10)
+ * that time/(N*D) does not differ more than a certain factor.
*/
public class MyersDiffPerformanceTest extends TestCase {
private static final long longTaskBoundary = 5000000000L;
diff --git a/org.eclipse.jgit.test/tst/org/eclipse/jgit/diff/RenameDetectorTest.java b/org.eclipse.jgit.test/tst/org/eclipse/jgit/diff/RenameDetectorTest.java
index b5d94c0..862fc0a 100644
--- a/org.eclipse.jgit.test/tst/org/eclipse/jgit/diff/RenameDetectorTest.java
+++ b/org.eclipse.jgit.test/tst/org/eclipse/jgit/diff/RenameDetectorTest.java
@@ -401,6 +401,116 @@ public void testNoRenames_SymlinkAndFileSamePath() throws Exception {
assertSame(b, entries.get(1));
}
+ public void testBreakModify_BreakAll() throws Exception {
+ ObjectId aId = blob("foo");
+ ObjectId bId = blob("bar");
+
+ DiffEntry m = DiffEntry.modify(PATH_A);
+ m.oldId = AbbreviatedObjectId.fromObjectId(aId);
+ m.newId = AbbreviatedObjectId.fromObjectId(bId);
+
+ DiffEntry a = DiffEntry.add(PATH_B, aId);
+
+ rd.add(a);
+ rd.add(m);
+
+ rd.setBreakScore(101);
+
+ List<DiffEntry> entries = rd.compute();
+ assertEquals(2, entries.size());
+ assertAdd(PATH_A, bId, FileMode.REGULAR_FILE, entries.get(0));
+ assertRename(DiffEntry.breakModify(m).get(0), a, 100, entries.get(1));
+ }
+
+ public void testBreakModify_BreakNone() throws Exception {
+ ObjectId aId = blob("foo");
+ ObjectId bId = blob("bar");
+
+ DiffEntry m = DiffEntry.modify(PATH_A);
+ m.oldId = AbbreviatedObjectId.fromObjectId(aId);
+ m.newId = AbbreviatedObjectId.fromObjectId(bId);
+
+ DiffEntry a = DiffEntry.add(PATH_B, aId);
+
+ rd.add(a);
+ rd.add(m);
+
+ rd.setBreakScore(-1);
+
+ List<DiffEntry> entries = rd.compute();
+ assertEquals(2, entries.size());
+ assertSame(m, entries.get(0));
+ assertSame(a, entries.get(1));
+ }
+
+ public void testBreakModify_BreakBelowScore() throws Exception {
+ ObjectId aId = blob("foo");
+ ObjectId bId = blob("bar");
+
+ DiffEntry m = DiffEntry.modify(PATH_A);
+ m.oldId = AbbreviatedObjectId.fromObjectId(aId);
+ m.newId = AbbreviatedObjectId.fromObjectId(bId);
+
+ DiffEntry a = DiffEntry.add(PATH_B, aId);
+
+ rd.add(a);
+ rd.add(m);
+
+ rd.setBreakScore(20); // Should break the modify
+
+ List<DiffEntry> entries = rd.compute();
+ assertEquals(2, entries.size());
+ assertAdd(PATH_A, bId, FileMode.REGULAR_FILE, entries.get(0));
+ assertRename(DiffEntry.breakModify(m).get(0), a, 100, entries.get(1));
+ }
+
+ public void testBreakModify_DontBreakAboveScore() throws Exception {
+ ObjectId aId = blob("blah\nblah\nfoo");
+ ObjectId bId = blob("blah\nblah\nbar");
+
+ DiffEntry m = DiffEntry.modify(PATH_A);
+ m.oldId = AbbreviatedObjectId.fromObjectId(aId);
+ m.newId = AbbreviatedObjectId.fromObjectId(bId);
+
+ DiffEntry a = DiffEntry.add(PATH_B, aId);
+
+ rd.add(a);
+ rd.add(m);
+
+ rd.setBreakScore(20); // Should not break the modify
+
+ List<DiffEntry> entries = rd.compute();
+ assertEquals(2, entries.size());
+ assertSame(m, entries.get(0));
+ assertSame(a, entries.get(1));
+ }
+
+ public void testBreakModify_RejoinIfUnpaired() throws Exception {
+ ObjectId aId = blob("foo");
+ ObjectId bId = blob("bar");
+
+ DiffEntry m = DiffEntry.modify(PATH_A);
+ m.oldId = AbbreviatedObjectId.fromObjectId(aId);
+ m.newId = AbbreviatedObjectId.fromObjectId(bId);
+
+ rd.add(m);
+
+ rd.setBreakScore(101); // Ensure m is broken apart
+
+ List<DiffEntry> entries = rd.compute();
+ assertEquals(1, entries.size());
+
+ DiffEntry modify = entries.get(0);
+ assertEquals(m.oldPath, modify.oldPath);
+ assertEquals(m.oldId, modify.oldId);
+ assertEquals(m.oldMode, modify.oldMode);
+ assertEquals(m.newPath, modify.newPath);
+ assertEquals(m.newId, modify.newId);
+ assertEquals(m.newMode, modify.newMode);
+ assertEquals(m.changeType, modify.changeType);
+ assertEquals(0, modify.score);
+ }
+
public void testSetRenameScore_IllegalArgs() throws Exception {
try {
rd.setRenameScore(-1);
@@ -453,8 +563,8 @@ private static void assertRename(DiffEntry o, DiffEntry n, int score,
DiffEntry rename) {
assertEquals(ChangeType.RENAME, rename.getChangeType());
- assertEquals(o.getOldName(), rename.getOldName());
- assertEquals(n.getNewName(), rename.getNewName());
+ assertEquals(o.getOldPath(), rename.getOldPath());
+ assertEquals(n.getNewPath(), rename.getNewPath());
assertEquals(o.getOldMode(), rename.getOldMode());
assertEquals(n.getNewMode(), rename.getNewMode());
@@ -469,8 +579,8 @@ private static void assertCopy(DiffEntry o, DiffEntry n, int score,
DiffEntry copy) {
assertEquals(ChangeType.COPY, copy.getChangeType());
- assertEquals(o.getOldName(), copy.getOldName());
- assertEquals(n.getNewName(), copy.getNewName());
+ assertEquals(o.getOldPath(), copy.getOldPath());
+ assertEquals(n.getNewPath(), copy.getNewPath());
assertEquals(o.getOldMode(), copy.getOldMode());
assertEquals(n.getNewMode(), copy.getNewMode());
@@ -480,4 +590,15 @@ private static void assertCopy(DiffEntry o, DiffEntry n, int score,
assertEquals(score, copy.getScore());
}
+
+ private static void assertAdd(String newName, ObjectId newId,
+ FileMode newMode, DiffEntry add) {
+ assertEquals(DiffEntry.DEV_NULL, add.oldPath);
+ assertEquals(DiffEntry.A_ZERO, add.oldId);
+ assertEquals(FileMode.MISSING, add.oldMode);
+ assertEquals(ChangeType.ADD, add.changeType);
+ assertEquals(newName, add.newPath);
+ assertEquals(AbbreviatedObjectId.fromObjectId(newId), add.newId);
+ assertEquals(newMode, add.newMode);
+ }
}
diff --git a/org.eclipse.jgit.test/tst/org/eclipse/jgit/diff/SimilarityIndexTest.java b/org.eclipse.jgit.test/tst/org/eclipse/jgit/diff/SimilarityIndexTest.java
index d6915eb..7e42e53 100644
--- a/org.eclipse.jgit.test/tst/org/eclipse/jgit/diff/SimilarityIndexTest.java
+++ b/org.eclipse.jgit.test/tst/org/eclipse/jgit/diff/SimilarityIndexTest.java
@@ -43,12 +43,15 @@
package org.eclipse.jgit.diff;
+import java.io.ByteArrayInputStream;
+import java.io.IOException;
+
import junit.framework.TestCase;
import org.eclipse.jgit.lib.Constants;
public class SimilarityIndexTest extends TestCase {
- public void testIndexing() {
+ public void testIndexingSmallObject() {
SimilarityIndex si = hash("" //
+ "A\n" //
+ "B\n" //
@@ -67,6 +70,17 @@ public void testIndexing() {
assertEquals(2, si.count(si.findIndex(key_D)));
}
+ public void testIndexingLargeObject() throws IOException {
+ byte[] in = ("" //
+ + "A\n" //
+ + "B\n" //
+ + "B\n" //
+ + "B\n").getBytes("UTF-8");
+ SimilarityIndex si = new SimilarityIndex();
+ si.hash(new ByteArrayInputStream(in), in.length);
+ assertEquals(2, si.size());
+ }
+
public void testCommonScore_SameFiles() {
String text = "" //
+ "A\n" //
diff --git a/org.eclipse.jgit.test/tst/org/eclipse/jgit/dircache/DirCacheBasicTest.java b/org.eclipse.jgit.test/tst/org/eclipse/jgit/dircache/DirCacheBasicTest.java
index f4692b1..b6e4e42 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 void testReadMissing_RealIndex() throws Exception {
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());
}
@@ -63,7 +63,7 @@ public void testReadMissing_TempIndex() throws Exception {
final File idx = new File(db.getDirectory(), "tmp_index");
assertFalse(idx.exists());
- final DirCache dc = DirCache.read(idx);
+ final DirCache dc = DirCache.read(idx, db.getFS());
assertNotNull(dc);
assertEquals(0, dc.getEntryCount());
}
@@ -74,7 +74,7 @@ public void testLockMissing_RealIndex() throws Exception {
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());
@@ -91,7 +91,7 @@ public void testLockMissing_TempIndex() throws Exception {
assertFalse(idx.exists());
assertFalse(lck.exists());
- final DirCache dc = DirCache.lock(idx);
+ final DirCache dc = DirCache.lock(idx, db.getFS());
assertNotNull(dc);
assertFalse(idx.exists());
assertTrue(lck.exists());
@@ -108,7 +108,7 @@ public void testWriteEmptyUnlock_RealIndex() throws Exception {
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 void testWriteEmptyCommit_RealIndex() throws Exception {
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 void testWriteEmptyReadEmpty_RealIndex() throws Exception {
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 void testWriteEmptyLockEmpty_RealIndex() throws Exception {
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 void testWriteEmptyLockEmpty_RealIndex() throws Exception {
}
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 void testBuildThenClear() throws Exception {
}
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 03bb7f5..a09f8e8 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 @@
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 e919e41..81ffab9 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 @@
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 void testBuildEmpty() throws Exception {
assertTrue(dc.commit());
}
{
- final DirCache dc = DirCache.read(db);
+ final DirCache dc = db.readDirCache();
assertEquals(0, dc.getEntryCount());
}
}
@@ -86,7 +86,7 @@ public void testBuildOneFile_FinishWriteCommit() throws Exception {
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 void testBuildOneFile_FinishWriteCommit() throws Exception {
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 void testBuildOneFile_Commit() throws Exception {
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 void testBuildOneFile_Commit() throws Exception {
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 void testBuildOneFile_Commit() throws Exception {
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 void testFindSingleFile() throws Exception {
}
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 void testAdd_InGitSortOrder() throws Exception {
}
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 void testAdd_ReverseGitSortOrder() throws Exception {
}
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/DirCacheCGitCompatabilityTest.java b/org.eclipse.jgit.test/tst/org/eclipse/jgit/dircache/DirCacheCGitCompatabilityTest.java
index fa5fea8..f37d040 100644
--- a/org.eclipse.jgit.test/tst/org/eclipse/jgit/dircache/DirCacheCGitCompatabilityTest.java
+++ b/org.eclipse.jgit.test/tst/org/eclipse/jgit/dircache/DirCacheCGitCompatabilityTest.java
@@ -58,6 +58,7 @@
import org.eclipse.jgit.lib.ObjectId;
import org.eclipse.jgit.lib.Repository;
import org.eclipse.jgit.treewalk.TreeWalk;
+import org.eclipse.jgit.util.FS;
import org.eclipse.jgit.util.JGitTestUtil;
public class DirCacheCGitCompatabilityTest extends LocalDiskRepositoryTestCase {
@@ -65,7 +66,7 @@ public class DirCacheCGitCompatabilityTest extends LocalDiskRepositoryTestCase {
public void testReadIndex_LsFiles() throws Exception {
final Map<String, CGitIndexRecord> ls = readLsFiles();
- final DirCache dc = new DirCache(index);
+ final DirCache dc = new DirCache(index, FS.DETECTED);
assertEquals(0, dc.getEntryCount());
dc.read();
assertEquals(ls.size(), dc.getEntryCount());
@@ -79,7 +80,7 @@ public void testReadIndex_LsFiles() throws Exception {
public void testTreeWalk_LsFiles() throws Exception {
final Repository db = createBareRepository();
final Map<String, CGitIndexRecord> ls = readLsFiles();
- final DirCache dc = new DirCache(index);
+ final DirCache dc = new DirCache(index, db.getFS());
assertEquals(0, dc.getEntryCount());
dc.read();
assertEquals(ls.size(), dc.getEntryCount());
@@ -102,14 +103,16 @@ public void testTreeWalk_LsFiles() throws Exception {
}
public void testUnsupportedOptionalExtension() throws Exception {
- final DirCache dc = new DirCache(pathOf("gitgit.index.ZZZZ"));
+ final DirCache dc = new DirCache(pathOf("gitgit.index.ZZZZ"),
+ FS.DETECTED);
dc.read();
assertEquals(1, dc.getEntryCount());
assertEquals("A", dc.getEntry(0).getPathString());
}
public void testUnsupportedRequiredExtension() throws Exception {
- final DirCache dc = new DirCache(pathOf("gitgit.index.aaaa"));
+ final DirCache dc = new DirCache(pathOf("gitgit.index.aaaa"),
+ FS.DETECTED);
try {
dc.read();
fail("Cache loaded an unsupported extension");
@@ -120,7 +123,8 @@ public void testUnsupportedRequiredExtension() throws Exception {
}
public void testCorruptChecksumAtFooter() throws Exception {
- final DirCache dc = new DirCache(pathOf("gitgit.index.badchecksum"));
+ final DirCache dc = new DirCache(pathOf("gitgit.index.badchecksum"),
+ FS.DETECTED);
try {
dc.read();
fail("Cache loaded despite corrupt checksum");
@@ -143,7 +147,7 @@ private static void assertEqual(final CGitIndexRecord c,
public void testReadIndex_DirCacheTree() throws Exception {
final Map<String, CGitIndexRecord> cList = readLsFiles();
final Map<String, CGitLsTreeRecord> cTree = readLsTree();
- final DirCache dc = new DirCache(index);
+ final DirCache dc = new DirCache(index, FS.DETECTED);
assertEquals(0, dc.getEntryCount());
dc.read();
assertEquals(cList.size(), dc.getEntryCount());
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 d5a632c..5533fe3 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 @@
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 efea117..24e3c34 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 @@
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 void testEmptyTree_NoTreeWalk() throws Exception {
}
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 void testEmptyTree_WithTreeWalk() throws Exception {
}
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 void testNoSubtree_NoTreeWalk() throws Exception {
}
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 void testNoSubtree_WithTreeWalk() throws Exception {
}
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 void testSingleSubtree_NoRecursion() throws Exception {
}
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 void testSingleSubtree_Recursive() throws Exception {
}
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 void testTwoLevelSubtree_Recursive() throws Exception {
}
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 0926ab9..a6d7e39 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 @@ private void testLongPath(final int len) throws CorruptObjectException,
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 @@ private void testLongPath(final int len) throws CorruptObjectException,
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 8345c5d..dfca2fb 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 @@
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 void testEmptyCache_CreateEmptyCacheTree() throws Exception {
}
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 void testEmptyCache_Clear_NoCacheTree() throws Exception {
}
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 void testSingleSubtree() throws Exception {
}
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 void testTwoLevelSubtree() throws Exception {
* @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 void testWriteReadTree() throws CorruptObjectException, IOException {
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/ignore/IgnoreCacheTest.java b/org.eclipse.jgit.test/tst/org/eclipse/jgit/ignore/IgnoreCacheTest.java
deleted file mode 100644
index a9c1c31..0000000
--- a/org.eclipse.jgit.test/tst/org/eclipse/jgit/ignore/IgnoreCacheTest.java
+++ /dev/null
@@ -1,403 +0,0 @@
-/*
- * Copyright (C) 2010, Red Hat 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.ignore;
-
-import java.io.BufferedWriter;
-import java.io.File;
-import java.io.FileWriter;
-import java.io.IOException;
-import java.util.ArrayList;
-
-import org.eclipse.jgit.lib.RepositoryTestCase;
-import org.eclipse.jgit.util.JGitTestUtil;
-
-/**
- * Tests for the ignore cache
- */
-public class IgnoreCacheTest extends RepositoryTestCase {
-
- private File ignoreTestDir = JGitTestUtil.getTestResourceFile("excludeTest");
- private SimpleIgnoreCache cache;
- private final ArrayList<File> toDelete = new ArrayList<File>();
-
- //TODO: Do not use OS dependent strings to encode file paths
-
- public void tearDown() throws Exception {
- super.tearDown();
- deleteIgnoreFiles();
- cache.clear();
- toDelete.clear();
- }
-
- public void setUp() throws Exception {
- super.setUp();
- ignoreTestDir = JGitTestUtil.getTestResourceFile("excludeTest");
- assertTrue("Test resource directory is not a directory",ignoreTestDir.isDirectory());
-
- db = createWorkRepository();
- recursiveCopy(ignoreTestDir, db.getDirectory().getParentFile());
- cache = new SimpleIgnoreCache(db);
- initCache();
- }
-
- protected void recursiveCopy(File src, File parent) throws IOException {
- for (File file : src.listFiles()) {
- String rel = file.getName();
- File dst = new File(parent.toURI().resolve(rel));
- copyFileOrDirectory(file, dst);
- if (file.isDirectory())
- recursiveCopy(file, dst);
- }
- }
-
- protected static void copyFileOrDirectory(File src, File dst) throws IOException {
- if (src.isDirectory())
- dst.mkdir();
- else
- copyFile(src, dst);
- }
-
- public void testInitialization() {
- File test = new File(db.getDirectory().getParentFile() + "/new/a/b1/test.stp");
- assertTrue("Missing file " + test.getAbsolutePath(), test.exists());
-
- /*
- * Every folder along the path has a .gitignore file. Therefore every
- * folder should have been added and initialized
- */
- boolean result = isIgnored(getRelativePath(test));
- assertFalse("Unexpected match for " + test.toString(), result);
-
- /*
- * Check that every .gitignore along the path has been initialized
- */
- File folder = test.getParentFile();
- IgnoreNode rules = null;
- String fp = folder.getAbsolutePath();
- while (!folder.equals(db.getDirectory().getParentFile()) && fp.length() > 0) {
- rules = cache.getRules(getRelativePath(folder));
- assertNotNull("Ignore file not initialized for " + fp, rules);
- if (getRelativePath(folder).endsWith("new/a"))
- //The /new/a directory has an empty ignore file
- assertEquals("Ignore file not initialized for " + fp, 0, rules.getRules().size());
- else
- assertEquals("Ignore file not initialized for " + fp, 1, rules.getRules().size());
-
- folder = folder.getParentFile();
- fp = folder.getAbsolutePath();
- }
- if (rules != null)
- assertEquals(1, rules.getRules().size());
- else
- fail("Base directory not initialized");
-
- }
-
- public void testRules() {
- ignoreTestDir = JGitTestUtil.getTestResourceFile("excludeTest");
- assertTrue("Test resource directory is not a directory", ignoreTestDir.isDirectory());
- createExcludeFile();
- initCache();
-
- File test = new File(db.getDirectory().getParentFile(), "test.stp");
- String path = test.getAbsolutePath();
- assertTrue("Could not find test file " + path, test.exists());
-
- IgnoreNode baseRules = cache.getRules("");
- assertNotNull("Could not find base rules", baseRules);
-
- /*
- * .git/info/excludes:
- * /test.stp
- * /notignored
- *
- * new/.gitignore:
- * notarealfile
- *
- * new/a/.gitignore:
- * <empty>
- *
- * new/a/b2/.gitignore:
- * <does not exist>
- *
- * new/a/b1/.gitignore:
- * /c
- *
- * new/a/b1/c/.gitignore:
- * !/shouldbeignored.txt
- *
- * .gitignore:
- * !/notignored
- * /commentNotIgnored.tx#t
- * /commentIgnored.txt#comment
- * /commentIgnored.txt #comment
- */
- boolean result = isIgnored(getRelativePath(test));
- assertEquals(3, baseRules.getRules().size());
- assertTrue(db.getDirectory().getParentFile().toURI().equals(baseRules.getBaseDir().toURI()));
- //Test basic exclude file
- assertTrue("Did not match file " + test.toString(), result);
- //Test exclude file priority
- assertNotIgnored("notignored");
- //Test that /src/test.stp is not matched by /test.stp in exclude file (Do not reinitialize)
- assertNotIgnored("/src/test.stp");
- //Test file that is not mentioned -- should just return unmatched
- assertNotIgnored("not/mentioned/file.txt");
-
- //Test adding nonexistent node
- test = new File(db.getDirectory().getParentFile(), "new/a/b2/d/test.stp");
- assertNotIgnored("new/a/b2/d/test.stp");
- assertNotIgnored("new/a/b2/d/");
- assertNotIgnored("new/a/b2/d");
-
- //Test folder
- test = new File(db.getDirectory().getParentFile(), "new/a/b1/c");
- assertIgnored("new/a/b1/c");
- assertIgnored("new/a/b1/c/anything.c");
- assertIgnored("new/a/b1/c/and.o");
- assertIgnored("new/a/b1/c/everything.d");
- assertIgnored("new/a/b1/c/everything.d");
- //Special case -- the normally higher priority negation in c/.gitignore is cancelled by the folder being ignored
- assertIgnored("new/a/b1/c/shouldbeignored.txt");
-
- //Test name-only (use non-existent folders)
- assertNotIgnored("notarealfile");
- assertNotIgnored("/notarealfile");
- assertIgnored("new/notarealfile");
- assertIgnored("new/notarealfile/fake");
- assertIgnored("new/a/notarealfile");
- assertIgnored("new/a/b1/notarealfile");
-
- //Test clearing node -- create empty .gitignore
- createIgnoreFile(db.getDirectory().getParentFile() + "/new/a/b2/.gitignore", new String[0]);
- test = new File(db.getDirectory().getParentFile(), "new/a/b2/c");
- initCache();
- baseRules = cache.getRules("new/a/b2");
- assertNotNull(baseRules);
- baseRules.clear();
- assertEquals(baseRules.getRules().size(), 0);
- try {
- assertFalse("Node not properly cleared", baseRules.isIgnored(getRelativePath(test)));
- } catch (IOException e) {
- e.printStackTrace();
- fail("IO exception when testing base rules");
- }
-
- //Test clearing entire cache, and isEmpty
- assertNotNull(cache.getRules(""));
- assertFalse(cache.isEmpty());
- cache.clear();
- assertNull(cache.getRules(""));
- assertTrue(cache.isEmpty());
- assertNotIgnored("/anything");
- assertNotIgnored("/new/anything");
- assertNotIgnored("/src/anything");
- }
-
- public void testPriorities() {
- ignoreTestDir = JGitTestUtil.getTestResourceFile("excludeTest");
- assertTrue("Test resource directory is not a directory",ignoreTestDir.isDirectory());
- createExcludeFile();
- initCache();
-
- File test = new File(db.getDirectory().getParentFile(), "/src/test.stp");
- assertTrue("Resource file " + test.getName() + " is missing", test.exists());
-
- //Test basic exclude file
- IgnoreNode node = cache.getRules("src");
- assertNotNull("Excludes file was not initialized", node);
-
- /*
- * src/.gitignore:
- * /*.st?
- * !/test.stp
- * !/a.c
- * /a.c
- *
- * ./.gitignore:
- * !/notignored
- *
- * .git/info/exclude:
- * /test.stp
- * /notignored
- */
- assertIgnored("src/a.c");
- assertIgnored("test.stp");
- assertIgnored("src/blank.stp");
- assertNotIgnored("notignored");
- assertNotIgnored("src/test.stp");
-
- assertEquals(4, node.getRules().size());
-
- /*
- * new/.gitignore:
- * notarealfile
- *
- * new/a/.gitignore:
- * <empty>
- *
- * new/a/b2/.gitignore:
- * <does not exist>
- *
- * new/a/b2/c/.gitignore:
- * /notarealfile2
- */
- assertIgnored("new/a/b2/c/notarealfile2");
- assertIgnored("new/notarealfile");
- assertIgnored("new/a/notarealfile");
- assertNotIgnored("new/a/b2/c/test.stp");
- assertNotIgnored("new/a/b2/c");
- assertNotIgnored("new/a/b2/nonexistent");
- }
-
- /**
- * Check if a file is not matched as ignored
- * @param relativePath
- * Path to file, relative to db.getDirectory. Use "/" as a separator,
- * this method will replace all instances of "/" with File.separator
- */
- private void assertNotIgnored(String relativePath) {
- File test = new File(db.getDirectory().getParentFile(), relativePath);
- assertFalse("Should not match " + test.toString(), isIgnored(getRelativePath(test)));
- }
-
- /**
- * Check if a file is matched as ignored
- * @param relativePath
- * Path to file, relative to db.getDirectory. Use "/" as a separator,
- * this method will replace all instances of "/" with File.separator.
- */
- private void assertIgnored(String relativePath) {
- File test = new File(db.getDirectory().getParentFile(), relativePath);
- assertTrue("Failed to match " + test.toString(), isIgnored(getRelativePath(test)));
- }
-
- /**
- * Attempt to write an ignore file at the given location
- * @param path
- * Will create file at this path
- * @param contents
- * Each entry in contents will be entered on its own line
- */
- private void createIgnoreFile(String path, String[] contents) {
- File ignoreFile = new File(path);
- ignoreFile.delete();
- ignoreFile.deleteOnExit(); //Hope to catch in the event of crash
- toDelete.add(ignoreFile); //For teardown purposes
-
- //Jump through some hoops to create the exclude file
- try {
- if (!ignoreFile.createNewFile())
- fail("Could not create ignore file" + ignoreFile.getAbsolutePath());
-
- BufferedWriter bw = new BufferedWriter(new FileWriter (ignoreFile));
- for (String s : contents)
- bw.write(s + System.getProperty("line.separator"));
- bw.flush();
- bw.close();
- } catch (IOException e1) {
- e1.printStackTrace();
- fail("Could not create exclude file");
- }
- }
-
- private void createExcludeFile() {
- String[] content = new String[2];
- content[0] = "/test.stp";
- content[1] = "/notignored";
-
- //We can do this because we explicitly delete parent directories later in deleteIgnoreFiles.
- File parent= new File(db.getDirectory().getParentFile(), ".git/info");
- if (!parent.exists())
- parent.mkdirs();
-
- createIgnoreFile(db.getDirectory().getParentFile() + "/.git/info/exclude", content);
- }
-
- private void deleteIgnoreFiles() {
- for (File f : toDelete)
- f.delete();
-
- //Systematically delete exclude parent dirs
- File f = new File(ignoreTestDir.getAbsoluteFile(), ".git/info");
- f.delete();
- f = new File(ignoreTestDir.getAbsoluteFile(), ".git");
- f.delete();
- }
-
- /**
- * @param path
- * Filepath relative to the git directory
- * @return
- * Results of cache.isIgnored(path) -- true if ignored, false if
- * a negation is encountered or if no rules apply
- */
- private boolean isIgnored(String path) {
- try {
- return cache.isIgnored(path);
- } catch (IOException e) {
- fail("IOException when attempting to check ignored status");
- }
- return false;
- }
-
- private String getRelativePath(File file) {
- String retVal = db.getDirectory().getParentFile().toURI().relativize(file.toURI()).getPath();
- if (retVal.length() == file.getAbsolutePath().length())
- fail("Not a child of the git directory");
- if (retVal.endsWith("/"))
- retVal = retVal.substring(0, retVal.length() - 1);
-
- return retVal;
- }
-
- private void initCache() {
- try {
- cache.initialize();
- } catch (IOException e) {
- e.printStackTrace();
- fail("Could not initialize cache");
- }
- }
-
-}
diff --git a/org.eclipse.jgit.test/tst/org/eclipse/jgit/ignore/IgnoreNodeTest.java b/org.eclipse.jgit.test/tst/org/eclipse/jgit/ignore/IgnoreNodeTest.java
new file mode 100644
index 0000000..ea78204
--- /dev/null
+++ b/org.eclipse.jgit.test/tst/org/eclipse/jgit/ignore/IgnoreNodeTest.java
@@ -0,0 +1,184 @@
+/*
+ * Copyright (C) 2010, Red Hat 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.ignore;
+
+import java.io.File;
+import java.io.IOException;
+
+import org.eclipse.jgit.errors.CorruptObjectException;
+import org.eclipse.jgit.lib.FileMode;
+import org.eclipse.jgit.lib.RepositoryTestCase;
+import org.eclipse.jgit.treewalk.FileTreeIterator;
+import org.eclipse.jgit.treewalk.TreeWalk;
+import org.eclipse.jgit.treewalk.WorkingTreeIterator;
+
+/**
+ * Tests ignore node behavior on the local filesystem.
+ */
+public class IgnoreNodeTest extends RepositoryTestCase {
+ private static final FileMode D = FileMode.TREE;
+
+ private static final FileMode F = FileMode.REGULAR_FILE;
+
+ private static final boolean ignored = true;
+
+ private static final boolean tracked = false;
+
+ private TreeWalk walk;
+
+ public void testRules() throws IOException {
+ writeIgnoreFile(".git/info/exclude", "*~", "/out");
+
+ writeIgnoreFile(".gitignore", "*.o", "/config");
+ writeTrashFile("config/secret", "");
+ writeTrashFile("mylib.c", "");
+ writeTrashFile("mylib.c~", "");
+ writeTrashFile("mylib.o", "");
+
+ writeTrashFile("out/object/foo.exe", "");
+ writeIgnoreFile("src/config/.gitignore", "lex.out");
+ writeTrashFile("src/config/lex.out", "");
+ writeTrashFile("src/config/config.c", "");
+ writeTrashFile("src/config/config.c~", "");
+ writeTrashFile("src/config/old/lex.out", "");
+
+ beginWalk();
+ assertEntry(F, tracked, ".gitignore");
+ assertEntry(D, ignored, "config");
+ assertEntry(F, ignored, "config/secret");
+ assertEntry(F, tracked, "mylib.c");
+ assertEntry(F, ignored, "mylib.c~");
+ assertEntry(F, ignored, "mylib.o");
+
+ assertEntry(D, ignored, "out");
+ assertEntry(D, ignored, "out/object");
+ assertEntry(F, ignored, "out/object/foo.exe");
+
+ assertEntry(D, tracked, "src");
+ assertEntry(D, tracked, "src/config");
+ assertEntry(F, tracked, "src/config/.gitignore");
+ assertEntry(F, tracked, "src/config/config.c");
+ assertEntry(F, ignored, "src/config/config.c~");
+ assertEntry(F, ignored, "src/config/lex.out");
+ assertEntry(D, tracked, "src/config/old");
+ assertEntry(F, ignored, "src/config/old/lex.out");
+ }
+
+ public void testNegation() throws IOException {
+ writeIgnoreFile(".gitignore", "*.o");
+ writeIgnoreFile("src/a/b/.gitignore", "!keep.o");
+ writeTrashFile("src/a/b/keep.o", "");
+ writeTrashFile("src/a/b/nothere.o", "");
+
+ beginWalk();
+ assertEntry(F, tracked, ".gitignore");
+ assertEntry(D, tracked, "src");
+ assertEntry(D, tracked, "src/a");
+ assertEntry(D, tracked, "src/a/b");
+ assertEntry(F, tracked, "src/a/b/.gitignore");
+ assertEntry(F, tracked, "src/a/b/keep.o");
+ assertEntry(F, ignored, "src/a/b/nothere.o");
+ }
+
+ public void testSlashOnlyMatchesDirectory() throws IOException {
+ writeIgnoreFile(".gitignore", "out/");
+ writeTrashFile("out", "");
+
+ beginWalk();
+ assertEntry(F, tracked, ".gitignore");
+ assertEntry(F, tracked, "out");
+
+ new File(trash, "out").delete();
+ writeTrashFile("out/foo", "");
+
+ beginWalk();
+ assertEntry(F, tracked, ".gitignore");
+ assertEntry(D, ignored, "out");
+ assertEntry(F, ignored, "out/foo");
+ }
+
+ public void testWithSlashDoesNotMatchInSubDirectory() throws IOException {
+ writeIgnoreFile(".gitignore", "a/b");
+ writeTrashFile("a/a", "");
+ writeTrashFile("a/b", "");
+ writeTrashFile("src/a/a", "");
+ writeTrashFile("src/a/b", "");
+
+ beginWalk();
+ assertEntry(F, tracked, ".gitignore");
+ assertEntry(D, tracked, "a");
+ assertEntry(F, tracked, "a/a");
+ assertEntry(F, ignored, "a/b");
+ assertEntry(D, tracked, "src");
+ assertEntry(D, tracked, "src/a");
+ assertEntry(F, tracked, "src/a/a");
+ assertEntry(F, tracked, "src/a/b");
+ }
+
+ private void beginWalk() throws CorruptObjectException {
+ walk = new TreeWalk(db);
+ walk.reset();
+ walk.addTree(new FileTreeIterator(db));
+ }
+
+ private void assertEntry(FileMode type, boolean entryIgnored,
+ String pathName) throws IOException {
+ assertTrue("walk has entry", walk.next());
+ assertEquals(pathName, walk.getPathString());
+ assertEquals(type, walk.getFileMode(0));
+
+ WorkingTreeIterator itr = walk.getTree(0, WorkingTreeIterator.class);
+ assertNotNull("has tree", itr);
+ assertEquals("is ignored", entryIgnored, itr.isEntryIgnored());
+ if (D.equals(type))
+ walk.enterSubtree();
+ }
+
+ private void writeIgnoreFile(String name, String... rules)
+ throws IOException {
+ StringBuilder data = new StringBuilder();
+ for (String line : rules)
+ data.append(line + "\n");
+ writeTrashFile(name, data.toString());
+ }
+}
diff --git a/org.eclipse.jgit.test/tst/org/eclipse/jgit/lib/IndexDiffTest.java b/org.eclipse.jgit.test/tst/org/eclipse/jgit/lib/IndexDiffTest.java
index ac7ce5b..c439bac 100644
--- a/org.eclipse.jgit.test/tst/org/eclipse/jgit/lib/IndexDiffTest.java
+++ b/org.eclipse.jgit.test/tst/org/eclipse/jgit/lib/IndexDiffTest.java
@@ -48,16 +48,21 @@
import java.io.File;
import java.io.IOException;
+import org.eclipse.jgit.treewalk.FileTreeIterator;
+
public class IndexDiffTest extends RepositoryTestCase {
public void testAdded() throws IOException {
GitIndex index = new GitIndex(db);
writeTrashFile("file1", "file1");
writeTrashFile("dir/subfile", "dir/subfile");
Tree tree = new Tree(db);
+ tree.setId(new ObjectWriter(db).writeTree(tree));
index.add(trash, new File(trash, "file1"));
index.add(trash, new File(trash, "dir/subfile"));
- IndexDiff diff = new IndexDiff(tree, index);
+ index.write();
+ FileTreeIterator iterator = new FileTreeIterator(db);
+ IndexDiff diff = new IndexDiff(db, tree.getId(), iterator);
diff.diff();
assertEquals(2, diff.getAdded().size());
assertTrue(diff.getAdded().contains("file1"));
@@ -68,7 +73,6 @@ public void testAdded() throws IOException {
}
public void testRemoved() throws IOException {
- GitIndex index = new GitIndex(db);
writeTrashFile("file2", "file2");
writeTrashFile("dir/file3", "dir/file3");
@@ -82,7 +86,8 @@ public void testRemoved() throws IOException {
tree2.setId(new ObjectWriter(db).writeTree(tree2));
tree.setId(new ObjectWriter(db).writeTree(tree));
- IndexDiff diff = new IndexDiff(tree, index);
+ FileTreeIterator iterator = new FileTreeIterator(db);
+ IndexDiff diff = new IndexDiff(db, tree.getId(), iterator);
diff.diff();
assertEquals(2, diff.getRemoved().size());
assertTrue(diff.getRemoved().contains("file2"));
@@ -98,6 +103,7 @@ public void testModified() throws IOException {
index.add(trash, writeTrashFile("file2", "file2"));
index.add(trash, writeTrashFile("dir/file3", "dir/file3"));
+ index.write();
writeTrashFile("dir/file3", "changed");
@@ -109,7 +115,8 @@ public void testModified() throws IOException {
Tree tree2 = (Tree) tree.findTreeMember("dir");
tree2.setId(new ObjectWriter(db).writeTree(tree2));
tree.setId(new ObjectWriter(db).writeTree(tree));
- IndexDiff diff = new IndexDiff(tree, index);
+ FileTreeIterator iterator = new FileTreeIterator(db);
+ IndexDiff diff = new IndexDiff(db, tree.getId(), iterator);
diff.diff();
assertEquals(2, diff.getChanged().size());
assertTrue(diff.getChanged().contains("file2"));
@@ -128,6 +135,7 @@ public void testUnchangedSimple() throws IOException {
index.add(trash, writeTrashFile("a.c", "a.c"));
index.add(trash, writeTrashFile("a=c", "a=c"));
index.add(trash, writeTrashFile("a=d", "a=d"));
+ index.write();
Tree tree = new Tree(db);
// got the hash id'd from the data using echo -n a.b|git hash-object -t blob --stdin
@@ -138,7 +146,8 @@ public void testUnchangedSimple() throws IOException {
tree.setId(new ObjectWriter(db).writeTree(tree));
- IndexDiff diff = new IndexDiff(tree, index);
+ FileTreeIterator iterator = new FileTreeIterator(db);
+ IndexDiff diff = new IndexDiff(db, tree.getId(), iterator);
diff.diff();
assertEquals(0, diff.getChanged().size());
assertEquals(0, diff.getAdded().size());
@@ -163,6 +172,7 @@ public void testUnchangedComplex() throws IOException {
index.add(trash, writeTrashFile("a/c", "a/c"));
index.add(trash, writeTrashFile("a=c", "a=c"));
index.add(trash, writeTrashFile("a=d", "a=d"));
+ index.write();
Tree tree = new Tree(db);
// got the hash id'd from the data using echo -n a.b|git hash-object -t blob --stdin
@@ -180,7 +190,8 @@ public void testUnchangedComplex() throws IOException {
tree2.setId(new ObjectWriter(db).writeTree(tree2));
tree.setId(new ObjectWriter(db).writeTree(tree));
- IndexDiff diff = new IndexDiff(tree, index);
+ FileTreeIterator iterator = new FileTreeIterator(db);
+ IndexDiff diff = new IndexDiff(db, tree.getId(), iterator);
diff.diff();
assertEquals(0, diff.getChanged().size());
assertEquals(0, diff.getAdded().size());
diff --git a/org.eclipse.jgit.test/tst/org/eclipse/jgit/lib/MergeHeadMsgTest.java b/org.eclipse.jgit.test/tst/org/eclipse/jgit/lib/MergeHeadMsgTest.java
new file mode 100644
index 0000000..fbb7924
--- /dev/null
+++ b/org.eclipse.jgit.test/tst/org/eclipse/jgit/lib/MergeHeadMsgTest.java
@@ -0,0 +1,108 @@
+/*
+ * Copyright (C) 2010, Christian Halstrick <christian.halstrick@sap.com>
+ * and other copyright owners as documented in the project's IP log.
+ *
+ * This program and the accompanying materials are made available
+ * under the terms of the Eclipse Distribution License v1.0 which
+ * accompanies this distribution, is reproduced below, and is
+ * available at http://www.eclipse.org/org/documents/edl-v10.php
+ *
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or
+ * without modification, are permitted provided that the following
+ * conditions are met:
+ *
+ * - Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ *
+ * - Redistributions in binary form must reproduce the above
+ * copyright notice, this list of conditions and the following
+ * disclaimer in the documentation and/or other materials provided
+ * with the distribution.
+ *
+ * - Neither the name of the Eclipse Foundation, Inc. nor the
+ * names of its contributors may be used to endorse or promote
+ * products derived from this software without specific prior
+ * written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND
+ * CONTRIBUTORS "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES,
+ * INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
+ * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR
+ * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
+ * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT
+ * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
+ * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER
+ * CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT,
+ * STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
+ * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF
+ * ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ */
+package org.eclipse.jgit.lib;
+
+import java.io.File;
+import java.io.FileOutputStream;
+import java.io.IOException;
+import java.util.Arrays;
+import java.util.Collections;
+
+public class MergeHeadMsgTest extends RepositoryTestCase {
+ private static final String mergeMsg = "merge a and b";
+
+ private static final String sampleId = "1c6db447abdbb291b25f07be38ea0b1bf94947c5";
+
+ public void testReadWriteMergeHeads() throws IOException {
+ assertEquals(db.readMergeHeads(), null);
+ db.writeMergeHeads(Arrays.asList(ObjectId.zeroId(),
+ ObjectId.fromString(sampleId)));
+ assertEquals(read(new File(db.getDirectory(), "MERGE_HEAD")), "0000000000000000000000000000000000000000\n1c6db447abdbb291b25f07be38ea0b1bf94947c5\n");
+ assertEquals(db.readMergeHeads().size(), 2);
+ assertEquals(db.readMergeHeads().get(0), ObjectId.zeroId());
+ assertEquals(db.readMergeHeads().get(1), ObjectId.fromString(sampleId));
+ // same test again, this time with lower-level io
+ FileOutputStream fos = new FileOutputStream(new File(db.getDirectory(),
+ "MERGE_HEAD"));
+ try {
+ fos.write("0000000000000000000000000000000000000000\n1c6db447abdbb291b25f07be38ea0b1bf94947c5\n".getBytes(Constants.CHARACTER_ENCODING));
+ } finally {
+ fos.close();
+ }
+ assertEquals(db.readMergeHeads().size(), 2);
+ assertEquals(db.readMergeHeads().get(0), ObjectId.zeroId());
+ assertEquals(db.readMergeHeads().get(1), ObjectId.fromString(sampleId));
+ db.writeMergeHeads(Collections.EMPTY_LIST);
+ assertEquals(read(new File(db.getDirectory(), "MERGE_HEAD")), "");
+ assertEquals(db.readMergeHeads(), null);
+ fos = new FileOutputStream(new File(db.getDirectory(),
+ "MERGE_HEAD"));
+ try {
+ fos.write(sampleId.getBytes(Constants.CHARACTER_ENCODING));
+ } finally {
+ fos.close();
+ }
+ assertEquals(db.readMergeHeads().size(), 1);
+ assertEquals(db.readMergeHeads().get(0), ObjectId.fromString(sampleId));
+ }
+
+ public void testReadWriteMergeMsg() throws IOException {
+ assertEquals(db.readMergeCommitMsg(), null);
+ assertFalse(new File(db.getDirectory(), "MERGE_MSG").exists());
+ db.writeMergeCommitMsg(mergeMsg);
+ assertEquals(db.readMergeCommitMsg(), mergeMsg);
+ assertEquals(read(new File(db.getDirectory(), "MERGE_MSG")), mergeMsg);
+ db.writeMergeCommitMsg(null);
+ assertEquals(db.readMergeCommitMsg(), null);
+ assertFalse(new File(db.getDirectory(), "MERGE_MSG").exists());
+ FileOutputStream fos = new FileOutputStream(new File(db.getDirectory(),
+ Constants.MERGE_MSG));
+ try {
+ fos.write(mergeMsg.getBytes(Constants.CHARACTER_ENCODING));
+ } finally {
+ fos.close();
+ }
+ assertEquals(db.readMergeCommitMsg(), mergeMsg);
+ }
+
+}
diff --git a/org.eclipse.jgit.test/tst/org/eclipse/jgit/lib/RacyGitTests.java b/org.eclipse.jgit.test/tst/org/eclipse/jgit/lib/RacyGitTests.java
new file mode 100644
index 0000000..e208b27
--- /dev/null
+++ b/org.eclipse.jgit.test/tst/org/eclipse/jgit/lib/RacyGitTests.java
@@ -0,0 +1,191 @@
+/*
+ * Copyright (C) 2010, Christian Halstrick <christian.halstrick@sap.com>
+ * and other copyright owners as documented in the project's IP log.
+ *
+ * This program and the accompanying materials are made available
+ * under the terms of the Eclipse Distribution License v1.0 which
+ * accompanies this distribution, is reproduced below, and is
+ * available at http://www.eclipse.org/org/documents/edl-v10.php
+ *
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or
+ * without modification, are permitted provided that the following
+ * conditions are met:
+ *
+ * - Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ *
+ * - Redistributions in binary form must reproduce the above
+ * copyright notice, this list of conditions and the following
+ * disclaimer in the documentation and/or other materials provided
+ * with the distribution.
+ *
+ * - Neither the name of the Eclipse Foundation, Inc. nor the
+ * names of its contributors may be used to endorse or promote
+ * products derived from this software without specific prior
+ * written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND
+ * CONTRIBUTORS "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES,
+ * INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
+ * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR
+ * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
+ * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT
+ * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
+ * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER
+ * CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT,
+ * STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
+ * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF
+ * ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ */
+package org.eclipse.jgit.lib;
+
+import java.io.File;
+import java.io.FileNotFoundException;
+import java.io.FileOutputStream;
+import java.io.IOException;
+import java.util.TreeSet;
+
+import org.eclipse.jgit.dircache.DirCacheBuilder;
+import org.eclipse.jgit.dircache.DirCacheEntry;
+import org.eclipse.jgit.treewalk.FileTreeIterator;
+import org.eclipse.jgit.treewalk.FileTreeIteratorWithTimeControl;
+import org.eclipse.jgit.treewalk.NameConflictTreeWalk;
+
+public class RacyGitTests extends RepositoryTestCase {
+ public void testIterator() throws IllegalStateException, IOException,
+ InterruptedException {
+ TreeSet<Long> modTimes = new TreeSet<Long>();
+ File lastFile = null;
+ for (int i = 0; i < 10; i++) {
+ lastFile = new File(db.getWorkTree(), "0." + i);
+ lastFile.createNewFile();
+ if (i == 5)
+ fsTick(lastFile);
+ }
+ modTimes.add(fsTick(lastFile));
+ for (int i = 0; i < 10; i++) {
+ lastFile = new File(db.getWorkTree(), "1." + i);
+ lastFile.createNewFile();
+ }
+ modTimes.add(fsTick(lastFile));
+ for (int i = 0; i < 10; i++) {
+ lastFile = new File(db.getWorkTree(), "2." + i);
+ lastFile.createNewFile();
+ if (i % 4 == 0)
+ fsTick(lastFile);
+ }
+ FileTreeIteratorWithTimeControl fileIt = new FileTreeIteratorWithTimeControl(
+ db, modTimes);
+ NameConflictTreeWalk tw = new NameConflictTreeWalk(db);
+ tw.reset();
+ tw.addTree(fileIt);
+ tw.setRecursive(true);
+ FileTreeIterator t;
+ long t0 = 0;
+ for (int i = 0; i < 10; i++) {
+ assertTrue(tw.next());
+ t = tw.getTree(0, FileTreeIterator.class);
+ if (i == 0)
+ t0 = t.getEntryLastModified();
+ else
+ assertEquals(t0, t.getEntryLastModified());
+ }
+ long t1 = 0;
+ for (int i = 0; i < 10; i++) {
+ assertTrue(tw.next());
+ t = tw.getTree(0, FileTreeIterator.class);
+ if (i == 0) {
+ t1 = t.getEntryLastModified();
+ assertTrue(t1 > t0);
+ } else
+ assertEquals(t1, t.getEntryLastModified());
+ }
+ long t2 = 0;
+ for (int i = 0; i < 10; i++) {
+ assertTrue(tw.next());
+ t = tw.getTree(0, FileTreeIterator.class);
+ if (i == 0) {
+ t2 = t.getEntryLastModified();
+ assertTrue(t2 > t1);
+ } else
+ assertEquals(t2, t.getEntryLastModified());
+ }
+ }
+
+ public void testRacyGitDetection() throws IOException,
+ IllegalStateException, InterruptedException {
+ TreeSet<Long> modTimes = new TreeSet<Long>();
+ File lastFile;
+
+ // wait to ensure that modtimes of the file doesn't match last index
+ // file modtime
+ modTimes.add(fsTick(db.getIndexFile()));
+
+ // create two files
+ addToWorkDir("a", "a");
+ lastFile = addToWorkDir("b", "b");
+
+ // wait to ensure that file-modTimes and therefore index entry modTime
+ // doesn't match the modtime of index-file after next persistance
+ modTimes.add(fsTick(lastFile));
+
+ // now add both files to the index. No racy git expected
+ addToIndex(modTimes);
+
+ assertEquals(
+ "[a, mode:100644, time:t0, length:1, sha1:2e65efe2a145dda7ee51d1741299f848e5bf752e]" +
+ "[b, mode:100644, time:t0, length:1, sha1:63d8dbd40c23542e740659a7168a0ce3138ea748]",
+ indexState(SMUDGE | MOD_TIME | LENGTH | CONTENT_ID));
+
+ // Remember the last modTime of index file. All modifications times of
+ // further modification are translated to this value so it looks that
+ // files have been modified in the same time slot as the index file
+ modTimes.add(Long.valueOf(db.getIndexFile().lastModified()));
+
+ // modify one file
+ addToWorkDir("a", "a2");
+ // now update the index the index. 'a' has to be racily clean -- because
+ // it's modification time is exactly the same as the previous index file
+ // mod time.
+ addToIndex(modTimes);
+
+ db.readDirCache();
+ // although racily clean a should not be reported as being dirty
+ assertEquals(
+ "[a, mode:100644, time:t1, smudged, length:0]" +
+ "[b, mode:100644, time:t0, length:1]",
+ indexState(SMUDGE|MOD_TIME|LENGTH));
+ }
+
+ private void addToIndex(TreeSet<Long> modTimes)
+ throws FileNotFoundException, IOException {
+ DirCacheBuilder builder = db.lockDirCache().builder();
+ FileTreeIterator fIt = new FileTreeIteratorWithTimeControl(
+ db, modTimes);
+ DirCacheEntry dce;
+ while (!fIt.eof()) {
+ dce = new DirCacheEntry(fIt.getEntryPathString());
+ dce.setFileMode(fIt.getEntryFileMode());
+ dce.setLastModified(fIt.getEntryLastModified());
+ dce.setLength((int) fIt.getEntryLength());
+ dce.setObjectId(fIt.getEntryObjectId());
+ builder.add(dce);
+ fIt.next(1);
+ }
+ builder.commit();
+ }
+
+ private File addToWorkDir(String path, String content) throws IOException {
+ File f = new File(db.getWorkTree(), path);
+ FileOutputStream fos = new FileOutputStream(f);
+ try {
+ fos.write(content.getBytes(Constants.CHARACTER_ENCODING));
+ return f;
+ } finally {
+ fos.close();
+ }
+ }
+}
diff --git a/org.eclipse.jgit.test/tst/org/eclipse/jgit/lib/ReadTreeTest.java b/org.eclipse.jgit.test/tst/org/eclipse/jgit/lib/ReadTreeTest.java
index 5b4be43..2fa45c8 100644
--- a/org.eclipse.jgit.test/tst/org/eclipse/jgit/lib/ReadTreeTest.java
+++ b/org.eclipse.jgit.test/tst/org/eclipse/jgit/lib/ReadTreeTest.java
@@ -45,11 +45,9 @@
*/
package org.eclipse.jgit.lib;
-import java.io.ByteArrayInputStream;
import java.io.File;
import java.io.FileInputStream;
import java.io.IOException;
-import java.io.InputStream;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.HashMap;
@@ -58,7 +56,6 @@
import org.eclipse.jgit.errors.CorruptObjectException;
import org.eclipse.jgit.treewalk.FileTreeIterator;
import org.eclipse.jgit.treewalk.TreeWalk;
-import org.eclipse.jgit.util.FS;
public abstract class ReadTreeTest extends RepositoryTestCase {
protected Tree theHead;
@@ -132,13 +129,15 @@ private Tree buildTree(HashMap<String, String> headEntries) throws IOException {
}
ObjectId genSha1(String data) {
- InputStream is = new ByteArrayInputStream(data.getBytes());
- ObjectWriter objectWriter = new ObjectWriter(db);
+ ObjectInserter w = db.newObjectInserter();
try {
- return objectWriter.writeObject(Constants.OBJ_BLOB, data
- .getBytes().length, is, true);
+ ObjectId id = w.insert(Constants.OBJ_BLOB, data.getBytes());
+ w.flush();
+ return id;
} catch (IOException e) {
fail(e.toString());
+ } finally {
+ w.release();
}
return null;
}
@@ -613,7 +612,7 @@ public void assertWorkDir(HashMap<String, String> i)
TreeWalk walk = new TreeWalk(db);
walk.reset();
walk.setRecursive(true);
- walk.addTree(new FileTreeIterator(db.getWorkDir(), FS.DETECTED));
+ walk.addTree(new FileTreeIterator(db));
String expectedValue;
String path;
int nrFiles = 0;
@@ -624,7 +623,7 @@ public void assertWorkDir(HashMap<String, String> i)
expectedValue = i.get(path);
assertNotNull("found unexpected file for path "
+ path + " in workdir", expectedValue);
- File file = new File(db.getWorkDir(), path);
+ File file = new File(db.getWorkTree(), path);
assertTrue(file.exists());
if (file.isFile()) {
FileInputStream is = new FileInputStream(file);
@@ -662,8 +661,8 @@ public void assertIndex(HashMap<String, String> i)
assertTrue("unexpected content for path " + path
+ " in index. Expected: <" + expectedValue + ">",
Arrays.equals(
- db.openBlob(theIndex.getMembers()[j].getObjectId())
- .getBytes(), i.get(path).getBytes()));
+ db.open(theIndex.getMembers()[j].getObjectId())
+ .getCachedBytes(), i.get(path).getBytes()));
}
}
diff --git a/org.eclipse.jgit.test/tst/org/eclipse/jgit/lib/ReflogConfigTest.java b/org.eclipse.jgit.test/tst/org/eclipse/jgit/lib/ReflogConfigTest.java
index 88bcf76..d78892b 100644
--- a/org.eclipse.jgit.test/tst/org/eclipse/jgit/lib/ReflogConfigTest.java
+++ b/org.eclipse.jgit.test/tst/org/eclipse/jgit/lib/ReflogConfigTest.java
@@ -70,7 +70,7 @@ public void testlogAllRefUpdates() throws Exception {
// set the logAllRefUpdates parameter to true and check it
db.getConfig().setBoolean("core", null, "logallrefupdates", true);
- assertTrue(db.getConfig().getCore().isLogAllRefUpdates());
+ assertTrue(db.getConfig().get(CoreConfig.KEY).isLogAllRefUpdates());
// do one commit and check that reflog size is increased to 1
addFileToTree(t, "i-am-another-file", "and this is other data in me\n");
@@ -83,7 +83,7 @@ public void testlogAllRefUpdates() throws Exception {
// set the logAllRefUpdates parameter to false and check it
db.getConfig().setBoolean("core", null, "logallrefupdates", false);
- assertFalse(db.getConfig().getCore().isLogAllRefUpdates());
+ assertFalse(db.getConfig().get(CoreConfig.KEY).isLogAllRefUpdates());
// do one commit and check that reflog size is 2
addFileToTree(t, "i-am-anotheranother-file",
diff --git a/org.eclipse.jgit.test/tst/org/eclipse/jgit/lib/RepositoryTestCase.java b/org.eclipse.jgit.test/tst/org/eclipse/jgit/lib/RepositoryTestCase.java
index c5c6d99..963c9d0 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
@@ -52,8 +52,14 @@
import java.io.IOException;
import java.io.InputStreamReader;
import java.io.Reader;
+import java.util.Map;
+import java.util.TreeSet;
+import org.eclipse.jgit.dircache.DirCache;
+import org.eclipse.jgit.dircache.DirCacheIterator;
import org.eclipse.jgit.junit.LocalDiskRepositoryTestCase;
+import org.eclipse.jgit.storage.file.FileRepository;
+import org.eclipse.jgit.treewalk.NameConflictTreeWalk;
/**
* Base class for most JGit unit tests.
@@ -83,7 +89,7 @@ protected static void copyFile(final File src, final File dst)
protected File writeTrashFile(final String name, final String data)
throws IOException {
- File path = new File(db.getWorkDir(), name);
+ File path = new File(db.getWorkTree(), name);
write(path, data);
return path;
}
@@ -102,7 +108,7 @@ protected static void checkFile(File f, final String checkData)
}
/** Test repository, initialized for this test case. */
- protected Repository db;
+ protected FileRepository db;
/** Working directory of {@link #db}. */
protected File trash;
@@ -111,6 +117,156 @@ protected static void checkFile(File f, final String checkData)
protected void setUp() throws Exception {
super.setUp();
db = createWorkRepository();
- trash = db.getWorkDir();
+ trash = db.getWorkTree();
+ }
+
+ public static final int MOD_TIME = 1;
+
+ public static final int SMUDGE = 2;
+
+ public static final int LENGTH = 4;
+
+ public static final int CONTENT_ID = 8;
+
+ /**
+ * Represent the state of the index in one String. This representation is
+ * useful when writing tests which do assertions on the state of the index.
+ * By default information about path, mode, stage (if different from 0) is
+ * included. A bitmask controls which additional info about
+ * modificationTimes, smudge state and length is included.
+ * <p>
+ * The format of the returned string is described with this BNF:
+ *
+ * <pre>
+ * result = ( "[" path mode stage? time? smudge? length? sha1? "]" )* .
+ * mode = ", mode:" number .
+ * stage = ", stage:" number .
+ * time = ", time:t" timestamp-index .
+ * smudge = "" | ", smudged" .
+ * length = ", length:" number .
+ * sha1 = ", sha1:" hex-sha1 .
+ *
+ * 'stage' is only presented when the stage is different from 0. All
+ * reported time stamps are mapped to strings like "t0", "t1", ... "tn". The
+ * smallest reported time-stamp will be called "t0". This allows to write
+ * assertions against the string although the concrete value of the
+ * time stamps is unknown.
+ *
+ * @param includedOptions
+ * a bitmask constructed out of the constants {@link #MOD_TIME},
+ * {@link #SMUDGE}, {@link #LENGTH} and {@link #CONTENT_ID}
+ * controlling which info is present in the resulting string.
+ * @return a string encoding the index state
+ * @throws IllegalStateException
+ * @throws IOException
+ */
+ public String indexState(int includedOptions)
+ throws IllegalStateException, IOException {
+ DirCache dc = db.readDirCache();
+ StringBuilder sb = new StringBuilder();
+ TreeSet<Long> timeStamps = null;
+
+ // iterate once over the dircache just to collect all time stamps
+ if (0 != (includedOptions & MOD_TIME)) {
+ timeStamps = new TreeSet<Long>();
+ for (int i=0; i<dc.getEntryCount(); ++i)
+ timeStamps.add(Long.valueOf(dc.getEntry(i).getLastModified()));
+ }
+
+ // iterate again, now produce the result string
+ NameConflictTreeWalk tw = new NameConflictTreeWalk(db);
+ tw.setRecursive(true);
+ tw.reset();
+ tw.addTree(new DirCacheIterator(dc));
+ while (tw.next()) {
+ DirCacheIterator dcIt = tw.getTree(0, DirCacheIterator.class);
+ sb.append("["+tw.getPathString()+", mode:" + dcIt.getEntryFileMode());
+ int stage = dcIt.getDirCacheEntry().getStage();
+ if (stage != 0)
+ sb.append(", stage:" + stage);
+ if (0 != (includedOptions & MOD_TIME)) {
+ sb.append(", time:t"+
+ timeStamps.headSet(Long.valueOf(dcIt.getDirCacheEntry().getLastModified())).size());
+ }
+ if (0 != (includedOptions & SMUDGE))
+ if (dcIt.getDirCacheEntry().isSmudged())
+ sb.append(", smudged");
+ if (0 != (includedOptions & LENGTH))
+ sb.append(", length:"
+ + Integer.toString(dcIt.getDirCacheEntry().getLength()));
+ if (0 != (includedOptions & CONTENT_ID))
+ sb.append(", sha1:" + ObjectId.toString(dcIt
+ .getEntryObjectId()));
+ sb.append("]");
+ }
+ return sb.toString();
+ }
+
+ /**
+ * Helper method to map arbitrary objects to user-defined names. This can be
+ * used create short names for objects to produce small and stable debug
+ * output. It is guaranteed that when you lookup the same object multiple
+ * times even with different nameTemplates this method will always return
+ * the same name which was derived from the first nameTemplate.
+ * nameTemplates can contain "%n" which will be replaced by a running number
+ * before used as a name.
+ *
+ * @param l
+ * the object to lookup
+ * @param nameTemplate
+ * the name for that object. Can contain "%n" which will be
+ * replaced by a running number before used as a name. If the
+ * lookup table already contains the object this parameter will
+ * be ignored
+ * @param lookupTable
+ * a table storing object-name mappings.
+ * @return a name of that object. Is not guaranteed to be unique. Use
+ * nameTemplates containing "%n" to always have unique names
+ */
+ public static String lookup(Object l, String nameTemplate,
+ Map<Object, String> lookupTable) {
+ String name = lookupTable.get(l);
+ if (name == null) {
+ name = nameTemplate.replaceAll("%n",
+ Integer.toString(lookupTable.size()));
+ lookupTable.put(l, name);
+ }
+ return name;
+ }
+
+ /**
+ * Waits until it is guaranteed that a subsequent file modification has a
+ * younger modification timestamp than the modification timestamp of the
+ * given file. This is done by touching a temporary file, reading the
+ * lastmodified attribute and, if needed, sleeping. After sleeping this loop
+ * starts again until the filesystem timer has advanced enough.
+ *
+ * @param lastFile
+ * the file on which we want to wait until the filesystem timer
+ * has advanced more than the lastmodification timestamp of this
+ * file
+ * @return return the last measured value of the filesystem timer which is
+ * greater than then the lastmodification time of lastfile.
+ * @throws InterruptedException
+ * @throws IOException
+ */
+ public static long fsTick(File lastFile) throws InterruptedException,
+ IOException {
+ long sleepTime = 1;
+ File tmp = File.createTempFile("FileTreeIteratorWithTimeControl", null);
+ try {
+ long startTime = (lastFile == null) ? tmp.lastModified() : lastFile
+ .lastModified();
+ long actTime = tmp.lastModified();
+ while (actTime <= startTime) {
+ Thread.sleep(sleepTime);
+ sleepTime *= 5;
+ tmp.setLastModified(System.currentTimeMillis());
+ actTime = tmp.lastModified();
+ }
+ return actTime;
+ } finally {
+ tmp.delete();
+ }
}
}
diff --git a/org.eclipse.jgit.test/tst/org/eclipse/jgit/lib/SampleDataRepositoryTestCase.java b/org.eclipse.jgit.test/tst/org/eclipse/jgit/lib/SampleDataRepositoryTestCase.java
index 10c0056..7bc9bb2 100644
--- a/org.eclipse.jgit.test/tst/org/eclipse/jgit/lib/SampleDataRepositoryTestCase.java
+++ b/org.eclipse.jgit.test/tst/org/eclipse/jgit/lib/SampleDataRepositoryTestCase.java
@@ -65,7 +65,7 @@ protected void setUp() throws Exception {
"pack-e6d07037cbcf13376308a0a995d1fa48f8f76aaa",
"pack-3280af9c07ee18a87705ef50b0cc4cd20266cf12"
};
- final File packDir = new File(db.getObjectsDirectory(), "pack");
+ final File packDir = new File(db.getObjectDatabase().getDirectory(), "pack");
for (String n : packs) {
copyFile(JGitTestUtil.getTestResourceFile(n + ".pack"), new File(packDir, n + ".pack"));
copyFile(JGitTestUtil.getTestResourceFile(n + ".idx"), new File(packDir, n + ".idx"));
diff --git a/org.eclipse.jgit.test/tst/org/eclipse/jgit/lib/WorkDirCheckout_ReadTreeTest.java b/org.eclipse.jgit.test/tst/org/eclipse/jgit/lib/WorkDirCheckout_ReadTreeTest.java
index ecaac58..7b5c3cd 100644
--- a/org.eclipse.jgit.test/tst/org/eclipse/jgit/lib/WorkDirCheckout_ReadTreeTest.java
+++ b/org.eclipse.jgit.test/tst/org/eclipse/jgit/lib/WorkDirCheckout_ReadTreeTest.java
@@ -52,12 +52,12 @@
public class WorkDirCheckout_ReadTreeTest extends ReadTreeTest {
private WorkDirCheckout wdc;
public void prescanTwoTrees(Tree head, Tree merge) throws IllegalStateException, IOException {
- wdc = new WorkDirCheckout(db, db.getWorkDir(), head, db.getIndex(), merge);
+ wdc = new WorkDirCheckout(db, db.getWorkTree(), head, db.getIndex(), merge);
wdc.prescanTwoTrees();
}
public void checkout() throws IOException {
- wdc = new WorkDirCheckout(db, db.getWorkDir(), theHead, db.getIndex(), theMerge);
+ wdc = new WorkDirCheckout(db, db.getWorkTree(), theHead, db.getIndex(), theMerge);
wdc.checkout();
}
diff --git a/org.eclipse.jgit.test/tst/org/eclipse/jgit/merge/CherryPickTest.java b/org.eclipse.jgit.test/tst/org/eclipse/jgit/merge/CherryPickTest.java
index 42e653b..1cd1261 100644
--- a/org.eclipse.jgit.test/tst/org/eclipse/jgit/merge/CherryPickTest.java
+++ b/org.eclipse.jgit.test/tst/org/eclipse/jgit/merge/CherryPickTest.java
@@ -44,7 +44,8 @@
package org.eclipse.jgit.merge;
-import java.io.ByteArrayInputStream;
+import static org.eclipse.jgit.lib.Constants.OBJ_BLOB;
+import static org.eclipse.jgit.lib.Constants.OBJ_COMMIT;
import org.eclipse.jgit.dircache.DirCache;
import org.eclipse.jgit.dircache.DirCacheBuilder;
@@ -53,7 +54,7 @@
import org.eclipse.jgit.lib.Constants;
import org.eclipse.jgit.lib.FileMode;
import org.eclipse.jgit.lib.ObjectId;
-import org.eclipse.jgit.lib.ObjectWriter;
+import org.eclipse.jgit.lib.ObjectInserter;
import org.eclipse.jgit.lib.PersonIdent;
import org.eclipse.jgit.lib.RepositoryTestCase;
import org.eclipse.jgit.treewalk.TreeWalk;
@@ -66,10 +67,10 @@ public void testPick() throws Exception {
// Cherry-pick "T" onto "O". This shouldn't introduce "p-fail", which
// was created by "P", nor should it modify "a", which was done by "P".
//
- final DirCache treeB = DirCache.read(db);
- final DirCache treeO = DirCache.read(db);
- final DirCache treeP = DirCache.read(db);
- final DirCache treeT = DirCache.read(db);
+ final DirCache treeB = db.readDirCache();
+ final DirCache treeO = db.readDirCache();
+ final DirCache treeP = db.readDirCache();
+ final DirCache treeT = db.readDirCache();
{
final DirCacheBuilder b = treeB.builder();
final DirCacheBuilder o = treeO.builder();
@@ -93,7 +94,7 @@ public void testPick() throws Exception {
t.finish();
}
- final ObjectWriter ow = new ObjectWriter(db);
+ final ObjectInserter ow = db.newObjectInserter();
final ObjectId B = commit(ow, treeB, new ObjectId[] {});
final ObjectId O = commit(ow, treeO, new ObjectId[] { B });
final ObjectId P = commit(ow, treeP, new ObjectId[] { B });
@@ -128,15 +129,17 @@ private void assertCorrectId(final DirCache treeT, final TreeWalk tw) {
.getObjectId(0));
}
- private ObjectId commit(final ObjectWriter ow, final DirCache treeB,
+ private ObjectId commit(final ObjectInserter odi, final DirCache treeB,
final ObjectId[] parentIds) throws Exception {
final Commit c = new Commit(db);
- c.setTreeId(treeB.writeTree(ow));
+ c.setTreeId(treeB.writeTree(odi));
c.setAuthor(new PersonIdent("A U Thor", "a.u.thor", 1L, 0));
c.setCommitter(c.getAuthor());
c.setParentIds(parentIds);
c.setMessage("Tree " + c.getTreeId().name());
- return ow.writeCommit(c);
+ ObjectId id = odi.insert(OBJ_COMMIT, odi.format(c));
+ odi.flush();
+ return id;
}
private DirCacheEntry makeEntry(final String path, final FileMode mode)
@@ -148,9 +151,8 @@ private DirCacheEntry makeEntry(final String path, final FileMode mode,
final String content) throws Exception {
final DirCacheEntry ent = new DirCacheEntry(path);
ent.setFileMode(mode);
- final byte[] contentBytes = Constants.encode(content);
- ent.setObjectId(new ObjectWriter(db).computeBlobSha1(
- contentBytes.length, new ByteArrayInputStream(contentBytes)));
+ ent.setObjectId(new ObjectInserter.Formatter().idFor(OBJ_BLOB,
+ Constants.encode(content)));
return ent;
}
}
diff --git a/org.eclipse.jgit.test/tst/org/eclipse/jgit/merge/SimpleMergeTest.java b/org.eclipse.jgit.test/tst/org/eclipse/jgit/merge/SimpleMergeTest.java
index 690b166..8657c52 100644
--- a/org.eclipse.jgit.test/tst/org/eclipse/jgit/merge/SimpleMergeTest.java
+++ b/org.eclipse.jgit.test/tst/org/eclipse/jgit/merge/SimpleMergeTest.java
@@ -44,7 +44,9 @@
package org.eclipse.jgit.merge;
-import java.io.ByteArrayInputStream;
+import static org.eclipse.jgit.lib.Constants.OBJ_BLOB;
+import static org.eclipse.jgit.lib.Constants.OBJ_COMMIT;
+
import java.io.IOException;
import org.eclipse.jgit.dircache.DirCache;
@@ -54,7 +56,7 @@
import org.eclipse.jgit.lib.Constants;
import org.eclipse.jgit.lib.FileMode;
import org.eclipse.jgit.lib.ObjectId;
-import org.eclipse.jgit.lib.ObjectWriter;
+import org.eclipse.jgit.lib.ObjectInserter;
import org.eclipse.jgit.lib.PersonIdent;
import org.eclipse.jgit.lib.SampleDataRepositoryTestCase;
import org.eclipse.jgit.treewalk.TreeWalk;
@@ -103,9 +105,9 @@ public void testTrivialTwoWay_conflict() throws IOException {
}
public void testTrivialTwoWay_validSubtreeSort() throws Exception {
- final DirCache treeB = DirCache.read(db);
- final DirCache treeO = DirCache.read(db);
- final DirCache treeT = DirCache.read(db);
+ final DirCache treeB = db.readDirCache();
+ final DirCache treeO = db.readDirCache();
+ final DirCache treeT = db.readDirCache();
{
final DirCacheBuilder b = treeB.builder();
final DirCacheBuilder o = treeO.builder();
@@ -126,7 +128,7 @@ public void testTrivialTwoWay_validSubtreeSort() throws Exception {
t.finish();
}
- final ObjectWriter ow = new ObjectWriter(db);
+ final ObjectInserter ow = db.newObjectInserter();
final ObjectId b = commit(ow, treeB, new ObjectId[] {});
final ObjectId o = commit(ow, treeO, new ObjectId[] { b });
final ObjectId t = commit(ow, treeT, new ObjectId[] { b });
@@ -155,9 +157,9 @@ public void testTrivialTwoWay_validSubtreeSort() throws Exception {
}
public void testTrivialTwoWay_concurrentSubtreeChange() throws Exception {
- final DirCache treeB = DirCache.read(db);
- final DirCache treeO = DirCache.read(db);
- final DirCache treeT = DirCache.read(db);
+ final DirCache treeB = db.readDirCache();
+ final DirCache treeO = db.readDirCache();
+ final DirCache treeT = db.readDirCache();
{
final DirCacheBuilder b = treeB.builder();
final DirCacheBuilder o = treeO.builder();
@@ -177,7 +179,7 @@ public void testTrivialTwoWay_concurrentSubtreeChange() throws Exception {
t.finish();
}
- final ObjectWriter ow = new ObjectWriter(db);
+ final ObjectInserter ow = db.newObjectInserter();
final ObjectId b = commit(ow, treeB, new ObjectId[] {});
final ObjectId o = commit(ow, treeO, new ObjectId[] { b });
final ObjectId t = commit(ow, treeT, new ObjectId[] { b });
@@ -202,9 +204,9 @@ public void testTrivialTwoWay_concurrentSubtreeChange() throws Exception {
}
public void testTrivialTwoWay_conflictSubtreeChange() throws Exception {
- final DirCache treeB = DirCache.read(db);
- final DirCache treeO = DirCache.read(db);
- final DirCache treeT = DirCache.read(db);
+ final DirCache treeB = db.readDirCache();
+ final DirCache treeO = db.readDirCache();
+ final DirCache treeT = db.readDirCache();
{
final DirCacheBuilder b = treeB.builder();
final DirCacheBuilder o = treeO.builder();
@@ -224,7 +226,7 @@ public void testTrivialTwoWay_conflictSubtreeChange() throws Exception {
t.finish();
}
- final ObjectWriter ow = new ObjectWriter(db);
+ final ObjectInserter ow = db.newObjectInserter();
final ObjectId b = commit(ow, treeB, new ObjectId[] {});
final ObjectId o = commit(ow, treeO, new ObjectId[] { b });
final ObjectId t = commit(ow, treeT, new ObjectId[] { b });
@@ -235,9 +237,9 @@ public void testTrivialTwoWay_conflictSubtreeChange() throws Exception {
}
public void testTrivialTwoWay_leftDFconflict1() throws Exception {
- final DirCache treeB = DirCache.read(db);
- final DirCache treeO = DirCache.read(db);
- final DirCache treeT = DirCache.read(db);
+ final DirCache treeB = db.readDirCache();
+ final DirCache treeO = db.readDirCache();
+ final DirCache treeT = db.readDirCache();
{
final DirCacheBuilder b = treeB.builder();
final DirCacheBuilder o = treeO.builder();
@@ -256,7 +258,7 @@ public void testTrivialTwoWay_leftDFconflict1() throws Exception {
t.finish();
}
- final ObjectWriter ow = new ObjectWriter(db);
+ final ObjectInserter ow = db.newObjectInserter();
final ObjectId b = commit(ow, treeB, new ObjectId[] {});
final ObjectId o = commit(ow, treeO, new ObjectId[] { b });
final ObjectId t = commit(ow, treeT, new ObjectId[] { b });
@@ -267,9 +269,9 @@ public void testTrivialTwoWay_leftDFconflict1() throws Exception {
}
public void testTrivialTwoWay_rightDFconflict1() throws Exception {
- final DirCache treeB = DirCache.read(db);
- final DirCache treeO = DirCache.read(db);
- final DirCache treeT = DirCache.read(db);
+ final DirCache treeB = db.readDirCache();
+ final DirCache treeO = db.readDirCache();
+ final DirCache treeT = db.readDirCache();
{
final DirCacheBuilder b = treeB.builder();
final DirCacheBuilder o = treeO.builder();
@@ -288,7 +290,7 @@ public void testTrivialTwoWay_rightDFconflict1() throws Exception {
t.finish();
}
- final ObjectWriter ow = new ObjectWriter(db);
+ final ObjectInserter ow = db.newObjectInserter();
final ObjectId b = commit(ow, treeB, new ObjectId[] {});
final ObjectId o = commit(ow, treeO, new ObjectId[] { b });
final ObjectId t = commit(ow, treeT, new ObjectId[] { b });
@@ -299,9 +301,9 @@ public void testTrivialTwoWay_rightDFconflict1() throws Exception {
}
public void testTrivialTwoWay_leftDFconflict2() throws Exception {
- final DirCache treeB = DirCache.read(db);
- final DirCache treeO = DirCache.read(db);
- final DirCache treeT = DirCache.read(db);
+ final DirCache treeB = db.readDirCache();
+ final DirCache treeO = db.readDirCache();
+ final DirCache treeT = db.readDirCache();
{
final DirCacheBuilder b = treeB.builder();
final DirCacheBuilder o = treeO.builder();
@@ -318,7 +320,7 @@ public void testTrivialTwoWay_leftDFconflict2() throws Exception {
t.finish();
}
- final ObjectWriter ow = new ObjectWriter(db);
+ final ObjectInserter ow = db.newObjectInserter();
final ObjectId b = commit(ow, treeB, new ObjectId[] {});
final ObjectId o = commit(ow, treeO, new ObjectId[] { b });
final ObjectId t = commit(ow, treeT, new ObjectId[] { b });
@@ -329,9 +331,9 @@ public void testTrivialTwoWay_leftDFconflict2() throws Exception {
}
public void testTrivialTwoWay_rightDFconflict2() throws Exception {
- final DirCache treeB = DirCache.read(db);
- final DirCache treeO = DirCache.read(db);
- final DirCache treeT = DirCache.read(db);
+ final DirCache treeB = db.readDirCache();
+ final DirCache treeO = db.readDirCache();
+ final DirCache treeT = db.readDirCache();
{
final DirCacheBuilder b = treeB.builder();
final DirCacheBuilder o = treeO.builder();
@@ -348,7 +350,7 @@ public void testTrivialTwoWay_rightDFconflict2() throws Exception {
t.finish();
}
- final ObjectWriter ow = new ObjectWriter(db);
+ final ObjectInserter ow = db.newObjectInserter();
final ObjectId b = commit(ow, treeB, new ObjectId[] {});
final ObjectId o = commit(ow, treeO, new ObjectId[] { b });
final ObjectId t = commit(ow, treeT, new ObjectId[] { b });
@@ -363,15 +365,17 @@ private void assertCorrectId(final DirCache treeT, final TreeWalk tw) {
.getObjectId(0));
}
- private ObjectId commit(final ObjectWriter ow, final DirCache treeB,
+ private ObjectId commit(final ObjectInserter odi, final DirCache treeB,
final ObjectId[] parentIds) throws Exception {
final Commit c = new Commit(db);
- c.setTreeId(treeB.writeTree(ow));
+ c.setTreeId(treeB.writeTree(odi));
c.setAuthor(new PersonIdent("A U Thor", "a.u.thor", 1L, 0));
c.setCommitter(c.getAuthor());
c.setParentIds(parentIds);
c.setMessage("Tree " + c.getTreeId().name());
- return ow.writeCommit(c);
+ ObjectId id = odi.insert(OBJ_COMMIT, odi.format(c));
+ odi.flush();
+ return id;
}
private DirCacheEntry makeEntry(final String path, final FileMode mode)
@@ -383,9 +387,8 @@ private DirCacheEntry makeEntry(final String path, final FileMode mode,
final String content) throws Exception {
final DirCacheEntry ent = new DirCacheEntry(path);
ent.setFileMode(mode);
- final byte[] contentBytes = Constants.encode(content);
- ent.setObjectId(new ObjectWriter(db).computeBlobSha1(
- contentBytes.length, new ByteArrayInputStream(contentBytes)));
+ ent.setObjectId(new ObjectInserter.Formatter().idFor(OBJ_BLOB,
+ Constants.encode(content)));
return ent;
}
}
diff --git a/org.eclipse.jgit.test/tst/org/eclipse/jgit/patch/FileHeaderTest.java b/org.eclipse.jgit.test/tst/org/eclipse/jgit/patch/FileHeaderTest.java
index 17e9977..813a701 100644
--- a/org.eclipse.jgit.test/tst/org/eclipse/jgit/patch/FileHeaderTest.java
+++ b/org.eclipse.jgit.test/tst/org/eclipse/jgit/patch/FileHeaderTest.java
@@ -79,16 +79,16 @@ public void testParseGitFileName_Foo() {
final FileHeader fh = header(name);
assertEquals(gitLine(name).length(), fh.parseGitFileName(0,
fh.buf.length));
- assertEquals(name, fh.getOldName());
- assertSame(fh.getOldName(), fh.getNewName());
+ assertEquals(name, fh.getOldPath());
+ assertSame(fh.getOldPath(), fh.getNewPath());
assertFalse(fh.hasMetaDataChanges());
}
public void testParseGitFileName_FailFooBar() {
final FileHeader fh = data("a/foo b/bar\n-");
assertTrue(fh.parseGitFileName(0, fh.buf.length) > 0);
- assertNull(fh.getOldName());
- assertNull(fh.getNewName());
+ assertNull(fh.getOldPath());
+ assertNull(fh.getNewPath());
assertFalse(fh.hasMetaDataChanges());
}
@@ -97,8 +97,8 @@ public void testParseGitFileName_FooSpBar() {
final FileHeader fh = header(name);
assertEquals(gitLine(name).length(), fh.parseGitFileName(0,
fh.buf.length));
- assertEquals(name, fh.getOldName());
- assertSame(fh.getOldName(), fh.getNewName());
+ assertEquals(name, fh.getOldPath());
+ assertSame(fh.getOldPath(), fh.getNewPath());
assertFalse(fh.hasMetaDataChanges());
}
@@ -108,8 +108,8 @@ public void testParseGitFileName_DqFooTabBar() {
final FileHeader fh = dqHeader(dqName);
assertEquals(dqGitLine(dqName).length(), fh.parseGitFileName(0,
fh.buf.length));
- assertEquals(name, fh.getOldName());
- assertSame(fh.getOldName(), fh.getNewName());
+ assertEquals(name, fh.getOldPath());
+ assertSame(fh.getOldPath(), fh.getNewPath());
assertFalse(fh.hasMetaDataChanges());
}
@@ -119,8 +119,8 @@ public void testParseGitFileName_DqFooSpLfNulBar() {
final FileHeader fh = dqHeader(dqName);
assertEquals(dqGitLine(dqName).length(), fh.parseGitFileName(0,
fh.buf.length));
- assertEquals(name, fh.getOldName());
- assertSame(fh.getOldName(), fh.getNewName());
+ assertEquals(name, fh.getOldPath());
+ assertSame(fh.getOldPath(), fh.getNewPath());
assertFalse(fh.hasMetaDataChanges());
}
@@ -129,8 +129,8 @@ public void testParseGitFileName_SrcFooC() {
final FileHeader fh = header(name);
assertEquals(gitLine(name).length(), fh.parseGitFileName(0,
fh.buf.length));
- assertEquals(name, fh.getOldName());
- assertSame(fh.getOldName(), fh.getNewName());
+ assertEquals(name, fh.getOldPath());
+ assertSame(fh.getOldPath(), fh.getNewPath());
assertFalse(fh.hasMetaDataChanges());
}
@@ -139,8 +139,8 @@ public void testParseGitFileName_SrcFooCNonStandardPrefix() {
final String header = "project-v-1.0/" + name + " mydev/" + name + "\n";
final FileHeader fh = data(header + "-");
assertEquals(header.length(), fh.parseGitFileName(0, fh.buf.length));
- assertEquals(name, fh.getOldName());
- assertSame(fh.getOldName(), fh.getNewName());
+ assertEquals(name, fh.getOldPath());
+ assertSame(fh.getOldPath(), fh.getNewPath());
assertFalse(fh.hasMetaDataChanges());
}
@@ -153,9 +153,9 @@ public void testParseUnicodeName_NewFile() {
+ "@@ -0,0 +1 @@\n" + "+a\n");
assertParse(fh);
- assertEquals("/dev/null", fh.getOldName());
- assertSame(DiffEntry.DEV_NULL, fh.getOldName());
- assertEquals("\u00c5ngstr\u00f6m", fh.getNewName());
+ assertEquals("/dev/null", fh.getOldPath());
+ assertSame(DiffEntry.DEV_NULL, fh.getOldPath());
+ assertEquals("\u00c5ngstr\u00f6m", fh.getNewPath());
assertSame(FileHeader.ChangeType.ADD, fh.getChangeType());
assertSame(FileHeader.PatchType.UNIFIED, fh.getPatchType());
@@ -178,9 +178,9 @@ public void testParseUnicodeName_DeleteFile() {
+ "@@ -1 +0,0 @@\n" + "-a\n");
assertParse(fh);
- assertEquals("\u00c5ngstr\u00f6m", fh.getOldName());
- assertEquals("/dev/null", fh.getNewName());
- assertSame(DiffEntry.DEV_NULL, fh.getNewName());
+ assertEquals("\u00c5ngstr\u00f6m", fh.getOldPath());
+ assertEquals("/dev/null", fh.getNewPath());
+ assertSame(DiffEntry.DEV_NULL, fh.getNewPath());
assertSame(FileHeader.ChangeType.DELETE, fh.getChangeType());
assertSame(FileHeader.PatchType.UNIFIED, fh.getPatchType());
@@ -198,8 +198,8 @@ public void testParseModeChange() {
final FileHeader fh = data("diff --git a/a b b/a b\n"
+ "old mode 100644\n" + "new mode 100755\n");
assertParse(fh);
- assertEquals("a b", fh.getOldName());
- assertEquals("a b", fh.getNewName());
+ assertEquals("a b", fh.getOldPath());
+ assertEquals("a b", fh.getNewPath());
assertSame(FileHeader.ChangeType.MODIFY, fh.getChangeType());
assertSame(FileHeader.PatchType.UNIFIED, fh.getPatchType());
@@ -220,14 +220,14 @@ public void testParseRename100_NewStyle() {
+ "rename to \" c/\\303\\205ngstr\\303\\266m\"\n");
int ptr = fh.parseGitFileName(0, fh.buf.length);
assertTrue(ptr > 0);
- assertNull(fh.getOldName()); // can't parse names on a rename
- assertNull(fh.getNewName());
+ assertNull(fh.getOldPath()); // can't parse names on a rename
+ assertNull(fh.getNewPath());
ptr = fh.parseGitHeaders(ptr, fh.buf.length);
assertTrue(ptr > 0);
- assertEquals("a", fh.getOldName());
- assertEquals(" c/\u00c5ngstr\u00f6m", fh.getNewName());
+ assertEquals("a", fh.getOldPath());
+ assertEquals(" c/\u00c5ngstr\u00f6m", fh.getNewPath());
assertSame(FileHeader.ChangeType.RENAME, fh.getChangeType());
assertSame(FileHeader.PatchType.UNIFIED, fh.getPatchType());
@@ -249,14 +249,14 @@ public void testParseRename100_OldStyle() {
+ "rename new \" c/\\303\\205ngstr\\303\\266m\"\n");
int ptr = fh.parseGitFileName(0, fh.buf.length);
assertTrue(ptr > 0);
- assertNull(fh.getOldName()); // can't parse names on a rename
- assertNull(fh.getNewName());
+ assertNull(fh.getOldPath()); // can't parse names on a rename
+ assertNull(fh.getNewPath());
ptr = fh.parseGitHeaders(ptr, fh.buf.length);
assertTrue(ptr > 0);
- assertEquals("a", fh.getOldName());
- assertEquals(" c/\u00c5ngstr\u00f6m", fh.getNewName());
+ assertEquals("a", fh.getOldPath());
+ assertEquals(" c/\u00c5ngstr\u00f6m", fh.getNewPath());
assertSame(FileHeader.ChangeType.RENAME, fh.getChangeType());
assertSame(FileHeader.PatchType.UNIFIED, fh.getPatchType());
@@ -278,14 +278,14 @@ public void testParseCopy100() {
+ "copy to \" c/\\303\\205ngstr\\303\\266m\"\n");
int ptr = fh.parseGitFileName(0, fh.buf.length);
assertTrue(ptr > 0);
- assertNull(fh.getOldName()); // can't parse names on a copy
- assertNull(fh.getNewName());
+ assertNull(fh.getOldPath()); // can't parse names on a copy
+ assertNull(fh.getNewPath());
ptr = fh.parseGitHeaders(ptr, fh.buf.length);
assertTrue(ptr > 0);
- assertEquals("a", fh.getOldName());
- assertEquals(" c/\u00c5ngstr\u00f6m", fh.getNewName());
+ assertEquals("a", fh.getOldPath());
+ assertEquals(" c/\u00c5ngstr\u00f6m", fh.getNewPath());
assertSame(FileHeader.ChangeType.COPY, fh.getChangeType());
assertSame(FileHeader.PatchType.UNIFIED, fh.getPatchType());
@@ -307,8 +307,8 @@ public void testParseFullIndexLine_WithMode() {
+ ".." + nid + " 100644\n" + "--- a/a\n" + "+++ b/a\n");
assertParse(fh);
- assertEquals("a", fh.getOldName());
- assertEquals("a", fh.getNewName());
+ assertEquals("a", fh.getOldPath());
+ assertEquals("a", fh.getNewPath());
assertSame(FileMode.REGULAR_FILE, fh.getOldMode());
assertSame(FileMode.REGULAR_FILE, fh.getNewMode());
@@ -331,8 +331,8 @@ public void testParseFullIndexLine_NoMode() {
+ ".." + nid + "\n" + "--- a/a\n" + "+++ b/a\n");
assertParse(fh);
- assertEquals("a", fh.getOldName());
- assertEquals("a", fh.getNewName());
+ assertEquals("a", fh.getOldPath());
+ assertEquals("a", fh.getNewPath());
assertFalse(fh.hasMetaDataChanges());
assertNull(fh.getOldMode());
@@ -357,8 +357,8 @@ public void testParseAbbrIndexLine_WithMode() {
+ " 100644\n" + "--- a/a\n" + "+++ b/a\n");
assertParse(fh);
- assertEquals("a", fh.getOldName());
- assertEquals("a", fh.getNewName());
+ assertEquals("a", fh.getOldPath());
+ assertEquals("a", fh.getNewPath());
assertSame(FileMode.REGULAR_FILE, fh.getOldMode());
assertSame(FileMode.REGULAR_FILE, fh.getNewMode());
@@ -386,8 +386,8 @@ public void testParseAbbrIndexLine_NoMode() {
+ "\n" + "--- a/a\n" + "+++ b/a\n");
assertParse(fh);
- assertEquals("a", fh.getOldName());
- assertEquals("a", fh.getNewName());
+ assertEquals("a", fh.getOldPath());
+ assertEquals("a", fh.getNewPath());
assertNull(fh.getOldMode());
assertNull(fh.getNewMode());
diff --git a/org.eclipse.jgit.test/tst/org/eclipse/jgit/patch/PatchCcTest.java b/org.eclipse.jgit.test/tst/org/eclipse/jgit/patch/PatchCcTest.java
index 1d879cb..cef13f5 100644
--- a/org.eclipse.jgit.test/tst/org/eclipse/jgit/patch/PatchCcTest.java
+++ b/org.eclipse.jgit.test/tst/org/eclipse/jgit/patch/PatchCcTest.java
@@ -60,8 +60,8 @@ public void testParse_OneFileCc() throws IOException {
final CombinedFileHeader cfh = (CombinedFileHeader) p.getFiles().get(0);
assertEquals("org.spearce.egit.ui/src/org/spearce/egit/ui/UIText.java",
- cfh.getNewName());
- assertEquals(cfh.getNewName(), cfh.getOldName());
+ cfh.getNewPath());
+ assertEquals(cfh.getNewPath(), cfh.getOldPath());
assertEquals(98, cfh.startOffset);
@@ -114,8 +114,8 @@ public void testParse_CcNewFile() throws IOException {
final CombinedFileHeader cfh = (CombinedFileHeader) p.getFiles().get(0);
- assertSame(DiffEntry.DEV_NULL, cfh.getOldName());
- assertEquals("d", cfh.getNewName());
+ assertSame(DiffEntry.DEV_NULL, cfh.getOldPath());
+ assertEquals("d", cfh.getNewPath());
assertEquals(187, cfh.startOffset);
@@ -168,8 +168,8 @@ public void testParse_CcDeleteFile() throws IOException {
final CombinedFileHeader cfh = (CombinedFileHeader) p.getFiles().get(0);
- assertEquals("a", cfh.getOldName());
- assertSame(DiffEntry.DEV_NULL, cfh.getNewName());
+ assertEquals("a", cfh.getOldPath());
+ assertSame(DiffEntry.DEV_NULL, cfh.getNewPath());
assertEquals(187, cfh.startOffset);
diff --git a/org.eclipse.jgit.test/tst/org/eclipse/jgit/patch/PatchErrorTest.java b/org.eclipse.jgit.test/tst/org/eclipse/jgit/patch/PatchErrorTest.java
index 62a1071..67b3f5c 100644
--- a/org.eclipse.jgit.test/tst/org/eclipse/jgit/patch/PatchErrorTest.java
+++ b/org.eclipse.jgit.test/tst/org/eclipse/jgit/patch/PatchErrorTest.java
@@ -56,7 +56,7 @@ public void testError_DisconnectedHunk() throws IOException {
final FileHeader fh = p.getFiles().get(0);
assertEquals(
"org.eclipse.jgit/src/org/spearce/jgit/lib/RepositoryConfig.java",
- fh.getNewName());
+ fh.getNewPath());
assertEquals(1, fh.getHunks().size());
}
@@ -114,14 +114,14 @@ public void testError_GarbageBetweenFiles() throws IOException {
final FileHeader fh = p.getFiles().get(0);
assertEquals(
"org.eclipse.jgit.test/tst/org/spearce/jgit/lib/RepositoryConfigTest.java",
- fh.getNewName());
+ fh.getNewPath());
assertEquals(1, fh.getHunks().size());
}
{
final FileHeader fh = p.getFiles().get(1);
assertEquals(
"org.eclipse.jgit/src/org/spearce/jgit/lib/RepositoryConfig.java",
- fh.getNewName());
+ fh.getNewPath());
assertEquals(1, fh.getHunks().size());
}
@@ -139,7 +139,7 @@ public void testError_GitBinaryNoForwardHunk() throws IOException {
{
final FileHeader fh = p.getFiles().get(0);
assertEquals("org.spearce.egit.ui/icons/toolbar/fetchd.png", fh
- .getNewName());
+ .getNewPath());
assertSame(FileHeader.PatchType.GIT_BINARY, fh.getPatchType());
assertTrue(fh.getHunks().isEmpty());
assertNull(fh.getForwardBinaryHunk());
@@ -147,7 +147,7 @@ public void testError_GitBinaryNoForwardHunk() throws IOException {
{
final FileHeader fh = p.getFiles().get(1);
assertEquals("org.spearce.egit.ui/icons/toolbar/fetche.png", fh
- .getNewName());
+ .getNewPath());
assertSame(FileHeader.PatchType.UNIFIED, fh.getPatchType());
assertTrue(fh.getHunks().isEmpty());
assertNull(fh.getForwardBinaryHunk());
diff --git a/org.eclipse.jgit.test/tst/org/eclipse/jgit/patch/PatchTest.java b/org.eclipse.jgit.test/tst/org/eclipse/jgit/patch/PatchTest.java
index 52d6e27..dd76251 100644
--- a/org.eclipse.jgit.test/tst/org/eclipse/jgit/patch/PatchTest.java
+++ b/org.eclipse.jgit.test/tst/org/eclipse/jgit/patch/PatchTest.java
@@ -68,11 +68,11 @@ public void testParse_ConfigCaseInsensitive() throws IOException {
assertEquals(
"org.eclipse.jgit.test/tst/org/spearce/jgit/lib/RepositoryConfigTest.java",
- fRepositoryConfigTest.getNewName());
+ fRepositoryConfigTest.getNewPath());
assertEquals(
"org.eclipse.jgit/src/org/spearce/jgit/lib/RepositoryConfig.java",
- fRepositoryConfig.getNewName());
+ fRepositoryConfig.getNewPath());
assertEquals(572, fRepositoryConfigTest.startOffset);
assertEquals(1490, fRepositoryConfig.startOffset);
@@ -168,7 +168,7 @@ public void testParse_NoBinary() throws IOException {
assertEquals("0000000", fh.getOldId().name());
assertSame(FileMode.MISSING, fh.getOldMode());
assertSame(FileMode.REGULAR_FILE, fh.getNewMode());
- assertTrue(fh.getNewName().startsWith(
+ assertTrue(fh.getNewPath().startsWith(
"org.spearce.egit.ui/icons/toolbar/"));
assertSame(FileHeader.PatchType.BINARY, fh.getPatchType());
assertTrue(fh.getHunks().isEmpty());
@@ -179,7 +179,7 @@ public void testParse_NoBinary() throws IOException {
}
final FileHeader fh = p.getFiles().get(4);
- assertEquals("org.spearce.egit.ui/plugin.xml", fh.getNewName());
+ assertEquals("org.spearce.egit.ui/plugin.xml", fh.getNewPath());
assertSame(FileHeader.ChangeType.MODIFY, fh.getChangeType());
assertSame(FileHeader.PatchType.UNIFIED, fh.getPatchType());
assertFalse(fh.hasMetaDataChanges());
@@ -203,7 +203,7 @@ public void testParse_GitBinaryLiteral() throws IOException {
assertNotNull(fh.getNewId());
assertEquals(ObjectId.zeroId().name(), fh.getOldId().name());
assertSame(FileMode.REGULAR_FILE, fh.getNewMode());
- assertTrue(fh.getNewName().startsWith(
+ assertTrue(fh.getNewPath().startsWith(
"org.spearce.egit.ui/icons/toolbar/"));
assertSame(FileHeader.PatchType.GIT_BINARY, fh.getPatchType());
assertTrue(fh.getHunks().isEmpty());
@@ -224,7 +224,7 @@ public void testParse_GitBinaryLiteral() throws IOException {
}
final FileHeader fh = p.getFiles().get(4);
- assertEquals("org.spearce.egit.ui/plugin.xml", fh.getNewName());
+ assertEquals("org.spearce.egit.ui/plugin.xml", fh.getNewPath());
assertSame(FileHeader.ChangeType.MODIFY, fh.getChangeType());
assertSame(FileHeader.PatchType.UNIFIED, fh.getPatchType());
assertFalse(fh.hasMetaDataChanges());
@@ -241,7 +241,7 @@ public void testParse_GitBinaryDelta() throws IOException {
assertTrue(p.getErrors().isEmpty());
final FileHeader fh = p.getFiles().get(0);
- assertTrue(fh.getNewName().startsWith("zero.bin"));
+ assertTrue(fh.getNewPath().startsWith("zero.bin"));
assertSame(FileHeader.ChangeType.MODIFY, fh.getChangeType());
assertSame(FileHeader.PatchType.GIT_BINARY, fh.getPatchType());
assertSame(FileMode.REGULAR_FILE, fh.getNewMode());
@@ -279,7 +279,7 @@ public void testParse_FixNoNewline() throws IOException {
final FileHeader f = p.getFiles().get(0);
- assertEquals("a", f.getNewName());
+ assertEquals("a", f.getNewPath());
assertEquals(252, f.startOffset);
assertEquals("2e65efe", f.getOldId().name());
@@ -313,7 +313,7 @@ public void testParse_AddNoNewline() throws IOException {
final FileHeader f = p.getFiles().get(0);
- assertEquals("a", f.getNewName());
+ assertEquals("a", f.getNewPath());
assertEquals(256, f.startOffset);
assertEquals("f2ad6c7", f.getOldId().name());
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 6405232..9473fe6 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 org.eclipse.jgit.dircache.DirCacheEntry;
import org.eclipse.jgit.junit.TestRepository;
+import org.eclipse.jgit.lib.Repository;
import org.eclipse.jgit.lib.RepositoryTestCase;
/** Support for tests of the {@link RevWalk} class. */
public abstract class RevWalkTestCase extends RepositoryTestCase {
- private TestRepository util;
+ private TestRepository<Repository> util;
protected RevWalk rw;
@Override
public void setUp() throws Exception {
super.setUp();
- util = new TestRepository(db, createRevWalk());
+ util = new TestRepository<Repository>(db, createRevWalk());
rw = util.getRevWalk();
}
diff --git a/org.eclipse.jgit.test/tst/org/eclipse/jgit/lib/ConcurrentRepackTest.java b/org.eclipse.jgit.test/tst/org/eclipse/jgit/storage/file/ConcurrentRepackTest.java
similarity index 90%
rename from org.eclipse.jgit.test/tst/org/eclipse/jgit/lib/ConcurrentRepackTest.java
rename to org.eclipse.jgit.test/tst/org/eclipse/jgit/storage/file/ConcurrentRepackTest.java
index 69430ed..c8f2aad 100644
--- a/org.eclipse.jgit.test/tst/org/eclipse/jgit/lib/ConcurrentRepackTest.java
+++ b/org.eclipse.jgit.test/tst/org/eclipse/jgit/storage/file/ConcurrentRepackTest.java
@@ -42,7 +42,7 @@
* ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
*/
-package org.eclipse.jgit.lib;
+package org.eclipse.jgit.storage.file;
import java.io.BufferedOutputStream;
import java.io.File;
@@ -53,8 +53,17 @@
import org.eclipse.jgit.errors.IncorrectObjectTypeException;
import org.eclipse.jgit.errors.MissingObjectException;
+import org.eclipse.jgit.lib.AnyObjectId;
+import org.eclipse.jgit.lib.Constants;
+import org.eclipse.jgit.lib.NullProgressMonitor;
+import org.eclipse.jgit.lib.ObjectId;
+import org.eclipse.jgit.lib.ObjectLoader;
+import org.eclipse.jgit.lib.ObjectWriter;
+import org.eclipse.jgit.lib.Repository;
+import org.eclipse.jgit.lib.RepositoryTestCase;
import org.eclipse.jgit.revwalk.RevObject;
import org.eclipse.jgit.revwalk.RevWalk;
+import org.eclipse.jgit.storage.pack.PackWriter;
public class ConcurrentRepackTest extends RepositoryTestCase {
public void setUp() throws Exception {
@@ -125,10 +134,11 @@ public void testObjectMovedWithinPack()
// within the pack has been modified.
//
final RevObject o2 = writeBlob(eden, "o2");
- final PackWriter pw = new PackWriter(eden, NullProgressMonitor.INSTANCE);
+ final PackWriter pw = new PackWriter(eden);
pw.addObject(o2);
pw.addObject(o1);
write(out1, pw);
+ pw.release();
// Try the old name, then the new name. The old name should cause the
// pack to reload when it opens and the index and pack mismatch.
@@ -148,7 +158,7 @@ public void testObjectMovedToNewPack2()
final File[] out1 = pack(eden, o1);
assertEquals(o1.name(), parse(o1).name());
- final ObjectLoader load1 = db.openBlob(o1);
+ final ObjectLoader load1 = db.open(o1, Constants.OBJ_BLOB);
assertNotNull(load1);
final RevObject o2 = writeBlob(eden, "o2");
@@ -163,7 +173,7 @@ public void testObjectMovedToNewPack2()
// earlier still resolve the object, even though its underlying
// pack is gone, but the object still exists.
//
- final ObjectLoader load2 = db.openBlob(o1);
+ final ObjectLoader load2 = db.open(o1, Constants.OBJ_BLOB);
assertNotNull(load2);
assertNotSame(load1, load2);
@@ -189,7 +199,7 @@ private RevObject parse(final AnyObjectId id)
private File[] pack(final Repository src, final RevObject... list)
throws IOException {
- final PackWriter pw = new PackWriter(src, NullProgressMonitor.INSTANCE);
+ final PackWriter pw = new PackWriter(src);
for (final RevObject o : list) {
pw.addObject(o);
}
@@ -199,17 +209,19 @@ private RevObject parse(final AnyObjectId id)
final File idxFile = fullPackFileName(name, ".idx");
final File[] files = new File[] { packFile, idxFile };
write(files, pw);
+ pw.release();
return files;
}
private static void write(final File[] files, final PackWriter pw)
throws IOException {
final long begin = files[0].getParentFile().lastModified();
+ NullProgressMonitor m = NullProgressMonitor.INSTANCE;
OutputStream out;
out = new BufferedOutputStream(new FileOutputStream(files[0]));
try {
- pw.writePack(out);
+ pw.writePack(m, m, out);
} finally {
out.close();
}
@@ -245,7 +257,7 @@ private static void touch(final long begin, final File dir) {
}
private File fullPackFileName(final ObjectId name, final String suffix) {
- final File packdir = new File(db.getObjectsDirectory(), "pack");
+ final File packdir = new File(db.getObjectDatabase().getDirectory(), "pack");
return new File(packdir, "pack-" + name.name() + suffix);
}
diff --git a/org.eclipse.jgit.test/tst/org/eclipse/jgit/storage/file/PackFileTest.java b/org.eclipse.jgit.test/tst/org/eclipse/jgit/storage/file/PackFileTest.java
new file mode 100644
index 0000000..1a40b8e
--- /dev/null
+++ b/org.eclipse.jgit.test/tst/org/eclipse/jgit/storage/file/PackFileTest.java
@@ -0,0 +1,387 @@
+/*
+ * Copyright (C) 2010, Google Inc.
+ * and other copyright owners as documented in the project's IP log.
+ *
+ * This program and the accompanying materials are made available
+ * under the terms of the Eclipse Distribution License v1.0 which
+ * accompanies this distribution, is reproduced below, and is
+ * available at http://www.eclipse.org/org/documents/edl-v10.php
+ *
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or
+ * without modification, are permitted provided that the following
+ * conditions are met:
+ *
+ * - Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ *
+ * - Redistributions in binary form must reproduce the above
+ * copyright notice, this list of conditions and the following
+ * disclaimer in the documentation and/or other materials provided
+ * with the distribution.
+ *
+ * - Neither the name of the Eclipse Foundation, Inc. nor the
+ * names of its contributors may be used to endorse or promote
+ * products derived from this software without specific prior
+ * written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND
+ * CONTRIBUTORS "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES,
+ * INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
+ * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR
+ * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
+ * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT
+ * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
+ * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER
+ * CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT,
+ * STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
+ * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF
+ * ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ */
+
+package org.eclipse.jgit.storage.file;
+
+import java.io.ByteArrayInputStream;
+import java.io.ByteArrayOutputStream;
+import java.io.IOException;
+import java.security.MessageDigest;
+import java.util.Arrays;
+import java.util.zip.Deflater;
+
+import org.eclipse.jgit.errors.LargeObjectException;
+import org.eclipse.jgit.junit.LocalDiskRepositoryTestCase;
+import org.eclipse.jgit.junit.TestRepository;
+import org.eclipse.jgit.junit.TestRng;
+import org.eclipse.jgit.lib.Constants;
+import org.eclipse.jgit.lib.NullProgressMonitor;
+import org.eclipse.jgit.lib.ObjectId;
+import org.eclipse.jgit.lib.ObjectInserter;
+import org.eclipse.jgit.lib.ObjectLoader;
+import org.eclipse.jgit.lib.ObjectStream;
+import org.eclipse.jgit.revwalk.RevBlob;
+import org.eclipse.jgit.storage.pack.DeltaEncoder;
+import org.eclipse.jgit.transport.IndexPack;
+import org.eclipse.jgit.util.IO;
+import org.eclipse.jgit.util.NB;
+import org.eclipse.jgit.util.TemporaryBuffer;
+
+public class PackFileTest extends LocalDiskRepositoryTestCase {
+ private TestRng rng;
+
+ private FileRepository repo;
+
+ private TestRepository<FileRepository> tr;
+
+ private WindowCursor wc;
+
+ protected void setUp() throws Exception {
+ super.setUp();
+ rng = new TestRng(getName());
+ repo = createBareRepository();
+ tr = new TestRepository<FileRepository>(repo);
+ wc = (WindowCursor) repo.newObjectReader();
+ }
+
+ protected void tearDown() throws Exception {
+ if (wc != null)
+ wc.release();
+ super.tearDown();
+ }
+
+ public void testWhole_SmallObject() throws Exception {
+ final int type = Constants.OBJ_BLOB;
+ byte[] data = rng.nextBytes(300);
+ RevBlob id = tr.blob(data);
+ tr.branch("master").commit().add("A", id).create();
+ tr.packAndPrune();
+ assertTrue("has blob", wc.has(id));
+
+ ObjectLoader ol = wc.open(id);
+ assertNotNull("created loader", ol);
+ assertEquals(type, ol.getType());
+ assertEquals(data.length, ol.getSize());
+ assertFalse("is not large", ol.isLarge());
+ assertTrue("same content", Arrays.equals(data, ol.getCachedBytes()));
+
+ ObjectStream in = ol.openStream();
+ assertNotNull("have stream", in);
+ assertEquals(type, in.getType());
+ assertEquals(data.length, in.getSize());
+ byte[] data2 = new byte[data.length];
+ IO.readFully(in, data2, 0, data.length);
+ assertTrue("same content", Arrays.equals(data2, data));
+ assertEquals("stream at EOF", -1, in.read());
+ in.close();
+ }
+
+ public void testWhole_LargeObject() throws Exception {
+ final int type = Constants.OBJ_BLOB;
+ byte[] data = rng.nextBytes(ObjectLoader.STREAM_THRESHOLD + 5);
+ RevBlob id = tr.blob(data);
+ tr.branch("master").commit().add("A", id).create();
+ tr.packAndPrune();
+ assertTrue("has blob", wc.has(id));
+
+ ObjectLoader ol = wc.open(id);
+ assertNotNull("created loader", ol);
+ assertEquals(type, ol.getType());
+ assertEquals(data.length, ol.getSize());
+ assertTrue("is large", ol.isLarge());
+ try {
+ ol.getCachedBytes();
+ fail("Should have thrown LargeObjectException");
+ } catch (LargeObjectException tooBig) {
+ assertEquals(id.name(), tooBig.getMessage());
+ }
+
+ ObjectStream in = ol.openStream();
+ assertNotNull("have stream", in);
+ assertEquals(type, in.getType());
+ assertEquals(data.length, in.getSize());
+ byte[] data2 = new byte[data.length];
+ IO.readFully(in, data2, 0, data.length);
+ assertTrue("same content", Arrays.equals(data2, data));
+ assertEquals("stream at EOF", -1, in.read());
+ in.close();
+ }
+
+ public void testDelta_SmallObjectChain() throws Exception {
+ ObjectInserter.Formatter fmt = new ObjectInserter.Formatter();
+ byte[] data0 = new byte[512];
+ Arrays.fill(data0, (byte) 0xf3);
+ ObjectId id0 = fmt.idFor(Constants.OBJ_BLOB, data0);
+
+ TemporaryBuffer.Heap pack = new TemporaryBuffer.Heap(64 * 1024);
+ packHeader(pack, 4);
+ objectHeader(pack, Constants.OBJ_BLOB, data0.length);
+ deflate(pack, data0);
+
+ byte[] data1 = clone(0x01, data0);
+ byte[] delta1 = delta(data0, data1);
+ ObjectId id1 = fmt.idFor(Constants.OBJ_BLOB, data1);
+ objectHeader(pack, Constants.OBJ_REF_DELTA, delta1.length);
+ id0.copyRawTo(pack);
+ deflate(pack, delta1);
+
+ byte[] data2 = clone(0x02, data1);
+ byte[] delta2 = delta(data1, data2);
+ ObjectId id2 = fmt.idFor(Constants.OBJ_BLOB, data2);
+ objectHeader(pack, Constants.OBJ_REF_DELTA, delta2.length);
+ id1.copyRawTo(pack);
+ deflate(pack, delta2);
+
+ byte[] data3 = clone(0x03, data2);
+ byte[] delta3 = delta(data2, data3);
+ ObjectId id3 = fmt.idFor(Constants.OBJ_BLOB, data3);
+ objectHeader(pack, Constants.OBJ_REF_DELTA, delta3.length);
+ id2.copyRawTo(pack);
+ deflate(pack, delta3);
+
+ digest(pack);
+ final byte[] raw = pack.toByteArray();
+ IndexPack ip = IndexPack.create(repo, new ByteArrayInputStream(raw));
+ ip.setFixThin(true);
+ ip.index(NullProgressMonitor.INSTANCE);
+ ip.renameAndOpenPack();
+
+ assertTrue("has blob", wc.has(id3));
+
+ ObjectLoader ol = wc.open(id3);
+ assertNotNull("created loader", ol);
+ assertEquals(Constants.OBJ_BLOB, ol.getType());
+ assertEquals(data3.length, ol.getSize());
+ assertFalse("is large", ol.isLarge());
+ assertNotNull(ol.getCachedBytes());
+ assertTrue(Arrays.equals(data3, ol.getCachedBytes()));
+
+ ObjectStream in = ol.openStream();
+ assertNotNull("have stream", in);
+ assertEquals(Constants.OBJ_BLOB, in.getType());
+ assertEquals(data3.length, in.getSize());
+ byte[] act = new byte[data3.length];
+ IO.readFully(in, act, 0, data3.length);
+ assertTrue("same content", Arrays.equals(act, data3));
+ assertEquals("stream at EOF", -1, in.read());
+ in.close();
+ }
+
+ public void testDelta_LargeObjectChain() throws Exception {
+ ObjectInserter.Formatter fmt = new ObjectInserter.Formatter();
+ byte[] data0 = new byte[ObjectLoader.STREAM_THRESHOLD + 5];
+ Arrays.fill(data0, (byte) 0xf3);
+ ObjectId id0 = fmt.idFor(Constants.OBJ_BLOB, data0);
+
+ TemporaryBuffer.Heap pack = new TemporaryBuffer.Heap(64 * 1024);
+ packHeader(pack, 4);
+ objectHeader(pack, Constants.OBJ_BLOB, data0.length);
+ deflate(pack, data0);
+
+ byte[] data1 = clone(0x01, data0);
+ byte[] delta1 = delta(data0, data1);
+ ObjectId id1 = fmt.idFor(Constants.OBJ_BLOB, data1);
+ objectHeader(pack, Constants.OBJ_REF_DELTA, delta1.length);
+ id0.copyRawTo(pack);
+ deflate(pack, delta1);
+
+ byte[] data2 = clone(0x02, data1);
+ byte[] delta2 = delta(data1, data2);
+ ObjectId id2 = fmt.idFor(Constants.OBJ_BLOB, data2);
+ objectHeader(pack, Constants.OBJ_REF_DELTA, delta2.length);
+ id1.copyRawTo(pack);
+ deflate(pack, delta2);
+
+ byte[] data3 = clone(0x03, data2);
+ byte[] delta3 = delta(data2, data3);
+ ObjectId id3 = fmt.idFor(Constants.OBJ_BLOB, data3);
+ objectHeader(pack, Constants.OBJ_REF_DELTA, delta3.length);
+ id2.copyRawTo(pack);
+ deflate(pack, delta3);
+
+ digest(pack);
+ final byte[] raw = pack.toByteArray();
+ IndexPack ip = IndexPack.create(repo, new ByteArrayInputStream(raw));
+ ip.setFixThin(true);
+ ip.index(NullProgressMonitor.INSTANCE);
+ ip.renameAndOpenPack();
+
+ assertTrue("has blob", wc.has(id3));
+
+ ObjectLoader ol = wc.open(id3);
+ assertNotNull("created loader", ol);
+ assertEquals(Constants.OBJ_BLOB, ol.getType());
+ assertEquals(data3.length, ol.getSize());
+ assertTrue("is large", ol.isLarge());
+ try {
+ ol.getCachedBytes();
+ fail("Should have thrown LargeObjectException");
+ } catch (LargeObjectException tooBig) {
+ assertEquals(id3.name(), tooBig.getMessage());
+ }
+
+ ObjectStream in = ol.openStream();
+ assertNotNull("have stream", in);
+ assertEquals(Constants.OBJ_BLOB, in.getType());
+ assertEquals(data3.length, in.getSize());
+ byte[] act = new byte[data3.length];
+ IO.readFully(in, act, 0, data3.length);
+ assertTrue("same content", Arrays.equals(act, data3));
+ assertEquals("stream at EOF", -1, in.read());
+ in.close();
+ }
+
+ public void testDelta_LargeInstructionStream() throws Exception {
+ ObjectInserter.Formatter fmt = new ObjectInserter.Formatter();
+ byte[] data0 = new byte[32];
+ Arrays.fill(data0, (byte) 0xf3);
+ ObjectId id0 = fmt.idFor(Constants.OBJ_BLOB, data0);
+
+ byte[] data3 = rng.nextBytes(ObjectLoader.STREAM_THRESHOLD + 5);
+ ByteArrayOutputStream tmp = new ByteArrayOutputStream();
+ DeltaEncoder de = new DeltaEncoder(tmp, data0.length, data3.length);
+ de.insert(data3, 0, data3.length);
+ byte[] delta3 = tmp.toByteArray();
+ assertTrue(delta3.length > ObjectLoader.STREAM_THRESHOLD);
+
+ TemporaryBuffer.Heap pack = new TemporaryBuffer.Heap(64 * 1024);
+ packHeader(pack, 2);
+ objectHeader(pack, Constants.OBJ_BLOB, data0.length);
+ deflate(pack, data0);
+
+ ObjectId id3 = fmt.idFor(Constants.OBJ_BLOB, data3);
+ objectHeader(pack, Constants.OBJ_REF_DELTA, delta3.length);
+ id0.copyRawTo(pack);
+ deflate(pack, delta3);
+
+ digest(pack);
+ final byte[] raw = pack.toByteArray();
+ IndexPack ip = IndexPack.create(repo, new ByteArrayInputStream(raw));
+ ip.setFixThin(true);
+ ip.index(NullProgressMonitor.INSTANCE);
+ ip.renameAndOpenPack();
+
+ assertTrue("has blob", wc.has(id3));
+
+ ObjectLoader ol = wc.open(id3);
+ assertNotNull("created loader", ol);
+ assertEquals(Constants.OBJ_BLOB, ol.getType());
+ assertEquals(data3.length, ol.getSize());
+ assertTrue("is large", ol.isLarge());
+ try {
+ ol.getCachedBytes();
+ fail("Should have thrown LargeObjectException");
+ } catch (LargeObjectException tooBig) {
+ assertEquals(id3.name(), tooBig.getMessage());
+ }
+
+ ObjectStream in = ol.openStream();
+ assertNotNull("have stream", in);
+ assertEquals(Constants.OBJ_BLOB, in.getType());
+ assertEquals(data3.length, in.getSize());
+ byte[] act = new byte[data3.length];
+ IO.readFully(in, act, 0, data3.length);
+ assertTrue("same content", Arrays.equals(act, data3));
+ assertEquals("stream at EOF", -1, in.read());
+ in.close();
+ }
+
+ private byte[] clone(int first, byte[] base) {
+ byte[] r = new byte[base.length];
+ System.arraycopy(base, 1, r, 1, r.length - 1);
+ r[0] = (byte) first;
+ return r;
+ }
+
+ private byte[] delta(byte[] base, byte[] dest) throws IOException {
+ ByteArrayOutputStream tmp = new ByteArrayOutputStream();
+ DeltaEncoder de = new DeltaEncoder(tmp, base.length, dest.length);
+ de.insert(dest, 0, 1);
+ de.copy(1, base.length - 1);
+ return tmp.toByteArray();
+ }
+
+ private void packHeader(TemporaryBuffer.Heap pack, int cnt)
+ throws IOException {
+ final byte[] hdr = new byte[8];
+ NB.encodeInt32(hdr, 0, 2);
+ NB.encodeInt32(hdr, 4, cnt);
+ pack.write(Constants.PACK_SIGNATURE);
+ pack.write(hdr, 0, 8);
+ }
+
+ private void objectHeader(TemporaryBuffer.Heap pack, int type, int sz)
+ throws IOException {
+ byte[] buf = new byte[8];
+ int nextLength = sz >>> 4;
+ buf[0] = (byte) ((nextLength > 0 ? 0x80 : 0x00) | (type << 4) | (sz & 0x0F));
+ sz = nextLength;
+ int n = 1;
+ while (sz > 0) {
+ nextLength >>>= 7;
+ buf[n++] = (byte) ((nextLength > 0 ? 0x80 : 0x00) | (sz & 0x7F));
+ sz = nextLength;
+ }
+ pack.write(buf, 0, n);
+ }
+
+ private void deflate(TemporaryBuffer.Heap pack, final byte[] content)
+ throws IOException {
+ final Deflater deflater = new Deflater();
+ final byte[] buf = new byte[128];
+ deflater.setInput(content, 0, content.length);
+ deflater.finish();
+ do {
+ final int n = deflater.deflate(buf, 0, buf.length);
+ if (n > 0)
+ pack.write(buf, 0, n);
+ } while (!deflater.finished());
+ deflater.end();
+ }
+
+ private void digest(TemporaryBuffer.Heap buf) throws IOException {
+ MessageDigest md = Constants.newMessageDigest();
+ md.update(buf.toByteArray());
+ buf.write(md.digest());
+ }
+}
diff --git a/org.eclipse.jgit.test/tst/org/eclipse/jgit/lib/PackIndexTestCase.java b/org.eclipse.jgit.test/tst/org/eclipse/jgit/storage/file/PackIndexTestCase.java
similarity index 97%
rename from org.eclipse.jgit.test/tst/org/eclipse/jgit/lib/PackIndexTestCase.java
rename to org.eclipse.jgit.test/tst/org/eclipse/jgit/storage/file/PackIndexTestCase.java
index d1c5c5e..9884142 100644
--- a/org.eclipse.jgit.test/tst/org/eclipse/jgit/lib/PackIndexTestCase.java
+++ b/org.eclipse.jgit.test/tst/org/eclipse/jgit/storage/file/PackIndexTestCase.java
@@ -41,14 +41,15 @@
* ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
*/
-package org.eclipse.jgit.lib;
+package org.eclipse.jgit.storage.file;
import java.io.File;
import java.util.Iterator;
import java.util.NoSuchElementException;
import org.eclipse.jgit.errors.MissingObjectException;
-import org.eclipse.jgit.lib.PackIndex.MutableEntry;
+import org.eclipse.jgit.lib.RepositoryTestCase;
+import org.eclipse.jgit.storage.file.PackIndex.MutableEntry;
public abstract class PackIndexTestCase extends RepositoryTestCase {
diff --git a/org.eclipse.jgit.test/tst/org/eclipse/jgit/lib/PackIndexV1Test.java b/org.eclipse.jgit.test/tst/org/eclipse/jgit/storage/file/PackIndexV1Test.java
similarity index 97%
rename from org.eclipse.jgit.test/tst/org/eclipse/jgit/lib/PackIndexV1Test.java
rename to org.eclipse.jgit.test/tst/org/eclipse/jgit/storage/file/PackIndexV1Test.java
index f3082fb..303eeff 100644
--- a/org.eclipse.jgit.test/tst/org/eclipse/jgit/lib/PackIndexV1Test.java
+++ b/org.eclipse.jgit.test/tst/org/eclipse/jgit/storage/file/PackIndexV1Test.java
@@ -43,11 +43,12 @@
* ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
*/
-package org.eclipse.jgit.lib;
+package org.eclipse.jgit.storage.file;
import java.io.File;
import org.eclipse.jgit.errors.MissingObjectException;
+import org.eclipse.jgit.lib.ObjectId;
import org.eclipse.jgit.util.JGitTestUtil;
public class PackIndexV1Test extends PackIndexTestCase {
diff --git a/org.eclipse.jgit.test/tst/org/eclipse/jgit/lib/PackIndexV2Test.java b/org.eclipse.jgit.test/tst/org/eclipse/jgit/storage/file/PackIndexV2Test.java
similarity index 97%
rename from org.eclipse.jgit.test/tst/org/eclipse/jgit/lib/PackIndexV2Test.java
rename to org.eclipse.jgit.test/tst/org/eclipse/jgit/storage/file/PackIndexV2Test.java
index c5669f9..2d3ec7b 100644
--- a/org.eclipse.jgit.test/tst/org/eclipse/jgit/lib/PackIndexV2Test.java
+++ b/org.eclipse.jgit.test/tst/org/eclipse/jgit/storage/file/PackIndexV2Test.java
@@ -43,11 +43,12 @@
* ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
*/
-package org.eclipse.jgit.lib;
+package org.eclipse.jgit.storage.file;
import java.io.File;
import org.eclipse.jgit.errors.MissingObjectException;
+import org.eclipse.jgit.lib.ObjectId;
import org.eclipse.jgit.util.JGitTestUtil;
public class PackIndexV2Test extends PackIndexTestCase {
diff --git a/org.eclipse.jgit.test/tst/org/eclipse/jgit/lib/PackReverseIndexTest.java b/org.eclipse.jgit.test/tst/org/eclipse/jgit/storage/file/PackReverseIndexTest.java
similarity index 96%
rename from org.eclipse.jgit.test/tst/org/eclipse/jgit/lib/PackReverseIndexTest.java
rename to org.eclipse.jgit.test/tst/org/eclipse/jgit/storage/file/PackReverseIndexTest.java
index 19b7058..07a40a4 100644
--- a/org.eclipse.jgit.test/tst/org/eclipse/jgit/lib/PackReverseIndexTest.java
+++ b/org.eclipse.jgit.test/tst/org/eclipse/jgit/storage/file/PackReverseIndexTest.java
@@ -42,10 +42,11 @@
* ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
*/
-package org.eclipse.jgit.lib;
+package org.eclipse.jgit.storage.file;
import org.eclipse.jgit.errors.CorruptObjectException;
-import org.eclipse.jgit.lib.PackIndex.MutableEntry;
+import org.eclipse.jgit.lib.RepositoryTestCase;
+import org.eclipse.jgit.storage.file.PackIndex.MutableEntry;
import org.eclipse.jgit.util.JGitTestUtil;
public class PackReverseIndexTest extends RepositoryTestCase {
diff --git a/org.eclipse.jgit.test/tst/org/eclipse/jgit/lib/PackWriterTest.java b/org.eclipse.jgit.test/tst/org/eclipse/jgit/storage/file/PackWriterTest.java
similarity index 90%
rename from org.eclipse.jgit.test/tst/org/eclipse/jgit/lib/PackWriterTest.java
rename to org.eclipse.jgit.test/tst/org/eclipse/jgit/storage/file/PackWriterTest.java
index 76b663a..5685cca 100644
--- a/org.eclipse.jgit.test/tst/org/eclipse/jgit/lib/PackWriterTest.java
+++ b/org.eclipse.jgit.test/tst/org/eclipse/jgit/storage/file/PackWriterTest.java
@@ -41,7 +41,7 @@
* ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
*/
-package org.eclipse.jgit.lib;
+package org.eclipse.jgit.storage.file;
import java.io.ByteArrayInputStream;
import java.io.ByteArrayOutputStream;
@@ -59,9 +59,15 @@
import java.util.List;
import org.eclipse.jgit.errors.MissingObjectException;
-import org.eclipse.jgit.lib.PackIndex.MutableEntry;
+import org.eclipse.jgit.lib.NullProgressMonitor;
+import org.eclipse.jgit.lib.ObjectId;
+import org.eclipse.jgit.lib.SampleDataRepositoryTestCase;
+import org.eclipse.jgit.lib.TextProgressMonitor;
import org.eclipse.jgit.revwalk.RevObject;
import org.eclipse.jgit.revwalk.RevWalk;
+import org.eclipse.jgit.storage.file.PackIndex.MutableEntry;
+import org.eclipse.jgit.storage.pack.PackConfig;
+import org.eclipse.jgit.storage.pack.PackWriter;
import org.eclipse.jgit.transport.IndexPack;
import org.eclipse.jgit.util.JGitTestUtil;
@@ -73,6 +79,8 @@ public class PackWriterTest extends SampleDataRepositoryTestCase {
private static final List<RevObject> EMPTY_LIST_REVS = Collections
.<RevObject> emptyList();
+ private PackConfig config;
+
private PackWriter writer;
private ByteArrayOutputStream os;
@@ -91,16 +99,23 @@ public void setUp() throws Exception {
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());
+ config = new PackConfig(db);
+ }
+
+ public void tearDown() throws Exception {
+ if (writer != null)
+ writer.release();
+ super.tearDown();
}
/**
* Test constructor for exceptions, default settings, initialization.
*/
public void testContructor() {
+ writer = new PackWriter(config, db.newObjectReader());
assertEquals(false, writer.isDeltaBaseAsOffset());
- assertEquals(true, writer.isReuseDeltas());
- assertEquals(true, writer.isReuseObjects());
+ assertEquals(true, config.isReuseDeltas());
+ assertEquals(true, config.isReuseObjects());
assertEquals(0, writer.getObjectsNumber());
}
@@ -108,13 +123,17 @@ public void testContructor() {
* Change default settings and verify them.
*/
public void testModifySettings() {
- writer.setDeltaBaseAsOffset(true);
- writer.setReuseDeltas(false);
- writer.setReuseObjects(false);
+ config.setReuseDeltas(false);
+ config.setReuseObjects(false);
+ config.setDeltaBaseAsOffset(false);
+ assertEquals(false, config.isReuseDeltas());
+ assertEquals(false, config.isReuseObjects());
+ assertEquals(false, config.isDeltaBaseAsOffset());
+ writer = new PackWriter(config, db.newObjectReader());
+ writer.setDeltaBaseAsOffset(true);
assertEquals(true, writer.isDeltaBaseAsOffset());
- assertEquals(false, writer.isReuseDeltas());
- assertEquals(false, writer.isReuseObjects());
+ assertEquals(false, config.isDeltaBaseAsOffset());
}
/**
@@ -183,7 +202,7 @@ public void testIgnoreNonExistingObjects() throws IOException {
* @throws IOException
*/
public void testWritePack1() throws IOException {
- writer.setReuseDeltas(false);
+ config.setReuseDeltas(false);
writeVerifyPack1();
}
@@ -194,8 +213,8 @@ public void testWritePack1() throws IOException {
* @throws IOException
*/
public void testWritePack1NoObjectReuse() throws IOException {
- writer.setReuseDeltas(false);
- writer.setReuseObjects(false);
+ config.setReuseDeltas(false);
+ config.setReuseObjects(false);
writeVerifyPack1();
}
@@ -226,7 +245,7 @@ public void testWritePack2DeltasReuseRefs() throws IOException {
* @throws IOException
*/
public void testWritePack2DeltasReuseOffsets() throws IOException {
- writer.setDeltaBaseAsOffset(true);
+ config.setDeltaBaseAsOffset(true);
writeVerifyPack2(true);
}
@@ -238,7 +257,7 @@ public void testWritePack2DeltasReuseOffsets() throws IOException {
* @throws IOException
*/
public void testWritePack2DeltasCRC32Copy() throws IOException {
- final File packDir = new File(db.getObjectsDirectory(), "pack");
+ final File packDir = new File(db.getObjectDatabase().getDirectory(), "pack");
final File crc32Pack = new File(packDir,
"pack-34be9032ac282b11fa9babdc2b2a93ca996c9c2f.pack");
final File crc32Idx = new File(packDir,
@@ -260,7 +279,7 @@ public void testWritePack2DeltasCRC32Copy() throws IOException {
*
*/
public void testWritePack3() throws MissingObjectException, IOException {
- writer.setReuseDeltas(false);
+ config.setReuseDeltas(false);
final ObjectId forcedOrder[] = new ObjectId[] {
ObjectId.fromString("82c6b885ff600be425b4ea96dee75dca255b69e7"),
ObjectId.fromString("c59759f143fb1fe21c197981df75a7ee00290799"),
@@ -358,7 +377,7 @@ public void testWritePack4SizeThinVsNoThin() throws Exception {
}
public void testWriteIndex() throws Exception {
- writer.setIndexVersion(2);
+ config.setIndexVersion(2);
writeVerifyPack4(false);
// Validate that IndexPack came up with the right CRC32 value.
@@ -414,7 +433,7 @@ private void writeVerifyPack1() throws IOException {
}
private void writeVerifyPack2(boolean deltaReuse) throws IOException {
- writer.setReuseDeltas(deltaReuse);
+ config.setReuseDeltas(deltaReuse);
final LinkedList<ObjectId> interestings = new LinkedList<ObjectId>();
interestings.add(ObjectId
.fromString("82c6b885ff600be425b4ea96dee75dca255b69e7"));
@@ -476,17 +495,23 @@ private void createVerifyOpenPack(final Collection<ObjectId> interestings,
final Collection<ObjectId> uninterestings, final boolean thin,
final boolean ignoreMissingUninteresting)
throws MissingObjectException, IOException {
+ NullProgressMonitor m = NullProgressMonitor.INSTANCE;
+ writer = new PackWriter(config, db.newObjectReader());
writer.setThin(thin);
writer.setIgnoreMissingUninteresting(ignoreMissingUninteresting);
- writer.preparePack(interestings, uninterestings);
- writer.writePack(os);
+ writer.preparePack(m, interestings, uninterestings);
+ writer.writePack(m, m, os);
+ writer.release();
verifyOpenPack(thin);
}
private void createVerifyOpenPack(final Iterator<RevObject> objectSource)
throws MissingObjectException, IOException {
+ NullProgressMonitor m = NullProgressMonitor.INSTANCE;
+ writer = new PackWriter(config, db.newObjectReader());
writer.preparePack(objectSource);
- writer.writePack(os);
+ writer.writePack(m, m, os);
+ writer.release();
verifyOpenPack(false);
}
diff --git a/org.eclipse.jgit.test/tst/org/eclipse/jgit/lib/RefDirectoryTest.java b/org.eclipse.jgit.test/tst/org/eclipse/jgit/storage/file/RefDirectoryTest.java
similarity index 99%
rename from org.eclipse.jgit.test/tst/org/eclipse/jgit/lib/RefDirectoryTest.java
rename to org.eclipse.jgit.test/tst/org/eclipse/jgit/storage/file/RefDirectoryTest.java
index a281290..6e98541 100644
--- a/org.eclipse.jgit.test/tst/org/eclipse/jgit/lib/RefDirectoryTest.java
+++ b/org.eclipse.jgit.test/tst/org/eclipse/jgit/storage/file/RefDirectoryTest.java
@@ -41,7 +41,7 @@
* ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
*/
-package org.eclipse.jgit.lib;
+package org.eclipse.jgit.storage.file;
import static org.eclipse.jgit.lib.Constants.HEAD;
import static org.eclipse.jgit.lib.Constants.R_HEADS;
@@ -55,6 +55,10 @@
import org.eclipse.jgit.junit.LocalDiskRepositoryTestCase;
import org.eclipse.jgit.junit.TestRepository;
+import org.eclipse.jgit.lib.AnyObjectId;
+import org.eclipse.jgit.lib.Ref;
+import org.eclipse.jgit.lib.RefDatabase;
+import org.eclipse.jgit.lib.Repository;
import org.eclipse.jgit.revwalk.RevCommit;
import org.eclipse.jgit.revwalk.RevTag;
diff --git a/org.eclipse.jgit.test/tst/org/eclipse/jgit/lib/RefUpdateTest.java b/org.eclipse.jgit.test/tst/org/eclipse/jgit/storage/file/RefUpdateTest.java
similarity index 95%
rename from org.eclipse.jgit.test/tst/org/eclipse/jgit/lib/RefUpdateTest.java
rename to org.eclipse.jgit.test/tst/org/eclipse/jgit/storage/file/RefUpdateTest.java
index 8a9bb52..8717008 100644
--- a/org.eclipse.jgit.test/tst/org/eclipse/jgit/lib/RefUpdateTest.java
+++ b/org.eclipse.jgit.test/tst/org/eclipse/jgit/storage/file/RefUpdateTest.java
@@ -43,7 +43,7 @@
* ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
*/
-package org.eclipse.jgit.lib;
+package org.eclipse.jgit.storage.file;
import java.io.File;
import java.io.IOException;
@@ -51,9 +51,22 @@
import java.util.Map;
import java.util.Map.Entry;
+import org.eclipse.jgit.lib.Constants;
+import org.eclipse.jgit.lib.ObjectId;
+import org.eclipse.jgit.lib.PersonIdent;
+import org.eclipse.jgit.lib.Ref;
+import org.eclipse.jgit.lib.RefRename;
+import org.eclipse.jgit.lib.RefUpdate;
+import org.eclipse.jgit.lib.Repository;
+import org.eclipse.jgit.lib.SampleDataRepositoryTestCase;
import org.eclipse.jgit.lib.RefUpdate.Result;
import org.eclipse.jgit.revwalk.RevCommit;
import org.eclipse.jgit.revwalk.RevWalk;
+import org.eclipse.jgit.storage.file.FileRepository;
+import org.eclipse.jgit.storage.file.LockFile;
+import org.eclipse.jgit.storage.file.RefDirectory;
+import org.eclipse.jgit.storage.file.RefDirectoryUpdate;
+import org.eclipse.jgit.storage.file.ReflogReader;
public class RefUpdateTest extends SampleDataRepositoryTestCase {
@@ -103,14 +116,14 @@ public void testNoCacheObjectIdSubclass() throws IOException {
assertNotSame(newid, r.getObjectId());
assertSame(ObjectId.class, r.getObjectId().getClass());
assertEquals(newid.copy(), r.getObjectId());
- List<org.eclipse.jgit.lib.ReflogReader.Entry> reverseEntries1 = db.getReflogReader("refs/heads/abc").getReverseEntries();
- org.eclipse.jgit.lib.ReflogReader.Entry entry1 = reverseEntries1.get(0);
+ List<org.eclipse.jgit.storage.file.ReflogReader.Entry> reverseEntries1 = db.getReflogReader("refs/heads/abc").getReverseEntries();
+ org.eclipse.jgit.storage.file.ReflogReader.Entry entry1 = reverseEntries1.get(0);
assertEquals(1, reverseEntries1.size());
assertEquals(ObjectId.zeroId(), entry1.getOldId());
assertEquals(r.getObjectId(), entry1.getNewId());
assertEquals(new PersonIdent(db).toString(), entry1.getWho().toString());
assertEquals("", entry1.getComment());
- List<org.eclipse.jgit.lib.ReflogReader.Entry> reverseEntries2 = db.getReflogReader("HEAD").getReverseEntries();
+ List<org.eclipse.jgit.storage.file.ReflogReader.Entry> reverseEntries2 = db.getReflogReader("HEAD").getReverseEntries();
assertEquals(0, reverseEntries2.size());
}
@@ -326,7 +339,7 @@ public void testUpdateRefDetached() throws Exception {
// the branch HEAD referred to is left untouched
assertEquals(pid, db.resolve("refs/heads/master"));
ReflogReader reflogReader = new ReflogReader(db, "HEAD");
- org.eclipse.jgit.lib.ReflogReader.Entry e = reflogReader.getReverseEntries().get(0);
+ org.eclipse.jgit.storage.file.ReflogReader.Entry e = reflogReader.getReverseEntries().get(0);
assertEquals(pid, e.getOldId());
assertEquals(ppid, e.getNewId());
assertEquals("GIT_COMMITTER_EMAIL", e.getWho().getEmailAddress());
@@ -355,7 +368,7 @@ public void testUpdateRefDetachedUnbornHead() throws Exception {
// the branch HEAD referred to is left untouched
assertNull(db.resolve("refs/heads/unborn"));
ReflogReader reflogReader = new ReflogReader(db, "HEAD");
- org.eclipse.jgit.lib.ReflogReader.Entry e = reflogReader.getReverseEntries().get(0);
+ org.eclipse.jgit.storage.file.ReflogReader.Entry e = reflogReader.getReverseEntries().get(0);
assertEquals(ObjectId.zeroId(), e.getOldId());
assertEquals(ppid, e.getNewId());
assertEquals("GIT_COMMITTER_EMAIL", e.getWho().getEmailAddress());
@@ -542,13 +555,15 @@ public void testUpdateRefLockFailureLocked() throws IOException {
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"));
+ LockFile lockFile1 = new LockFile(new File(db.getDirectory(),
+ "refs/heads/master"), db.getFS());
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"));
+ LockFile lockFile2 = new LockFile(new File(db.getDirectory(),"refs/heads/master"),
+ db.getFS());
assertFalse(lockFile2.lock()); // was locked, still is
} finally {
lockFile1.unlock();
@@ -664,7 +679,7 @@ public void testRenameBranchAlsoInPack() throws IOException {
// Create new Repository instance, to reread caches and make sure our
// assumptions are persistent.
- Repository ndb = new Repository(db.getDirectory());
+ Repository ndb = new FileRepository(db.getDirectory());
assertEquals(rb2, ndb.resolve("refs/heads/new/name"));
assertNull(ndb.resolve("refs/heads/b"));
}
@@ -677,16 +692,17 @@ public void tryRenameWhenLocked(String toLock, String fromName,
ObjectId oldHeadId = db.resolve(Constants.HEAD);
writeReflog(db, oldfromId, oldfromId, "Just a message",
fromName);
- List<org.eclipse.jgit.lib.ReflogReader.Entry> oldFromLog = db
+ List<org.eclipse.jgit.storage.file.ReflogReader.Entry> oldFromLog = db
.getReflogReader(fromName).getReverseEntries();
- List<org.eclipse.jgit.lib.ReflogReader.Entry> oldHeadLog = oldHeadId != null ? db
+ List<org.eclipse.jgit.storage.file.ReflogReader.Entry> oldHeadLog = oldHeadId != null ? db
.getReflogReader(Constants.HEAD).getReverseEntries() : null;
assertTrue("internal check, we have a log", new File(db.getDirectory(),
"logs/" + fromName).exists());
// "someone" has branch X locked
- LockFile lockFile = new LockFile(new File(db.getDirectory(), toLock));
+ LockFile lockFile = new LockFile(new File(db.getDirectory(), toLock),
+ db.getFS());
try {
assertTrue(lockFile.lock());
diff --git a/org.eclipse.jgit.test/tst/org/eclipse/jgit/lib/ReflogReaderTest.java b/org.eclipse.jgit.test/tst/org/eclipse/jgit/storage/file/ReflogReaderTest.java
similarity index 97%
rename from org.eclipse.jgit.test/tst/org/eclipse/jgit/lib/ReflogReaderTest.java
rename to org.eclipse.jgit.test/tst/org/eclipse/jgit/storage/file/ReflogReaderTest.java
index 6144851..1d268a4 100644
--- a/org.eclipse.jgit.test/tst/org/eclipse/jgit/lib/ReflogReaderTest.java
+++ b/org.eclipse.jgit.test/tst/org/eclipse/jgit/storage/file/ReflogReaderTest.java
@@ -42,7 +42,7 @@
* ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
*/
-package org.eclipse.jgit.lib;
+package org.eclipse.jgit.storage.file;
import java.io.File;
import java.io.FileNotFoundException;
@@ -51,7 +51,10 @@
import java.text.SimpleDateFormat;
import java.util.List;
-import org.eclipse.jgit.lib.ReflogReader.Entry;
+import org.eclipse.jgit.lib.ObjectId;
+import org.eclipse.jgit.lib.PersonIdent;
+import org.eclipse.jgit.lib.SampleDataRepositoryTestCase;
+import org.eclipse.jgit.storage.file.ReflogReader.Entry;
public class ReflogReaderTest extends SampleDataRepositoryTestCase {
diff --git a/org.eclipse.jgit.test/tst/org/eclipse/jgit/lib/RepositorySetupWorkDirTest.java b/org.eclipse.jgit.test/tst/org/eclipse/jgit/storage/file/RepositorySetupWorkDirTest.java
similarity index 73%
rename from org.eclipse.jgit.test/tst/org/eclipse/jgit/lib/RepositorySetupWorkDirTest.java
rename to org.eclipse.jgit.test/tst/org/eclipse/jgit/storage/file/RepositorySetupWorkDirTest.java
index 6e5e005..d28dd39 100644
--- a/org.eclipse.jgit.test/tst/org/eclipse/jgit/lib/RepositorySetupWorkDirTest.java
+++ b/org.eclipse.jgit.test/tst/org/eclipse/jgit/storage/file/RepositorySetupWorkDirTest.java
@@ -42,12 +42,18 @@
* ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
*/
-package org.eclipse.jgit.lib;
+package org.eclipse.jgit.storage.file;
import java.io.File;
import java.io.IOException;
+import org.eclipse.jgit.errors.ConfigInvalidException;
+import org.eclipse.jgit.errors.NoWorkTreeException;
import org.eclipse.jgit.junit.LocalDiskRepositoryTestCase;
+import org.eclipse.jgit.lib.ConfigConstants;
+import org.eclipse.jgit.lib.Constants;
+import org.eclipse.jgit.lib.Repository;
+import org.eclipse.jgit.util.FS;
/**
* Tests for setting up the working directory when creating a Repository
@@ -57,12 +63,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 +77,14 @@ public void testNotBare_CreateRepositoryFromDotGitGitDir() throws Exception {
public void testWorkdirIsParentDir_CreateRepositoryFromDotGitGitDir()
throws Exception {
File gitDir = getFile("workdir", Constants.DOT_GIT);
- Repository repo = new Repository(gitDir);
- String workdir = repo.getWorkDir().getName();
+ Repository repo = new FileRepository(gitDir);
+ String workdir = repo.getWorkTree().getName();
assertEquals(workdir, "workdir");
}
public void testNotBare_CreateRepositoryFromWorkDirOnly() throws Exception {
File workdir = getFile("workdir", "repo");
- Repository repo = new Repository(null, workdir);
+ FileRepository repo = new FileRepositoryBuilder().setWorkTree(workdir).build();
assertFalse(repo.isBare());
assertWorkdirPath(repo, "workdir", "repo");
assertGitdirPath(repo, "workdir", "repo", Constants.DOT_GIT);
@@ -87,7 +93,7 @@ public void testNotBare_CreateRepositoryFromWorkDirOnly() throws Exception {
public void testWorkdirIsDotGit_CreateRepositoryFromWorkDirOnly()
throws Exception {
File workdir = getFile("workdir", "repo");
- Repository repo = new Repository(null, workdir);
+ FileRepository repo = new FileRepositoryBuilder().setWorkTree(workdir).build();
assertGitdirPath(repo, "workdir", "repo", Constants.DOT_GIT);
}
@@ -96,7 +102,7 @@ public void testNotBare_CreateRepositoryFromGitDirOnlyWithWorktreeConfig()
File gitDir = getFile("workdir", "repoWithConfig");
File workTree = getFile("workdir", "treeRoot");
setWorkTree(gitDir, workTree);
- Repository repo = new Repository(gitDir, null);
+ FileRepository repo = new FileRepositoryBuilder().setGitDir(gitDir).build();
assertFalse(repo.isBare());
assertWorkdirPath(repo, "workdir", "treeRoot");
assertGitdirPath(repo, "workdir", "repoWithConfig");
@@ -106,7 +112,7 @@ public void testBare_CreateRepositoryFromGitDirOnlyWithBareConfigTrue()
throws Exception {
File gitDir = getFile("workdir", "repoWithConfig");
setBare(gitDir, true);
- Repository repo = new Repository(gitDir, null);
+ FileRepository repo = new FileRepositoryBuilder().setGitDir(gitDir).build();
assertTrue(repo.isBare());
}
@@ -114,7 +120,7 @@ public void testWorkdirIsParent_CreateRepositoryFromGitDirOnlyWithBareConfigFals
throws Exception {
File gitDir = getFile("workdir", "repoWithBareConfigTrue", "child");
setBare(gitDir, false);
- Repository repo = new Repository(gitDir, null);
+ FileRepository repo = new FileRepositoryBuilder().setGitDir(gitDir).build();
assertWorkdirPath(repo, "workdir", "repoWithBareConfigTrue");
}
@@ -122,27 +128,18 @@ public void testNotBare_CreateRepositoryFromGitDirOnlyWithBareConfigFalse()
throws Exception {
File gitDir = getFile("workdir", "repoWithBareConfigFalse", "child");
setBare(gitDir, false);
- Repository repo = new Repository(gitDir, null);
+ FileRepository repo = new FileRepositoryBuilder().setGitDir(gitDir).build();
assertFalse(repo.isBare());
assertWorkdirPath(repo, "workdir", "repoWithBareConfigFalse");
assertGitdirPath(repo, "workdir", "repoWithBareConfigFalse", "child");
}
- public void testNotBare_MakeBareUnbareBySetWorkdir() throws Exception {
- File gitDir = getFile("gitDir");
- Repository repo = new Repository(gitDir);
- repo.setWorkDir(getFile("workingDir"));
- assertFalse(repo.isBare());
- assertWorkdirPath(repo, "workingDir");
- assertGitdirPath(repo, "gitDir");
- }
-
public void testExceptionThrown_BareRepoGetWorkDir() throws Exception {
File gitDir = getFile("workdir");
try {
- new Repository(gitDir).getWorkDir();
- fail("Expected IllegalStateException missing");
- } catch (IllegalStateException e) {
+ new FileRepository(gitDir).getWorkTree();
+ fail("Expected NoWorkTreeException missing");
+ } catch (NoWorkTreeException e) {
// expected
}
}
@@ -150,9 +147,9 @@ public void testExceptionThrown_BareRepoGetWorkDir() throws Exception {
public void testExceptionThrown_BareRepoGetIndex() throws Exception {
File gitDir = getFile("workdir");
try {
- new Repository(gitDir).getIndex();
- fail("Expected IllegalStateException missing");
- } catch (IllegalStateException e) {
+ new FileRepository(gitDir).getIndex();
+ fail("Expected NoWorkTreeException missing");
+ } catch (NoWorkTreeException e) {
// expected
}
}
@@ -160,9 +157,9 @@ public void testExceptionThrown_BareRepoGetIndex() throws Exception {
public void testExceptionThrown_BareRepoGetIndexFile() throws Exception {
File gitDir = getFile("workdir");
try {
- new Repository(gitDir).getIndexFile();
- fail("Expected Exception missing");
- } catch (IllegalStateException e) {
+ new FileRepository(gitDir).getIndexFile();
+ fail("Expected NoWorkTreeException missing");
+ } catch (NoWorkTreeException e) {
// expected
}
}
@@ -176,20 +173,29 @@ private File getFile(String... pathComponents) {
return result;
}
- private void setBare(File gitDir, boolean bare) throws IOException {
- Repository repo = new Repository(gitDir, null);
- repo.getConfig().setBoolean(ConfigConstants.CONFIG_CORE_SECTION, null,
+ private void setBare(File gitDir, boolean bare) throws IOException,
+ ConfigInvalidException {
+ FileBasedConfig cfg = configFor(gitDir);
+ cfg.setBoolean(ConfigConstants.CONFIG_CORE_SECTION, null,
ConfigConstants.CONFIG_KEY_BARE, bare);
- repo.getConfig().save();
+ cfg.save();
}
- private void setWorkTree(File gitDir, File workTree) throws IOException {
- Repository repo = new Repository(gitDir, null);
- repo.getConfig()
- .setString(ConfigConstants.CONFIG_CORE_SECTION, null,
- ConfigConstants.CONFIG_KEY_WORKTREE,
- workTree.getAbsolutePath());
- repo.getConfig().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 {
+ File configPath = new File(gitDir, "config");
+ FileBasedConfig cfg = new FileBasedConfig(configPath, FS.DETECTED);
+ cfg.load();
+ return cfg;
}
private void assertGitdirPath(Repository repo, String... expected)
@@ -202,7 +208,7 @@ private void assertGitdirPath(Repository repo, String... expected)
private void assertWorkdirPath(Repository repo, String... expected)
throws IOException {
File exp = getFile(expected).getCanonicalFile();
- File act = repo.getWorkDir().getCanonicalFile();
+ File act = repo.getWorkTree().getCanonicalFile();
assertEquals("Wrong working Directory", exp, act);
}
}
diff --git a/org.eclipse.jgit.test/tst/org/eclipse/jgit/lib/T0003_Basic.java b/org.eclipse.jgit.test/tst/org/eclipse/jgit/storage/file/T0003_Basic.java
similarity index 89%
rename from org.eclipse.jgit.test/tst/org/eclipse/jgit/lib/T0003_Basic.java
rename to org.eclipse.jgit.test/tst/org/eclipse/jgit/storage/file/T0003_Basic.java
index ce8a79e..ecabedd 100644
--- a/org.eclipse.jgit.test/tst/org/eclipse/jgit/lib/T0003_Basic.java
+++ b/org.eclipse.jgit.test/tst/org/eclipse/jgit/storage/file/T0003_Basic.java
@@ -43,7 +43,7 @@
* ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
*/
-package org.eclipse.jgit.lib;
+package org.eclipse.jgit.storage.file;
import java.io.ByteArrayInputStream;
import java.io.File;
@@ -53,7 +53,23 @@
import java.io.IOException;
import java.io.PrintWriter;
+import org.eclipse.jgit.JGitText;
import org.eclipse.jgit.errors.ConfigInvalidException;
+import org.eclipse.jgit.lib.Commit;
+import org.eclipse.jgit.lib.Config;
+import org.eclipse.jgit.lib.Constants;
+import org.eclipse.jgit.lib.FileTreeEntry;
+import org.eclipse.jgit.lib.ObjectDatabase;
+import org.eclipse.jgit.lib.ObjectId;
+import org.eclipse.jgit.lib.ObjectWriter;
+import org.eclipse.jgit.lib.PersonIdent;
+import org.eclipse.jgit.lib.RefUpdate;
+import org.eclipse.jgit.lib.Repository;
+import org.eclipse.jgit.lib.SampleDataRepositoryTestCase;
+import org.eclipse.jgit.lib.Tag;
+import org.eclipse.jgit.lib.Tree;
+import org.eclipse.jgit.lib.TreeEntry;
+import org.eclipse.jgit.lib.WriteTree;
public class T0003_Basic extends SampleDataRepositoryTestCase {
public void test001_Initalize() {
@@ -80,11 +96,11 @@ public void test001_Initalize() {
public void test000_openRepoBadArgs() throws IOException {
try {
- new Repository(null, null);
+ new FileRepositoryBuilder().build();
fail("Must pass either GIT_DIR or GIT_WORK_TREE");
} catch (IllegalArgumentException e) {
assertEquals(
- "Either GIT_DIR or GIT_WORK_TREE must be passed to Repository constructor",
+ JGitText.get().eitherGitDirOrWorkTreeRequired,
e.getMessage());
}
}
@@ -97,16 +113,16 @@ public void test000_openRepoBadArgs() throws IOException {
*/
public void test000_openrepo_default_gitDirSet() throws IOException {
File repo1Parent = new File(trash.getParentFile(), "r1");
- Repository repo1initial = new Repository(new File(repo1Parent, Constants.DOT_GIT));
+ Repository repo1initial = new FileRepository(new File(repo1Parent, Constants.DOT_GIT));
repo1initial.create();
repo1initial.close();
File theDir = new File(repo1Parent, Constants.DOT_GIT);
- Repository r = new Repository(theDir, null);
+ FileRepository r = new FileRepositoryBuilder().setGitDir(theDir).build();
assertEqualsPath(theDir, r.getDirectory());
- assertEqualsPath(repo1Parent, r.getWorkDir());
+ assertEqualsPath(repo1Parent, r.getWorkTree());
assertEqualsPath(new File(theDir, "index"), r.getIndexFile());
- assertEqualsPath(new File(theDir, "objects"), r.getObjectsDirectory());
+ assertEqualsPath(new File(theDir, "objects"), r.getObjectDatabase().getDirectory());
}
/**
@@ -117,16 +133,17 @@ public void test000_openrepo_default_gitDirSet() throws IOException {
*/
public void test000_openrepo_default_gitDirAndWorkTreeSet() throws IOException {
File repo1Parent = new File(trash.getParentFile(), "r1");
- Repository repo1initial = new Repository(new File(repo1Parent, Constants.DOT_GIT));
+ Repository repo1initial = new FileRepository(new File(repo1Parent, Constants.DOT_GIT));
repo1initial.create();
repo1initial.close();
File theDir = new File(repo1Parent, Constants.DOT_GIT);
- Repository r = new Repository(theDir, repo1Parent.getParentFile());
+ FileRepository r = new FileRepositoryBuilder().setGitDir(theDir)
+ .setWorkTree(repo1Parent.getParentFile()).build();
assertEqualsPath(theDir, r.getDirectory());
- assertEqualsPath(repo1Parent.getParentFile(), r.getWorkDir());
+ assertEqualsPath(repo1Parent.getParentFile(), r.getWorkTree());
assertEqualsPath(new File(theDir, "index"), r.getIndexFile());
- assertEqualsPath(new File(theDir, "objects"), r.getObjectsDirectory());
+ assertEqualsPath(new File(theDir, "objects"), r.getObjectDatabase().getDirectory());
}
/**
@@ -137,16 +154,16 @@ public void test000_openrepo_default_gitDirAndWorkTreeSet() throws IOException {
*/
public void test000_openrepo_default_workDirSet() throws IOException {
File repo1Parent = new File(trash.getParentFile(), "r1");
- Repository repo1initial = new Repository(new File(repo1Parent, Constants.DOT_GIT));
+ Repository repo1initial = new FileRepository(new File(repo1Parent, Constants.DOT_GIT));
repo1initial.create();
repo1initial.close();
File theDir = new File(repo1Parent, Constants.DOT_GIT);
- Repository r = new Repository(null, repo1Parent);
+ FileRepository r = new FileRepositoryBuilder().setWorkTree(repo1Parent).build();
assertEqualsPath(theDir, r.getDirectory());
- assertEqualsPath(repo1Parent, r.getWorkDir());
+ assertEqualsPath(repo1Parent, r.getWorkTree());
assertEqualsPath(new File(theDir, "index"), r.getIndexFile());
- assertEqualsPath(new File(theDir, "objects"), r.getObjectsDirectory());
+ assertEqualsPath(new File(theDir, "objects"), r.getObjectDatabase().getDirectory());
}
/**
@@ -159,7 +176,7 @@ public void test000_openrepo_default_absolute_workdirconfig()
File repo1Parent = new File(trash.getParentFile(), "r1");
File workdir = new File(trash.getParentFile(), "rw");
workdir.mkdir();
- Repository repo1initial = new Repository(new File(repo1Parent, Constants.DOT_GIT));
+ FileRepository repo1initial = new FileRepository(new File(repo1Parent, Constants.DOT_GIT));
repo1initial.create();
repo1initial.getConfig().setString("core", null, "worktree",
workdir.getAbsolutePath());
@@ -167,11 +184,11 @@ public void test000_openrepo_default_absolute_workdirconfig()
repo1initial.close();
File theDir = new File(repo1Parent, Constants.DOT_GIT);
- Repository r = new Repository(theDir, null);
+ FileRepository r = new FileRepositoryBuilder().setGitDir(theDir).build();
assertEqualsPath(theDir, r.getDirectory());
- assertEqualsPath(workdir, r.getWorkDir());
+ assertEqualsPath(workdir, r.getWorkTree());
assertEqualsPath(new File(theDir, "index"), r.getIndexFile());
- assertEqualsPath(new File(theDir, "objects"), r.getObjectsDirectory());
+ assertEqualsPath(new File(theDir, "objects"), r.getObjectDatabase().getDirectory());
}
/**
@@ -184,7 +201,7 @@ public void test000_openrepo_default_relative_workdirconfig()
File repo1Parent = new File(trash.getParentFile(), "r1");
File workdir = new File(trash.getParentFile(), "rw");
workdir.mkdir();
- Repository repo1initial = new Repository(new File(repo1Parent, Constants.DOT_GIT));
+ FileRepository repo1initial = new FileRepository(new File(repo1Parent, Constants.DOT_GIT));
repo1initial.create();
repo1initial.getConfig()
.setString("core", null, "worktree", "../../rw");
@@ -192,11 +209,11 @@ public void test000_openrepo_default_relative_workdirconfig()
repo1initial.close();
File theDir = new File(repo1Parent, Constants.DOT_GIT);
- Repository r = new Repository(theDir, null);
+ FileRepository r = new FileRepositoryBuilder().setGitDir(theDir).build();
assertEqualsPath(theDir, r.getDirectory());
- assertEqualsPath(workdir, r.getWorkDir());
+ assertEqualsPath(workdir, r.getWorkTree());
assertEqualsPath(new File(theDir, "index"), r.getIndexFile());
- assertEqualsPath(new File(theDir, "objects"), r.getObjectsDirectory());
+ assertEqualsPath(new File(theDir, "objects"), r.getObjectDatabase().getDirectory());
}
/**
@@ -210,18 +227,21 @@ public void test000_openrepo_alternate_index_file_and_objdirs()
File repo1Parent = new File(trash.getParentFile(), "r1");
File indexFile = new File(trash, "idx");
File objDir = new File(trash, "../obj");
- File[] altObjDirs = new File[] { db.getObjectsDirectory() };
- Repository repo1initial = new Repository(new File(repo1Parent, Constants.DOT_GIT));
+ File altObjDir = db.getObjectDatabase().getDirectory();
+ Repository repo1initial = new FileRepository(new File(repo1Parent, Constants.DOT_GIT));
repo1initial.create();
repo1initial.close();
File theDir = new File(repo1Parent, Constants.DOT_GIT);
- Repository r = new Repository(theDir, null, objDir, altObjDirs,
- indexFile);
+ FileRepository r = new FileRepositoryBuilder() //
+ .setGitDir(theDir).setObjectDirectory(objDir) //
+ .addAlternateObjectDirectory(altObjDir) //
+ .setIndexFile(indexFile) //
+ .build();
assertEqualsPath(theDir, r.getDirectory());
- assertEqualsPath(theDir.getParentFile(), r.getWorkDir());
+ assertEqualsPath(theDir.getParentFile(), r.getWorkTree());
assertEqualsPath(indexFile, r.getIndexFile());
- assertEqualsPath(objDir, r.getObjectsDirectory());
+ assertEqualsPath(objDir, r.getObjectDatabase().getDirectory());
assertNotNull(r.mapCommit("6db9c2ebf75590eef973081736730a9ea169a0c4"));
// Must close or the default repo pack files created by this test gets
// locked via the alternate object directories on Windows.
@@ -283,7 +303,7 @@ public void test003_WriteShouldBeEmptyTree() throws IOException {
}
public void test005_ReadSimpleConfig() {
- final RepositoryConfig c = db.getConfig();
+ final Config c = db.getConfig();
assertNotNull(c);
assertEquals("0", c.getString("core", null, "repositoryformatversion"));
assertEquals("0", c.getString("CoRe", null, "REPOSITORYFoRmAtVeRsIoN"));
@@ -294,8 +314,8 @@ public void test005_ReadSimpleConfig() {
public void test006_ReadUglyConfig() throws IOException,
ConfigInvalidException {
- final RepositoryConfig c = db.getConfig();
final File cfg = new File(db.getDirectory(), "config");
+ final FileBasedConfig c = new FileBasedConfig(cfg, db.getFS());
final FileWriter pw = new FileWriter(cfg);
final String configStr = " [core];comment\n\tfilemode = yes\n"
+ "[user]\n"
@@ -321,9 +341,9 @@ public void test006_ReadUglyConfig() throws IOException,
}
public void test007_Open() throws IOException {
- final Repository db2 = new Repository(db.getDirectory());
+ final FileRepository db2 = new FileRepository(db.getDirectory());
assertEquals(db.getDirectory(), db2.getDirectory());
- assertEquals(db.getObjectsDirectory(), db2.getObjectsDirectory());
+ assertEquals(db.getObjectDatabase().getDirectory(), db2.getObjectDatabase().getDirectory());
assertNotSame(db.getConfig(), db2.getConfig());
}
@@ -337,7 +357,7 @@ public void test008_FailOnWrongVersion() throws IOException {
pw.close();
try {
- new Repository(db.getDirectory());
+ new FileRepository(db.getDirectory());
fail("incorrectly opened a bad repository");
} catch (IOException ioe) {
assertTrue(ioe.getMessage().indexOf("format") > 0);
@@ -345,11 +365,7 @@ public void test008_FailOnWrongVersion() throws IOException {
}
}
- public void test009_CreateCommitOldFormat() throws IOException,
- ConfigInvalidException {
- writeTrashFile(".git/config", "[core]\n" + "legacyHeaders=1\n");
- db.getConfig().load();
-
+ public void test009_CreateCommitOldFormat() throws IOException {
final Tree t = new Tree(db);
final FileTreeEntry f = t.addFile("i-am-a-file");
writeTrashFile(f.getName(), "and this is the data in me\n");
@@ -369,8 +385,10 @@ public void test009_CreateCommitOldFormat() throws IOException,
assertEquals(cmtid, c.getCommitId());
// Verify the commit we just wrote is in the correct format.
- final XInputStream xis = new XInputStream(new FileInputStream(db
- .toFile(cmtid)));
+ ObjectDatabase odb = db.getObjectDatabase();
+ assertTrue("is ObjectDirectory", odb instanceof ObjectDirectory);
+ final XInputStream xis = new XInputStream(new FileInputStream(
+ ((ObjectDirectory) odb).fileFor(cmtid)));
try {
assertEquals(0x78, xis.readUInt8());
assertEquals(0x9c, xis.readUInt8());
@@ -724,10 +742,10 @@ public void test30_stripWorkDir() {
assertEquals("", Repository.stripWorkDir(relBase, relNonFile));
assertEquals("", Repository.stripWorkDir(absBase, absNonFile));
- assertEquals("", Repository.stripWorkDir(db.getWorkDir(), db.getWorkDir()));
+ assertEquals("", Repository.stripWorkDir(db.getWorkTree(), db.getWorkTree()));
- File file = new File(new File(db.getWorkDir(), "subdir"), "File.java");
- assertEquals("subdir/File.java", Repository.stripWorkDir(db.getWorkDir(), file));
+ File file = new File(new File(db.getWorkTree(), "subdir"), "File.java");
+ assertEquals("subdir/File.java", Repository.stripWorkDir(db.getWorkTree(), file));
}
diff --git a/org.eclipse.jgit.test/tst/org/eclipse/jgit/lib/T0004_PackReader.java b/org.eclipse.jgit.test/tst/org/eclipse/jgit/storage/file/T0004_PackReader.java
similarity index 91%
rename from org.eclipse.jgit.test/tst/org/eclipse/jgit/lib/T0004_PackReader.java
rename to org.eclipse.jgit.test/tst/org/eclipse/jgit/storage/file/T0004_PackReader.java
index 336bba2..472d695 100644
--- a/org.eclipse.jgit.test/tst/org/eclipse/jgit/lib/T0004_PackReader.java
+++ b/org.eclipse.jgit.test/tst/org/eclipse/jgit/storage/file/T0004_PackReader.java
@@ -44,11 +44,15 @@
* ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
*/
-package org.eclipse.jgit.lib;
+package org.eclipse.jgit.storage.file;
import java.io.File;
import java.io.IOException;
+import org.eclipse.jgit.lib.Constants;
+import org.eclipse.jgit.lib.ObjectId;
+import org.eclipse.jgit.lib.ObjectLoader;
+import org.eclipse.jgit.lib.SampleDataRepositoryTestCase;
import org.eclipse.jgit.util.JGitTestUtil;
public class T0004_PackReader extends SampleDataRepositoryTestCase {
@@ -59,15 +63,14 @@ public class T0004_PackReader extends SampleDataRepositoryTestCase {
public void test003_lookupCompressedObject() throws IOException {
final PackFile pr;
final ObjectId id;
- final PackedObjectLoader or;
+ final ObjectLoader or;
id = ObjectId.fromString("902d5476fa249b7abc9d84c611577a81381f0327");
pr = new PackFile(TEST_IDX, TEST_PACK);
- or = pr.get(new WindowCursor(), id);
+ or = pr.get(new WindowCursor(null), id);
assertNotNull(or);
assertEquals(Constants.OBJ_TREE, or.getType());
assertEquals(35, or.getSize());
- assertEquals(7736, or.getObjectOffset());
pr.close();
}
@@ -76,11 +79,9 @@ public void test004_lookupDeltifiedObject() throws IOException {
final ObjectLoader or;
id = ObjectId.fromString("5b6e7c66c276e7610d4a73c70ec1a1f7c1003259");
- or = db.openObject(id);
+ or = db.open(id);
assertNotNull(or);
- assertTrue(or instanceof PackedObjectLoader);
assertEquals(Constants.OBJ_BLOB, or.getType());
assertEquals(18009, or.getSize());
- assertEquals(516, ((PackedObjectLoader) or).getObjectOffset());
}
}
diff --git a/org.eclipse.jgit.test/tst/org/eclipse/jgit/storage/file/UnpackedObjectTest.java b/org.eclipse.jgit.test/tst/org/eclipse/jgit/storage/file/UnpackedObjectTest.java
new file mode 100644
index 0000000..25dfe4c
--- /dev/null
+++ b/org.eclipse.jgit.test/tst/org/eclipse/jgit/storage/file/UnpackedObjectTest.java
@@ -0,0 +1,534 @@
+/*
+ * Copyright (C) 2010, Google Inc.
+ * and other copyright owners as documented in the project's IP log.
+ *
+ * This program and the accompanying materials are made available
+ * under the terms of the Eclipse Distribution License v1.0 which
+ * accompanies this distribution, is reproduced below, and is
+ * available at http://www.eclipse.org/org/documents/edl-v10.php
+ *
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or
+ * without modification, are permitted provided that the following
+ * conditions are met:
+ *
+ * - Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ *
+ * - Redistributions in binary form must reproduce the above
+ * copyright notice, this list of conditions and the following
+ * disclaimer in the documentation and/or other materials provided
+ * with the distribution.
+ *
+ * - Neither the name of the Eclipse Foundation, Inc. nor the
+ * names of its contributors may be used to endorse or promote
+ * products derived from this software without specific prior
+ * written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND
+ * CONTRIBUTORS "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES,
+ * INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
+ * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR
+ * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
+ * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT
+ * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
+ * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER
+ * CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT,
+ * STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
+ * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF
+ * ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ */
+
+package org.eclipse.jgit.storage.file;
+
+import java.io.ByteArrayInputStream;
+import java.io.ByteArrayOutputStream;
+import java.io.File;
+import java.io.FileInputStream;
+import java.io.FileOutputStream;
+import java.io.IOException;
+import java.io.InputStream;
+import java.text.MessageFormat;
+import java.util.Arrays;
+import java.util.zip.DeflaterOutputStream;
+
+import org.eclipse.jgit.JGitText;
+import org.eclipse.jgit.errors.CorruptObjectException;
+import org.eclipse.jgit.errors.LargeObjectException;
+import org.eclipse.jgit.junit.LocalDiskRepositoryTestCase;
+import org.eclipse.jgit.junit.TestRng;
+import org.eclipse.jgit.lib.Constants;
+import org.eclipse.jgit.lib.ObjectId;
+import org.eclipse.jgit.lib.ObjectInserter;
+import org.eclipse.jgit.lib.ObjectLoader;
+import org.eclipse.jgit.lib.ObjectStream;
+import org.eclipse.jgit.util.IO;
+
+public class UnpackedObjectTest extends LocalDiskRepositoryTestCase {
+ private TestRng rng;
+
+ private FileRepository repo;
+
+ private WindowCursor wc;
+
+ protected void setUp() throws Exception {
+ super.setUp();
+ rng = new TestRng(getName());
+ repo = createBareRepository();
+ wc = (WindowCursor) repo.newObjectReader();
+ }
+
+ protected void tearDown() throws Exception {
+ if (wc != null)
+ wc.release();
+ super.tearDown();
+ }
+
+ public void testStandardFormat_SmallObject() throws Exception {
+ final int type = Constants.OBJ_BLOB;
+ byte[] data = rng.nextBytes(300);
+ byte[] gz = compressStandardFormat(type, data);
+ ObjectId id = ObjectId.zeroId();
+
+ ObjectLoader ol = UnpackedObject.open(new ByteArrayInputStream(gz),
+ path(id), id, wc);
+ assertNotNull("created loader", ol);
+ assertEquals(type, ol.getType());
+ assertEquals(data.length, ol.getSize());
+ assertFalse("is not large", ol.isLarge());
+ assertTrue("same content", Arrays.equals(data, ol.getCachedBytes()));
+
+ ObjectStream in = ol.openStream();
+ assertNotNull("have stream", in);
+ assertEquals(type, in.getType());
+ assertEquals(data.length, in.getSize());
+ byte[] data2 = new byte[data.length];
+ IO.readFully(in, data2, 0, data.length);
+ assertTrue("same content", Arrays.equals(data2, data));
+ assertEquals("stream at EOF", -1, in.read());
+ in.close();
+ }
+
+ public void testStandardFormat_LargeObject() throws Exception {
+ final int type = Constants.OBJ_BLOB;
+ byte[] data = rng.nextBytes(ObjectLoader.STREAM_THRESHOLD + 5);
+ ObjectId id = new ObjectInserter.Formatter().idFor(type, data);
+ write(id, compressStandardFormat(type, data));
+
+ ObjectLoader ol;
+ {
+ FileInputStream fs = new FileInputStream(path(id));
+ try {
+ ol = UnpackedObject.open(fs, path(id), id, wc);
+ } finally {
+ fs.close();
+ }
+ }
+
+ assertNotNull("created loader", ol);
+ assertEquals(type, ol.getType());
+ assertEquals(data.length, ol.getSize());
+ assertTrue("is large", ol.isLarge());
+ try {
+ ol.getCachedBytes();
+ fail("Should have thrown LargeObjectException");
+ } catch (LargeObjectException tooBig) {
+ assertEquals(id.name(), tooBig.getMessage());
+ }
+
+ ObjectStream in = ol.openStream();
+ assertNotNull("have stream", in);
+ assertEquals(type, in.getType());
+ assertEquals(data.length, in.getSize());
+ byte[] data2 = new byte[data.length];
+ IO.readFully(in, data2, 0, data.length);
+ assertTrue("same content", Arrays.equals(data2, data));
+ assertEquals("stream at EOF", -1, in.read());
+ in.close();
+ }
+
+ public void testStandardFormat_NegativeSize() throws Exception {
+ ObjectId id = ObjectId.zeroId();
+ byte[] data = rng.nextBytes(300);
+
+ try {
+ byte[] gz = compressStandardFormat("blob", "-1", data);
+ UnpackedObject.open(new ByteArrayInputStream(gz), path(id), id, wc);
+ fail("Did not throw CorruptObjectException");
+ } catch (CorruptObjectException coe) {
+ assertEquals(MessageFormat.format(JGitText.get().objectIsCorrupt,
+ id.name(), JGitText.get().corruptObjectNegativeSize), coe
+ .getMessage());
+ }
+ }
+
+ public void testStandardFormat_InvalidType() throws Exception {
+ ObjectId id = ObjectId.zeroId();
+ byte[] data = rng.nextBytes(300);
+
+ try {
+ byte[] gz = compressStandardFormat("not.a.type", "1", data);
+ UnpackedObject.open(new ByteArrayInputStream(gz), path(id), id, wc);
+ fail("Did not throw CorruptObjectException");
+ } catch (CorruptObjectException coe) {
+ assertEquals(MessageFormat.format(JGitText.get().objectIsCorrupt,
+ id.name(), JGitText.get().corruptObjectInvalidType), coe
+ .getMessage());
+ }
+ }
+
+ public void testStandardFormat_NoHeader() throws Exception {
+ ObjectId id = ObjectId.zeroId();
+ byte[] data = {};
+
+ try {
+ byte[] gz = compressStandardFormat("", "", data);
+ UnpackedObject.open(new ByteArrayInputStream(gz), path(id), id, wc);
+ fail("Did not throw CorruptObjectException");
+ } catch (CorruptObjectException coe) {
+ assertEquals(MessageFormat.format(JGitText.get().objectIsCorrupt,
+ id.name(), JGitText.get().corruptObjectNoHeader), coe
+ .getMessage());
+ }
+ }
+
+ public void testStandardFormat_GarbageAfterSize() throws Exception {
+ ObjectId id = ObjectId.zeroId();
+ byte[] data = rng.nextBytes(300);
+
+ try {
+ byte[] gz = compressStandardFormat("blob", "1foo", data);
+ UnpackedObject.open(new ByteArrayInputStream(gz), path(id), id, wc);
+ fail("Did not throw CorruptObjectException");
+ } catch (CorruptObjectException coe) {
+ assertEquals(MessageFormat.format(JGitText.get().objectIsCorrupt,
+ id.name(), JGitText.get().corruptObjectGarbageAfterSize),
+ coe.getMessage());
+ }
+ }
+
+ public void testStandardFormat_SmallObject_CorruptZLibStream()
+ throws Exception {
+ ObjectId id = ObjectId.zeroId();
+ byte[] data = rng.nextBytes(300);
+
+ try {
+ byte[] gz = compressStandardFormat(Constants.OBJ_BLOB, data);
+ for (int i = 5; i < gz.length; i++)
+ gz[i] = 0;
+ UnpackedObject.open(new ByteArrayInputStream(gz), path(id), id, wc);
+ fail("Did not throw CorruptObjectException");
+ } catch (CorruptObjectException coe) {
+ assertEquals(MessageFormat.format(JGitText.get().objectIsCorrupt,
+ id.name(), JGitText.get().corruptObjectBadStream), coe
+ .getMessage());
+ }
+ }
+
+ public void testStandardFormat_SmallObject_TruncatedZLibStream()
+ throws Exception {
+ ObjectId id = ObjectId.zeroId();
+ byte[] data = rng.nextBytes(300);
+
+ try {
+ byte[] gz = compressStandardFormat(Constants.OBJ_BLOB, data);
+ byte[] tr = new byte[gz.length - 1];
+ System.arraycopy(gz, 0, tr, 0, tr.length);
+ UnpackedObject.open(new ByteArrayInputStream(tr), path(id), id, wc);
+ fail("Did not throw CorruptObjectException");
+ } catch (CorruptObjectException coe) {
+ assertEquals(MessageFormat.format(JGitText.get().objectIsCorrupt,
+ id.name(), JGitText.get().corruptObjectBadStream), coe
+ .getMessage());
+ }
+ }
+
+ public void testStandardFormat_SmallObject_TrailingGarbage()
+ throws Exception {
+ ObjectId id = ObjectId.zeroId();
+ byte[] data = rng.nextBytes(300);
+
+ try {
+ byte[] gz = compressStandardFormat(Constants.OBJ_BLOB, data);
+ byte[] tr = new byte[gz.length + 1];
+ System.arraycopy(gz, 0, tr, 0, gz.length);
+ UnpackedObject.open(new ByteArrayInputStream(tr), path(id), id, wc);
+ fail("Did not throw CorruptObjectException");
+ } catch (CorruptObjectException coe) {
+ assertEquals(MessageFormat.format(JGitText.get().objectIsCorrupt,
+ id.name(), JGitText.get().corruptObjectBadStream), coe
+ .getMessage());
+ }
+ }
+
+ public void testStandardFormat_LargeObject_CorruptZLibStream()
+ throws Exception {
+ final int type = Constants.OBJ_BLOB;
+ byte[] data = rng.nextBytes(ObjectLoader.STREAM_THRESHOLD + 5);
+ ObjectId id = new ObjectInserter.Formatter().idFor(type, data);
+ byte[] gz = compressStandardFormat(type, data);
+ gz[gz.length - 1] = 0;
+ gz[gz.length - 2] = 0;
+
+ write(id, gz);
+
+ ObjectLoader ol;
+ {
+ FileInputStream fs = new FileInputStream(path(id));
+ try {
+ ol = UnpackedObject.open(fs, path(id), id, wc);
+ } finally {
+ fs.close();
+ }
+ }
+
+ try {
+ byte[] tmp = new byte[data.length];
+ InputStream in = ol.openStream();
+ try {
+ IO.readFully(in, tmp, 0, tmp.length);
+ } finally {
+ in.close();
+ }
+ fail("Did not throw CorruptObjectException");
+ } catch (CorruptObjectException coe) {
+ assertEquals(MessageFormat.format(JGitText.get().objectIsCorrupt,
+ id.name(), JGitText.get().corruptObjectBadStream), coe
+ .getMessage());
+ }
+ }
+
+ public void testStandardFormat_LargeObject_TruncatedZLibStream()
+ throws Exception {
+ final int type = Constants.OBJ_BLOB;
+ byte[] data = rng.nextBytes(ObjectLoader.STREAM_THRESHOLD + 5);
+ ObjectId id = new ObjectInserter.Formatter().idFor(type, data);
+ byte[] gz = compressStandardFormat(type, data);
+ byte[] tr = new byte[gz.length - 1];
+ System.arraycopy(gz, 0, tr, 0, tr.length);
+
+ write(id, tr);
+
+ ObjectLoader ol;
+ {
+ FileInputStream fs = new FileInputStream(path(id));
+ try {
+ ol = UnpackedObject.open(fs, path(id), id, wc);
+ } finally {
+ fs.close();
+ }
+ }
+
+ byte[] tmp = new byte[data.length];
+ InputStream in = ol.openStream();
+ IO.readFully(in, tmp, 0, tmp.length);
+ try {
+ in.close();
+ fail("close did not throw CorruptObjectException");
+ } catch (CorruptObjectException coe) {
+ assertEquals(MessageFormat.format(JGitText.get().objectIsCorrupt,
+ id.name(), JGitText.get().corruptObjectBadStream), coe
+ .getMessage());
+ }
+ }
+
+ public void testStandardFormat_LargeObject_TrailingGarbage()
+ throws Exception {
+ final int type = Constants.OBJ_BLOB;
+ byte[] data = rng.nextBytes(ObjectLoader.STREAM_THRESHOLD + 5);
+ ObjectId id = new ObjectInserter.Formatter().idFor(type, data);
+ byte[] gz = compressStandardFormat(type, data);
+ byte[] tr = new byte[gz.length + 1];
+ System.arraycopy(gz, 0, tr, 0, gz.length);
+
+ write(id, tr);
+
+ ObjectLoader ol;
+ {
+ FileInputStream fs = new FileInputStream(path(id));
+ try {
+ ol = UnpackedObject.open(fs, path(id), id, wc);
+ } finally {
+ fs.close();
+ }
+ }
+
+ byte[] tmp = new byte[data.length];
+ InputStream in = ol.openStream();
+ IO.readFully(in, tmp, 0, tmp.length);
+ try {
+ in.close();
+ fail("close did not throw CorruptObjectException");
+ } catch (CorruptObjectException coe) {
+ assertEquals(MessageFormat.format(JGitText.get().objectIsCorrupt,
+ id.name(), JGitText.get().corruptObjectBadStream), coe
+ .getMessage());
+ }
+ }
+
+ public void testPackFormat_SmallObject() throws Exception {
+ final int type = Constants.OBJ_BLOB;
+ byte[] data = rng.nextBytes(300);
+ byte[] gz = compressPackFormat(type, data);
+ ObjectId id = ObjectId.zeroId();
+
+ ObjectLoader ol = UnpackedObject.open(new ByteArrayInputStream(gz),
+ path(id), id, wc);
+ assertNotNull("created loader", ol);
+ assertEquals(type, ol.getType());
+ assertEquals(data.length, ol.getSize());
+ assertFalse("is not large", ol.isLarge());
+ assertTrue("same content", Arrays.equals(data, ol.getCachedBytes()));
+
+ ObjectStream in = ol.openStream();
+ assertNotNull("have stream", in);
+ assertEquals(type, in.getType());
+ assertEquals(data.length, in.getSize());
+ byte[] data2 = new byte[data.length];
+ IO.readFully(in, data2, 0, data.length);
+ assertTrue("same content", Arrays.equals(data, ol.getCachedBytes()));
+ in.close();
+ }
+
+ public void testPackFormat_LargeObject() throws Exception {
+ final int type = Constants.OBJ_BLOB;
+ byte[] data = rng.nextBytes(ObjectLoader.STREAM_THRESHOLD + 5);
+ ObjectId id = new ObjectInserter.Formatter().idFor(type, data);
+ write(id, compressPackFormat(type, data));
+
+ ObjectLoader ol;
+ {
+ FileInputStream fs = new FileInputStream(path(id));
+ try {
+ ol = UnpackedObject.open(fs, path(id), id, wc);
+ } finally {
+ fs.close();
+ }
+ }
+
+ assertNotNull("created loader", ol);
+ assertEquals(type, ol.getType());
+ assertEquals(data.length, ol.getSize());
+ assertTrue("is large", ol.isLarge());
+ try {
+ ol.getCachedBytes();
+ fail("Should have thrown LargeObjectException");
+ } catch (LargeObjectException tooBig) {
+ assertEquals(id.name(), tooBig.getMessage());
+ }
+
+ ObjectStream in = ol.openStream();
+ assertNotNull("have stream", in);
+ assertEquals(type, in.getType());
+ assertEquals(data.length, in.getSize());
+ byte[] data2 = new byte[data.length];
+ IO.readFully(in, data2, 0, data.length);
+ assertTrue("same content", Arrays.equals(data2, data));
+ assertEquals("stream at EOF", -1, in.read());
+ in.close();
+ }
+
+ public void testPackFormat_DeltaNotAllowed() throws Exception {
+ ObjectId id = ObjectId.zeroId();
+ byte[] data = rng.nextBytes(300);
+
+ try {
+ byte[] gz = compressPackFormat(Constants.OBJ_OFS_DELTA, data);
+ UnpackedObject.open(new ByteArrayInputStream(gz), path(id), id, wc);
+ fail("Did not throw CorruptObjectException");
+ } catch (CorruptObjectException coe) {
+ assertEquals(MessageFormat.format(JGitText.get().objectIsCorrupt,
+ id.name(), JGitText.get().corruptObjectInvalidType), coe
+ .getMessage());
+ }
+
+ try {
+ byte[] gz = compressPackFormat(Constants.OBJ_REF_DELTA, data);
+ UnpackedObject.open(new ByteArrayInputStream(gz), path(id), id, wc);
+ fail("Did not throw CorruptObjectException");
+ } catch (CorruptObjectException coe) {
+ assertEquals(MessageFormat.format(JGitText.get().objectIsCorrupt,
+ id.name(), JGitText.get().corruptObjectInvalidType), coe
+ .getMessage());
+ }
+
+ try {
+ byte[] gz = compressPackFormat(Constants.OBJ_TYPE_5, data);
+ UnpackedObject.open(new ByteArrayInputStream(gz), path(id), id, wc);
+ fail("Did not throw CorruptObjectException");
+ } catch (CorruptObjectException coe) {
+ assertEquals(MessageFormat.format(JGitText.get().objectIsCorrupt,
+ id.name(), JGitText.get().corruptObjectInvalidType), coe
+ .getMessage());
+ }
+
+ try {
+ byte[] gz = compressPackFormat(Constants.OBJ_EXT, data);
+ UnpackedObject.open(new ByteArrayInputStream(gz), path(id), id, wc);
+ fail("Did not throw CorruptObjectException");
+ } catch (CorruptObjectException coe) {
+ assertEquals(MessageFormat.format(JGitText.get().objectIsCorrupt,
+ id.name(), JGitText.get().corruptObjectInvalidType), coe
+ .getMessage());
+ }
+ }
+
+ private byte[] compressStandardFormat(int type, byte[] data)
+ throws IOException {
+ String typeString = Constants.typeString(type);
+ String length = String.valueOf(data.length);
+ return compressStandardFormat(typeString, length, data);
+ }
+
+ private byte[] compressStandardFormat(String type, String length,
+ byte[] data) throws IOException {
+ ByteArrayOutputStream out = new ByteArrayOutputStream();
+ DeflaterOutputStream d = new DeflaterOutputStream(out);
+ d.write(Constants.encodeASCII(type));
+ d.write(' ');
+ d.write(Constants.encodeASCII(length));
+ d.write(0);
+ d.write(data);
+ d.finish();
+ return out.toByteArray();
+ }
+
+ private byte[] compressPackFormat(int type, byte[] data) throws IOException {
+ byte[] hdr = new byte[64];
+ int rawLength = data.length;
+ int nextLength = rawLength >>> 4;
+ hdr[0] = (byte) ((nextLength > 0 ? 0x80 : 0x00) | (type << 4) | (rawLength & 0x0F));
+ rawLength = nextLength;
+ int n = 1;
+ while (rawLength > 0) {
+ nextLength >>>= 7;
+ hdr[n++] = (byte) ((nextLength > 0 ? 0x80 : 0x00) | (rawLength & 0x7F));
+ rawLength = nextLength;
+ }
+
+ final ByteArrayOutputStream out = new ByteArrayOutputStream();
+ out.write(hdr, 0, n);
+
+ DeflaterOutputStream d = new DeflaterOutputStream(out);
+ d.write(data);
+ d.finish();
+ return out.toByteArray();
+ }
+
+ private File path(ObjectId id) {
+ return repo.getObjectDatabase().fileFor(id);
+ }
+
+ private void write(ObjectId id, byte[] data) throws IOException {
+ File path = path(id);
+ path.getParentFile().mkdirs();
+ FileOutputStream out = new FileOutputStream(path);
+ try {
+ out.write(data);
+ } finally {
+ out.close();
+ }
+ }
+}
diff --git a/org.eclipse.jgit.test/tst/org/eclipse/jgit/lib/WindowCacheGetTest.java b/org.eclipse.jgit.test/tst/org/eclipse/jgit/storage/file/WindowCacheGetTest.java
similarity index 92%
rename from org.eclipse.jgit.test/tst/org/eclipse/jgit/lib/WindowCacheGetTest.java
rename to org.eclipse.jgit.test/tst/org/eclipse/jgit/storage/file/WindowCacheGetTest.java
index 8ff022d..d8c6829 100644
--- a/org.eclipse.jgit.test/tst/org/eclipse/jgit/lib/WindowCacheGetTest.java
+++ b/org.eclipse.jgit.test/tst/org/eclipse/jgit/storage/file/WindowCacheGetTest.java
@@ -41,7 +41,7 @@
* ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
*/
-package org.eclipse.jgit.lib;
+package org.eclipse.jgit.storage.file;
import java.io.BufferedReader;
import java.io.FileInputStream;
@@ -51,6 +51,10 @@
import java.util.List;
import org.eclipse.jgit.errors.CorruptObjectException;
+import org.eclipse.jgit.lib.Constants;
+import org.eclipse.jgit.lib.ObjectId;
+import org.eclipse.jgit.lib.ObjectLoader;
+import org.eclipse.jgit.lib.SampleDataRepositoryTestCase;
import org.eclipse.jgit.util.JGitTestUtil;
import org.eclipse.jgit.util.MutableInteger;
@@ -73,9 +77,9 @@ public void setUp() throws Exception {
final TestObject o = new TestObject();
o.id = ObjectId.fromString(parts[0]);
o.setType(parts[1]);
- o.rawSize = Integer.parseInt(parts[2]);
+ // parts[2] is the inflate size
// parts[3] is the size-in-pack
- o.offset = Long.parseLong(parts[4]);
+ // parts[4] is the offset in the pack
toLoad.add(o);
}
} finally {
@@ -122,12 +126,9 @@ private void checkLimits(final WindowCacheConfig cfg) {
private void doCacheTests() throws IOException {
for (final TestObject o : toLoad) {
- final ObjectLoader or = db.openObject(o.id);
+ final ObjectLoader or = db.open(o.id, o.type);
assertNotNull(or);
- assertTrue(or instanceof PackedObjectLoader);
assertEquals(o.type, or.getType());
- assertEquals(o.rawSize, or.getRawSize());
- assertEquals(o.offset, ((PackedObjectLoader) or).getObjectOffset());
}
}
@@ -136,10 +137,6 @@ private class TestObject {
int type;
- int rawSize;
-
- long offset;
-
void setType(final String typeStr) throws CorruptObjectException {
final byte[] typeRaw = Constants.encode(typeStr + " ");
final MutableInteger ptr = new MutableInteger();
diff --git a/org.eclipse.jgit.test/tst/org/eclipse/jgit/lib/WindowCacheReconfigureTest.java b/org.eclipse.jgit.test/tst/org/eclipse/jgit/storage/file/WindowCacheReconfigureTest.java
similarity index 97%
rename from org.eclipse.jgit.test/tst/org/eclipse/jgit/lib/WindowCacheReconfigureTest.java
rename to org.eclipse.jgit.test/tst/org/eclipse/jgit/storage/file/WindowCacheReconfigureTest.java
index 9e093c8..e52b19d 100644
--- a/org.eclipse.jgit.test/tst/org/eclipse/jgit/lib/WindowCacheReconfigureTest.java
+++ b/org.eclipse.jgit.test/tst/org/eclipse/jgit/storage/file/WindowCacheReconfigureTest.java
@@ -41,7 +41,9 @@
* ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
*/
-package org.eclipse.jgit.lib;
+package org.eclipse.jgit.storage.file;
+
+import org.eclipse.jgit.lib.RepositoryTestCase;
public class WindowCacheReconfigureTest extends RepositoryTestCase {
public void testConfigureCache_PackedGitLimit_0() {
diff --git a/org.eclipse.jgit.test/tst/org/eclipse/jgit/lib/XInputStream.java b/org.eclipse.jgit.test/tst/org/eclipse/jgit/storage/file/XInputStream.java
similarity index 98%
rename from org.eclipse.jgit.test/tst/org/eclipse/jgit/lib/XInputStream.java
rename to org.eclipse.jgit.test/tst/org/eclipse/jgit/storage/file/XInputStream.java
index eef32b9..9978c8e 100644
--- a/org.eclipse.jgit.test/tst/org/eclipse/jgit/lib/XInputStream.java
+++ b/org.eclipse.jgit.test/tst/org/eclipse/jgit/storage/file/XInputStream.java
@@ -41,7 +41,7 @@
* ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
*/
-package org.eclipse.jgit.lib;
+package org.eclipse.jgit.storage.file;
import java.io.BufferedInputStream;
import java.io.EOFException;
diff --git a/org.eclipse.jgit.test/tst/org/eclipse/jgit/storage/pack/DeltaIndexTest.java b/org.eclipse.jgit.test/tst/org/eclipse/jgit/storage/pack/DeltaIndexTest.java
new file mode 100644
index 0000000..868ef88
--- /dev/null
+++ b/org.eclipse.jgit.test/tst/org/eclipse/jgit/storage/pack/DeltaIndexTest.java
@@ -0,0 +1,228 @@
+/*
+ * Copyright (C) 2010, Google Inc.
+ * and other copyright owners as documented in the project's IP log.
+ *
+ * This program and the accompanying materials are made available
+ * under the terms of the Eclipse Distribution License v1.0 which
+ * accompanies this distribution, is reproduced below, and is
+ * available at http://www.eclipse.org/org/documents/edl-v10.php
+ *
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or
+ * without modification, are permitted provided that the following
+ * conditions are met:
+ *
+ * - Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ *
+ * - Redistributions in binary form must reproduce the above
+ * copyright notice, this list of conditions and the following
+ * disclaimer in the documentation and/or other materials provided
+ * with the distribution.
+ *
+ * - Neither the name of the Eclipse Foundation, Inc. nor the
+ * names of its contributors may be used to endorse or promote
+ * products derived from this software without specific prior
+ * written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND
+ * CONTRIBUTORS "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES,
+ * INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
+ * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR
+ * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
+ * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT
+ * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
+ * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER
+ * CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT,
+ * STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
+ * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF
+ * ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ */
+
+package org.eclipse.jgit.storage.pack;
+
+import java.io.ByteArrayOutputStream;
+import java.io.IOException;
+import java.util.Arrays;
+
+import junit.framework.TestCase;
+
+import org.eclipse.jgit.junit.TestRng;
+import org.eclipse.jgit.lib.Constants;
+
+public class DeltaIndexTest extends TestCase {
+ private TestRng rng;
+
+ private ByteArrayOutputStream actDeltaBuf;
+
+ private ByteArrayOutputStream expDeltaBuf;
+
+ private DeltaEncoder expDeltaEnc;
+
+ private byte[] src;
+
+ private byte[] dst;
+
+ private ByteArrayOutputStream dstBuf;
+
+ protected void setUp() throws Exception {
+ super.setUp();
+ rng = new TestRng(getName());
+ actDeltaBuf = new ByteArrayOutputStream();
+ expDeltaBuf = new ByteArrayOutputStream();
+ expDeltaEnc = new DeltaEncoder(expDeltaBuf, 0, 0);
+ dstBuf = new ByteArrayOutputStream();
+ }
+
+ public void testInsertWholeObject_Length12() throws IOException {
+ src = rng.nextBytes(12);
+ insert(src);
+ doTest();
+ }
+
+ public void testCopyWholeObject_Length128() throws IOException {
+ src = rng.nextBytes(128);
+ copy(0, 128);
+ doTest();
+ }
+
+ public void testCopyWholeObject_Length123() throws IOException {
+ src = rng.nextBytes(123);
+ copy(0, 123);
+ doTest();
+ }
+
+ public void testCopyZeros_Length128() throws IOException {
+ src = new byte[2048];
+ copy(0, src.length);
+ doTest();
+
+ // The index should be smaller than expected due to the chain
+ // being truncated. Without truncation we would expect to have
+ // more than 3584 bytes used.
+ //
+ assertEquals(2636, new DeltaIndex(src).getIndexSize());
+ }
+
+ public void testShuffleSegments() throws IOException {
+ src = rng.nextBytes(128);
+ copy(64, 64);
+ copy(0, 64);
+ doTest();
+ }
+
+ public void testInsertHeadMiddle() throws IOException {
+ src = rng.nextBytes(1024);
+ insert("foo");
+ copy(0, 512);
+ insert("yet more fooery");
+ copy(0, 512);
+ doTest();
+ }
+
+ public void testInsertTail() throws IOException {
+ src = rng.nextBytes(1024);
+ copy(0, 512);
+ insert("bar");
+ doTest();
+ }
+
+ public void testIndexSize() {
+ src = rng.nextBytes(1024);
+ DeltaIndex di = new DeltaIndex(src);
+ assertEquals(1860, di.getIndexSize());
+ assertEquals("DeltaIndex[2 KiB]", di.toString());
+ }
+
+ public void testLimitObjectSize_Length12InsertFails() throws IOException {
+ src = rng.nextBytes(12);
+ dst = src;
+
+ DeltaIndex di = new DeltaIndex(src);
+ assertFalse(di.encode(actDeltaBuf, dst, src.length));
+ }
+
+ public void testLimitObjectSize_Length130InsertFails() throws IOException {
+ src = rng.nextBytes(130);
+ dst = rng.nextBytes(130);
+
+ DeltaIndex di = new DeltaIndex(src);
+ assertFalse(di.encode(actDeltaBuf, dst, src.length));
+ }
+
+ public void testLimitObjectSize_Length130CopyOk() throws IOException {
+ src = rng.nextBytes(130);
+ copy(0, 130);
+ dst = dstBuf.toByteArray();
+
+ DeltaIndex di = new DeltaIndex(src);
+ assertTrue(di.encode(actDeltaBuf, dst, dst.length));
+
+ byte[] actDelta = actDeltaBuf.toByteArray();
+ byte[] expDelta = expDeltaBuf.toByteArray();
+
+ assertEquals(BinaryDelta.format(expDelta, false), //
+ BinaryDelta.format(actDelta, false));
+ }
+
+ public void testLimitObjectSize_Length130CopyFails() throws IOException {
+ src = rng.nextBytes(130);
+ copy(0, 130);
+ dst = dstBuf.toByteArray();
+
+ // The header requires 4 bytes for these objects, so a target length
+ // of 5 is bigger than the copy instruction and should cause an abort.
+ //
+ DeltaIndex di = new DeltaIndex(src);
+ assertFalse(di.encode(actDeltaBuf, dst, 5));
+ assertEquals(4, actDeltaBuf.size());
+ }
+
+ public void testLimitObjectSize_InsertFrontFails() throws IOException {
+ src = rng.nextBytes(130);
+ insert("eight");
+ copy(0, 130);
+ dst = dstBuf.toByteArray();
+
+ // The header requires 4 bytes for these objects, so a target length
+ // of 5 is bigger than the copy instruction and should cause an abort.
+ //
+ DeltaIndex di = new DeltaIndex(src);
+ assertFalse(di.encode(actDeltaBuf, dst, 5));
+ assertEquals(4, actDeltaBuf.size());
+ }
+
+ private void copy(int offset, int len) throws IOException {
+ dstBuf.write(src, offset, len);
+ expDeltaEnc.copy(offset, len);
+ }
+
+ private void insert(String text) throws IOException {
+ insert(Constants.encode(text));
+ }
+
+ private void insert(byte[] text) throws IOException {
+ dstBuf.write(text);
+ expDeltaEnc.insert(text);
+ }
+
+ private void doTest() throws IOException {
+ dst = dstBuf.toByteArray();
+
+ DeltaIndex di = new DeltaIndex(src);
+ di.encode(actDeltaBuf, dst);
+
+ byte[] actDelta = actDeltaBuf.toByteArray();
+ byte[] expDelta = expDeltaBuf.toByteArray();
+
+ assertEquals(BinaryDelta.format(expDelta, false), //
+ BinaryDelta.format(actDelta, false));
+
+ assertTrue("delta is not empty", actDelta.length > 0);
+ assertEquals(src.length, BinaryDelta.getBaseSize(actDelta));
+ assertEquals(dst.length, BinaryDelta.getResultSize(actDelta));
+ assertTrue(Arrays.equals(dst, BinaryDelta.apply(src, actDelta)));
+ }
+}
diff --git a/org.eclipse.jgit.test/tst/org/eclipse/jgit/storage/pack/DeltaStreamTest.java b/org.eclipse.jgit.test/tst/org/eclipse/jgit/storage/pack/DeltaStreamTest.java
new file mode 100644
index 0000000..9b34ad5
--- /dev/null
+++ b/org.eclipse.jgit.test/tst/org/eclipse/jgit/storage/pack/DeltaStreamTest.java
@@ -0,0 +1,273 @@
+/*
+ * Copyright (C) 2010, Google Inc.
+ * and other copyright owners as documented in the project's IP log.
+ *
+ * This program and the accompanying materials are made available
+ * under the terms of the Eclipse Distribution License v1.0 which
+ * accompanies this distribution, is reproduced below, and is
+ * available at http://www.eclipse.org/org/documents/edl-v10.php
+ *
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or
+ * without modification, are permitted provided that the following
+ * conditions are met:
+ *
+ * - Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ *
+ * - Redistributions in binary form must reproduce the above
+ * copyright notice, this list of conditions and the following
+ * disclaimer in the documentation and/or other materials provided
+ * with the distribution.
+ *
+ * - Neither the name of the Eclipse Foundation, Inc. nor the
+ * names of its contributors may be used to endorse or promote
+ * products derived from this software without specific prior
+ * written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND
+ * CONTRIBUTORS "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES,
+ * INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
+ * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR
+ * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
+ * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT
+ * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
+ * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER
+ * CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT,
+ * STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
+ * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF
+ * ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ */
+
+package org.eclipse.jgit.storage.pack;
+
+import java.io.ByteArrayInputStream;
+import java.io.ByteArrayOutputStream;
+import java.io.IOException;
+import java.io.InputStream;
+import java.util.Arrays;
+
+import junit.framework.TestCase;
+
+import org.eclipse.jgit.JGitText;
+import org.eclipse.jgit.errors.CorruptObjectException;
+import org.eclipse.jgit.junit.TestRng;
+import org.eclipse.jgit.lib.Constants;
+import org.eclipse.jgit.util.IO;
+
+public class DeltaStreamTest extends TestCase {
+ private TestRng rng;
+
+ private ByteArrayOutputStream deltaBuf;
+
+ private DeltaEncoder deltaEnc;
+
+ private byte[] base;
+
+ private byte[] data;
+
+ private int dataPtr;
+
+ private byte[] delta;
+
+ protected void setUp() throws Exception {
+ super.setUp();
+ rng = new TestRng(getName());
+ deltaBuf = new ByteArrayOutputStream();
+ }
+
+ public void testCopy_SingleOp() throws IOException {
+ init((1 << 16) + 1, (1 << 8) + 1);
+ copy(0, data.length);
+ assertValidState();
+ }
+
+ public void testCopy_MaxSize() throws IOException {
+ int max = (0xff << 16) + (0xff << 8) + 0xff;
+ init(1 + max, max);
+ copy(1, max);
+ assertValidState();
+ }
+
+ public void testCopy_64k() throws IOException {
+ init(0x10000 + 2, 0x10000 + 1);
+ copy(1, 0x10000);
+ copy(0x10001, 1);
+ assertValidState();
+ }
+
+ public void testCopy_Gap() throws IOException {
+ init(256, 8);
+ copy(4, 4);
+ copy(128, 4);
+ assertValidState();
+ }
+
+ public void testCopy_OutOfOrder() throws IOException {
+ init((1 << 16) + 1, (1 << 16) + 1);
+ copy(1 << 8, 1 << 8);
+ copy(0, data.length - dataPtr);
+ assertValidState();
+ }
+
+ public void testInsert_SingleOp() throws IOException {
+ init((1 << 16) + 1, 2);
+ insert("hi");
+ assertValidState();
+ }
+
+ public void testInsertAndCopy() throws IOException {
+ init(8, 512);
+ insert(new byte[127]);
+ insert(new byte[127]);
+ insert(new byte[127]);
+ insert(new byte[125]);
+ copy(2, 6);
+ assertValidState();
+ }
+
+ public void testSkip() throws IOException {
+ init(32, 15);
+ copy(2, 2);
+ insert("ab");
+ insert("cd");
+ copy(4, 4);
+ copy(0, 2);
+ insert("efg");
+ assertValidState();
+
+ for (int p = 0; p < data.length; p++) {
+ byte[] act = new byte[data.length];
+ System.arraycopy(data, 0, act, 0, p);
+ DeltaStream in = open();
+ IO.skipFully(in, p);
+ assertEquals(data.length - p, in.read(act, p, data.length - p));
+ assertEquals(-1, in.read());
+ assertTrue("skipping " + p, Arrays.equals(data, act));
+ }
+
+ // Skip all the way to the end should still recognize EOF.
+ DeltaStream in = open();
+ IO.skipFully(in, data.length);
+ assertEquals(-1, in.read());
+ assertEquals(0, in.skip(1));
+
+ // Skip should not open the base as we move past it, but it
+ // will open when we need to start copying data from it.
+ final boolean[] opened = new boolean[1];
+ in = new DeltaStream(new ByteArrayInputStream(delta)) {
+ @Override
+ protected long getBaseSize() throws IOException {
+ return base.length;
+ }
+
+ @Override
+ protected InputStream openBase() throws IOException {
+ opened[0] = true;
+ return new ByteArrayInputStream(base);
+ }
+ };
+ IO.skipFully(in, 7);
+ assertFalse("not yet open", opened[0]);
+ assertEquals(data[7], in.read());
+ assertTrue("now open", opened[0]);
+ }
+
+ public void testIncorrectBaseSize() throws IOException {
+ init(4, 4);
+ copy(0, 4);
+ assertValidState();
+
+ DeltaStream in = new DeltaStream(new ByteArrayInputStream(delta)) {
+ @Override
+ protected long getBaseSize() throws IOException {
+ return 128;
+ }
+
+ @Override
+ protected InputStream openBase() throws IOException {
+ return new ByteArrayInputStream(base);
+ }
+ };
+ try {
+ in.read(new byte[4]);
+ fail("did not throw an exception");
+ } catch (CorruptObjectException e) {
+ assertEquals(JGitText.get().baseLengthIncorrect, e.getMessage());
+ }
+
+ in = new DeltaStream(new ByteArrayInputStream(delta)) {
+ @Override
+ protected long getBaseSize() throws IOException {
+ return 4;
+ }
+
+ @Override
+ protected InputStream openBase() throws IOException {
+ return new ByteArrayInputStream(new byte[0]);
+ }
+ };
+ try {
+ in.read(new byte[4]);
+ fail("did not throw an exception");
+ } catch (CorruptObjectException e) {
+ assertEquals(JGitText.get().baseLengthIncorrect, e.getMessage());
+ }
+ }
+
+ private void init(int baseSize, int dataSize) throws IOException {
+ base = rng.nextBytes(baseSize);
+ data = new byte[dataSize];
+ deltaEnc = new DeltaEncoder(deltaBuf, baseSize, dataSize);
+ }
+
+ private void copy(int offset, int len) throws IOException {
+ System.arraycopy(base, offset, data, dataPtr, len);
+ deltaEnc.copy(offset, len);
+ assertEquals(deltaBuf.size(), deltaEnc.getSize());
+ dataPtr += len;
+ }
+
+ private void insert(String text) throws IOException {
+ insert(Constants.encode(text));
+ }
+
+ private void insert(byte[] text) throws IOException {
+ System.arraycopy(text, 0, data, dataPtr, text.length);
+ deltaEnc.insert(text);
+ assertEquals(deltaBuf.size(), deltaEnc.getSize());
+ dataPtr += text.length;
+ }
+
+ private void assertValidState() throws IOException {
+ assertEquals("test filled example result", data.length, dataPtr);
+
+ delta = deltaBuf.toByteArray();
+ assertEquals(base.length, BinaryDelta.getBaseSize(delta));
+ assertEquals(data.length, BinaryDelta.getResultSize(delta));
+ assertTrue(Arrays.equals(data, BinaryDelta.apply(base, delta)));
+
+ byte[] act = new byte[data.length];
+ DeltaStream in = open();
+ assertEquals(data.length, in.getSize());
+ assertEquals(data.length, in.read(act));
+ assertEquals(-1, in.read());
+ assertTrue(Arrays.equals(data, act));
+ }
+
+ private DeltaStream open() throws IOException {
+ return new DeltaStream(new ByteArrayInputStream(delta)) {
+ @Override
+ protected long getBaseSize() throws IOException {
+ return base.length;
+ }
+
+ @Override
+ protected InputStream openBase() throws IOException {
+ return new ByteArrayInputStream(base);
+ }
+ };
+ }
+}
diff --git a/org.eclipse.jgit.test/tst/org/eclipse/jgit/transport/BundleWriterTest.java b/org.eclipse.jgit.test/tst/org/eclipse/jgit/transport/BundleWriterTest.java
index 2d6aa28..cc70562 100644
--- a/org.eclipse.jgit.test/tst/org/eclipse/jgit/transport/BundleWriterTest.java
+++ b/org.eclipse.jgit.test/tst/org/eclipse/jgit/transport/BundleWriterTest.java
@@ -148,12 +148,12 @@ private FetchResult fetchFromBundle(final Repository newRepo,
throws FileNotFoundException, IOException {
final BundleWriter bw;
- bw = new BundleWriter(db, NullProgressMonitor.INSTANCE);
+ bw = new BundleWriter(db);
bw.include(name, ObjectId.fromString(anObjectToInclude));
if (assume != null)
bw.assume(assume);
final ByteArrayOutputStream out = new ByteArrayOutputStream();
- bw.writeBundle(out);
+ bw.writeBundle(NullProgressMonitor.INSTANCE, out);
return out.toByteArray();
}
diff --git a/org.eclipse.jgit.test/tst/org/eclipse/jgit/transport/IndexPackTest.java b/org.eclipse.jgit.test/tst/org/eclipse/jgit/transport/IndexPackTest.java
index e18f741..110804f 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.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 40c719f..b331f9c 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.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.revwalk.RevBlob;
import org.eclipse.jgit.revwalk.RevCommit;
import org.eclipse.jgit.revwalk.RevTree;
+import org.eclipse.jgit.storage.file.ObjectDirectory;
import org.eclipse.jgit.util.NB;
import org.eclipse.jgit.util.TemporaryBuffer;
@@ -176,7 +176,7 @@ public void testSuccess() throws Exception {
// Verify the only storage of b is our packed delta above.
//
ObjectDirectory od = (ObjectDirectory) src.getObjectDatabase();
- assertTrue("has b", od.hasObject(b));
+ assertTrue("has b", src.hasObject(b));
assertFalse("b not loose", od.fileFor(b).exists());
// Now use b but in a different commit than what is hidden.
@@ -255,7 +255,7 @@ public void testCreateBranchAtHiddenCommitFails() throws Exception {
}
public void testUsingHiddenDeltaBaseFails() throws Exception {
- final TemporaryBuffer.Heap pack = new TemporaryBuffer.Heap(64);
+ final TemporaryBuffer.Heap pack = new TemporaryBuffer.Heap(1024);
packHeader(pack, 1);
pack.write((Constants.OBJ_REF_DELTA) << 4 | 4);
b.copyRawTo(pack);
@@ -292,18 +292,18 @@ public void testUsingHiddenDeltaBaseFails() throws Exception {
public void testUsingHiddenCommonBlobFails() throws Exception {
// Try to use the 'b' blob that is hidden.
//
- TestRepository s = new TestRepository(src);
+ TestRepository<Repository> s = new TestRepository<Repository>(src);
RevCommit N = s.commit().parent(B).add("q", s.blob("b")).create();
// But don't include it in the pack.
//
- final TemporaryBuffer.Heap pack = new TemporaryBuffer.Heap(64);
+ final TemporaryBuffer.Heap pack = new TemporaryBuffer.Heap(1024);
packHeader(pack, 2);
- copy(pack, src.openObject(N));
- copy(pack,src.openObject(s.parseBody(N).getTree()));
+ copy(pack, src.open(N));
+ copy(pack,src.open(s.parseBody(N).getTree()));
digest(pack);
- final TemporaryBuffer.Heap inBuf = new TemporaryBuffer.Heap(256);
+ final TemporaryBuffer.Heap inBuf = new TemporaryBuffer.Heap(1024);
final PacketLineOut inPckLine = new PacketLineOut(inBuf);
inPckLine.writeString(ObjectId.zeroId().name() + ' ' + N.name() + ' '
+ "refs/heads/s" + '\0'
@@ -333,19 +333,19 @@ public void testUsingHiddenCommonBlobFails() throws Exception {
public void testUsingUnknownBlobFails() throws Exception {
// Try to use the 'n' blob that is not on the server.
//
- TestRepository s = new TestRepository(src);
+ TestRepository<Repository> s = new TestRepository<Repository>(src);
RevBlob n = s.blob("n");
RevCommit N = s.commit().parent(B).add("q", n).create();
// But don't include it in the pack.
//
- final TemporaryBuffer.Heap pack = new TemporaryBuffer.Heap(64);
+ final TemporaryBuffer.Heap pack = new TemporaryBuffer.Heap(1024);
packHeader(pack, 2);
- copy(pack, src.openObject(N));
- copy(pack,src.openObject(s.parseBody(N).getTree()));
+ copy(pack, src.open(N));
+ copy(pack,src.open(s.parseBody(N).getTree()));
digest(pack);
- final TemporaryBuffer.Heap inBuf = new TemporaryBuffer.Heap(256);
+ final TemporaryBuffer.Heap inBuf = new TemporaryBuffer.Heap(1024);
final PacketLineOut inPckLine = new PacketLineOut(inBuf);
inPckLine.writeString(ObjectId.zeroId().name() + ' ' + N.name() + ' '
+ "refs/heads/s" + '\0'
@@ -373,18 +373,18 @@ public void testUsingUnknownBlobFails() throws Exception {
}
public void testUsingUnknownTreeFails() throws Exception {
- TestRepository s = new TestRepository(src);
+ TestRepository<Repository> s = new TestRepository<Repository>(src);
RevCommit N = s.commit().parent(B).add("q", s.blob("a")).create();
RevTree t = s.parseBody(N).getTree();
// Don't include the tree in the pack.
//
- final TemporaryBuffer.Heap pack = new TemporaryBuffer.Heap(64);
+ final TemporaryBuffer.Heap pack = new TemporaryBuffer.Heap(1024);
packHeader(pack, 1);
- copy(pack, src.openObject(N));
+ copy(pack, src.open(N));
digest(pack);
- final TemporaryBuffer.Heap inBuf = new TemporaryBuffer.Heap(256);
+ final TemporaryBuffer.Heap inBuf = new TemporaryBuffer.Heap(1024);
final PacketLineOut inPckLine = new PacketLineOut(inBuf);
inPckLine.writeString(ObjectId.zeroId().name() + ' ' + N.name() + ' '
+ "refs/heads/s" + '\0'
diff --git a/org.eclipse.jgit.test/tst/org/eclipse/jgit/transport/TransportTest.java b/org.eclipse.jgit.test/tst/org/eclipse/jgit/transport/TransportTest.java
index e351825..a6bdd88 100644
--- a/org.eclipse.jgit.test/tst/org/eclipse/jgit/transport/TransportTest.java
+++ b/org.eclipse.jgit.test/tst/org/eclipse/jgit/transport/TransportTest.java
@@ -48,7 +48,7 @@
import java.util.Collection;
import java.util.Collections;
-import org.eclipse.jgit.lib.RepositoryConfig;
+import org.eclipse.jgit.lib.Config;
import org.eclipse.jgit.lib.SampleDataRepositoryTestCase;
public class TransportTest extends SampleDataRepositoryTestCase {
@@ -59,7 +59,7 @@ public class TransportTest extends SampleDataRepositoryTestCase {
@Override
public void setUp() throws Exception {
super.setUp();
- final RepositoryConfig config = db.getConfig();
+ final Config config = db.getConfig();
remoteConfig = new RemoteConfig(config, "test");
remoteConfig.addURI(new URIish("http://everyones.loves.git/u/2"));
transport = null;
diff --git a/org.eclipse.jgit.test/tst/org/eclipse/jgit/treewalk/AbstractTreeIteratorTest.java b/org.eclipse.jgit.test/tst/org/eclipse/jgit/treewalk/AbstractTreeIteratorTest.java
index e96445a..12c1148 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 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 FakeTreeIterator(String pathName, FileMode fileMode) {
}
@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 111264b..1ea2dc6 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 void testAtEOF() throws Exception {
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 void stopWalk() {
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 eb08e49..f939c90 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 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 void testSimpleIterate() throws Exception {
assertFalse(top.eof());
assertEquals(FileMode.TREE.getBits(), top.mode);
- final AbstractTreeIterator sub = top.createSubtreeIterator(db);
+ final ObjectReader reader = db.newObjectReader();
+ final AbstractTreeIterator sub = top.createSubtreeIterator(reader);
assertTrue(sub instanceof FileTreeIterator);
final FileTreeIterator subfti = (FileTreeIterator) sub;
assertTrue(sub.first());
diff --git a/org.eclipse.jgit.test/tst/org/eclipse/jgit/treewalk/FileTreeIteratorWithTimeControl.java b/org.eclipse.jgit.test/tst/org/eclipse/jgit/treewalk/FileTreeIteratorWithTimeControl.java
new file mode 100644
index 0000000..bb76d00
--- /dev/null
+++ b/org.eclipse.jgit.test/tst/org/eclipse/jgit/treewalk/FileTreeIteratorWithTimeControl.java
@@ -0,0 +1,108 @@
+/*
+ * Copyright (C) 2010, Christian Halstrick <christian.halstrick@sap.com>
+ * and other copyright owners as documented in the project's IP log.
+ *
+ * This program and the accompanying materials are made available
+ * under the terms of the Eclipse Distribution License v1.0 which
+ * accompanies this distribution, is reproduced below, and is
+ * available at http://www.eclipse.org/org/documents/edl-v10.php
+ *
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or
+ * without modification, are permitted provided that the following
+ * conditions are met:
+ *
+ * - Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ *
+ * - Redistributions in binary form must reproduce the above
+ * copyright notice, this list of conditions and the following
+ * disclaimer in the documentation and/or other materials provided
+ * with the distribution.
+ *
+ * - Neither the name of the Eclipse Foundation, Inc. nor the
+ * names of its contributors may be used to endorse or promote
+ * products derived from this software without specific prior
+ * written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND
+ * CONTRIBUTORS "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES,
+ * INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
+ * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR
+ * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
+ * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT
+ * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
+ * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER
+ * CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT,
+ * STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
+ * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF
+ * ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ */
+package org.eclipse.jgit.treewalk;
+
+import java.io.File;
+import java.util.SortedSet;
+import java.util.TreeSet;
+
+import org.eclipse.jgit.lib.ObjectReader;
+import org.eclipse.jgit.lib.Repository;
+import org.eclipse.jgit.util.FS;
+
+/**
+ * A {@link FileTreeIterator} used in tests which allows to specify explicitly
+ * what will be returned by {@link #getEntryLastModified()}. This allows to
+ * write tests where certain files have to have the same modification time.
+ * <p>
+ * This iterator is configured by a list of strictly increasing long values
+ * t(0), t(1), ..., t(n). For each file with a modification between t(x) and
+ * t(x+1) [ t(x) <= time < t(x+1) ] this iterator will report t(x). For files
+ * with a modification time smaller t(0) a modification time of 0 is returned.
+ * For files with a modification time greater or equal t(n) t(n) will be
+ * returned.
+ * <p>
+ * This class was written especially to test racy-git problems
+ */
+public class FileTreeIteratorWithTimeControl extends FileTreeIterator {
+ private TreeSet<Long> modTimes;
+
+ public FileTreeIteratorWithTimeControl(FileTreeIterator p, Repository repo,
+ TreeSet<Long> modTimes) {
+ super(p, repo.getWorkTree(), repo.getFS());
+ this.modTimes = modTimes;
+ }
+
+ public FileTreeIteratorWithTimeControl(FileTreeIterator p, File f, FS fs,
+ TreeSet<Long> modTimes) {
+ super(p, f, fs);
+ this.modTimes = modTimes;
+ }
+
+ public FileTreeIteratorWithTimeControl(Repository repo,
+ TreeSet<Long> modTimes) {
+ super(repo);
+ this.modTimes = modTimes;
+ }
+
+ public FileTreeIteratorWithTimeControl(File f, FS fs,
+ TreeSet<Long> modTimes) {
+ super(f, fs);
+ this.modTimes = modTimes;
+ }
+
+ @Override
+ public AbstractTreeIterator createSubtreeIterator(final ObjectReader reader) {
+ return new FileTreeIteratorWithTimeControl(this,
+ ((FileEntry) current()).file, fs, modTimes);
+ }
+
+ @Override
+ public long getEntryLastModified() {
+ if (modTimes == null)
+ return 0;
+ Long cutOff = Long.valueOf(super.getEntryLastModified() + 1);
+ SortedSet<Long> head = modTimes.headSet(cutOff);
+ return head.isEmpty() ? 0 : head.last().longValue();
+ }
+}
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 35298b8..e59b7c1 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 void testNoDF_NoGap() throws Exception {
}
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();
@@ -120,16 +120,20 @@ public void testDF_NoGap() throws Exception {
tw.addTree(new DirCacheIterator(tree1));
assertModes("a", REGULAR_FILE, TREE, tw);
+ assertTrue(tw.isDirectoryFileConflict());
assertTrue(tw.isSubtree());
tw.enterSubtree();
assertModes("a/b", MISSING, REGULAR_FILE, tw);
+ assertTrue(tw.isDirectoryFileConflict());
assertModes("a.b", EXECUTABLE_FILE, MISSING, tw);
+ assertFalse(tw.isDirectoryFileConflict());
assertModes("a0b", SYMLINK, MISSING, tw);
+ assertFalse(tw.isDirectoryFileConflict());
}
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();
@@ -153,15 +157,19 @@ public void testDF_GapByOne() throws Exception {
assertModes("a", REGULAR_FILE, TREE, tw);
assertTrue(tw.isSubtree());
+ assertTrue(tw.isDirectoryFileConflict());
tw.enterSubtree();
assertModes("a/b", MISSING, REGULAR_FILE, tw);
+ assertTrue(tw.isDirectoryFileConflict());
assertModes("a.b", EXECUTABLE_FILE, EXECUTABLE_FILE, tw);
+ assertFalse(tw.isDirectoryFileConflict());
assertModes("a0b", SYMLINK, MISSING, tw);
+ assertFalse(tw.isDirectoryFileConflict());
}
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();
@@ -185,10 +193,57 @@ public void testDF_SkipsSeenSubtree() throws Exception {
assertModes("a", REGULAR_FILE, TREE, tw);
assertTrue(tw.isSubtree());
+ assertTrue(tw.isDirectoryFileConflict());
tw.enterSubtree();
assertModes("a/b", MISSING, REGULAR_FILE, tw);
+ assertTrue(tw.isDirectoryFileConflict());
assertModes("a.b", MISSING, EXECUTABLE_FILE, tw);
+ assertFalse(tw.isDirectoryFileConflict());
assertModes("a0b", SYMLINK, SYMLINK, tw);
+ assertFalse(tw.isDirectoryFileConflict());
+ }
+
+ public void testDF_DetectConflict() throws Exception {
+ final DirCache tree0 = db.readDirCache();
+ final DirCache tree1 = db.readDirCache();
+ {
+ final DirCacheBuilder b0 = tree0.builder();
+ final DirCacheBuilder b1 = tree1.builder();
+
+ b0.add(makeEntry("0", REGULAR_FILE));
+ b0.add(makeEntry("a", REGULAR_FILE));
+ b1.add(makeEntry("0", REGULAR_FILE));
+ b1.add(makeEntry("a.b", REGULAR_FILE));
+ b1.add(makeEntry("a/b", REGULAR_FILE));
+ b1.add(makeEntry("a/c/e", REGULAR_FILE));
+
+ b0.finish();
+ b1.finish();
+ assertEquals(2, tree0.getEntryCount());
+ assertEquals(4, tree1.getEntryCount());
+ }
+
+ final NameConflictTreeWalk tw = new NameConflictTreeWalk(db);
+ tw.reset();
+ tw.addTree(new DirCacheIterator(tree0));
+ tw.addTree(new DirCacheIterator(tree1));
+
+ assertModes("0", REGULAR_FILE, REGULAR_FILE, tw);
+ assertFalse(tw.isDirectoryFileConflict());
+ assertModes("a", REGULAR_FILE, TREE, tw);
+ assertTrue(tw.isSubtree());
+ assertTrue(tw.isDirectoryFileConflict());
+ tw.enterSubtree();
+ assertModes("a/b", MISSING, REGULAR_FILE, tw);
+ assertTrue(tw.isDirectoryFileConflict());
+ assertModes("a/c", MISSING, TREE, tw);
+ assertTrue(tw.isDirectoryFileConflict());
+ tw.enterSubtree();
+ assertModes("a/c/e", MISSING, REGULAR_FILE, tw);
+ assertTrue(tw.isDirectoryFileConflict());
+
+ assertModes("a.b", MISSING, REGULAR_FILE, tw);
+ assertFalse(tw.isDirectoryFileConflict());
}
private DirCacheEntry makeEntry(final String path, final FileMode mode)
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 d136b8f..274df5b 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 void testResetDoesNotAffectPostOrder() throws Exception {
}
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 void testNoPostOrder() throws Exception {
}
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 void testWithPostOrder_EnterSubtree() throws Exception {
}
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 1aaefc4..302eada 100644
--- a/org.eclipse.jgit.test/tst/org/eclipse/jgit/treewalk/filter/PathSuffixFilterTestCase.java
+++ b/org.eclipse.jgit.test/tst/org/eclipse/jgit/treewalk/filter/PathSuffixFilterTestCase.java
@@ -43,6 +43,8 @@
package org.eclipse.jgit.treewalk.filter;
+import static org.eclipse.jgit.lib.Constants.OBJ_BLOB;
+
import java.io.IOException;
import java.util.LinkedList;
import java.util.List;
@@ -52,17 +54,17 @@
import org.eclipse.jgit.dircache.DirCacheEntry;
import org.eclipse.jgit.lib.FileMode;
import org.eclipse.jgit.lib.ObjectId;
-import org.eclipse.jgit.lib.ObjectWriter;
+import org.eclipse.jgit.lib.ObjectInserter;
import org.eclipse.jgit.lib.RepositoryTestCase;
import org.eclipse.jgit.treewalk.TreeWalk;
public class PathSuffixFilterTestCase extends RepositoryTestCase {
public void testNonRecursiveFiltering() throws IOException {
- final ObjectWriter ow = new ObjectWriter(db);
- final ObjectId aSth = ow.writeBlob("a.sth".getBytes());
- final ObjectId aTxt = ow.writeBlob("a.txt".getBytes());
- final DirCache dc = DirCache.read(db);
+ final ObjectInserter odi = db.newObjectInserter();
+ final ObjectId aSth = odi.insert(OBJ_BLOB, "a.sth".getBytes());
+ final ObjectId aTxt = odi.insert(OBJ_BLOB, "a.txt".getBytes());
+ final DirCache dc = db.readDirCache();
final DirCacheBuilder builder = dc.builder();
final DirCacheEntry aSthEntry = new DirCacheEntry("a.sth");
aSthEntry.setFileMode(FileMode.REGULAR_FILE);
@@ -73,7 +75,8 @@ public void testNonRecursiveFiltering() throws IOException {
builder.add(aSthEntry);
builder.add(aTxtEntry);
builder.finish();
- final ObjectId treeId = dc.writeTree(ow);
+ final ObjectId treeId = dc.writeTree(odi);
+ odi.flush();
final TreeWalk tw = new TreeWalk(db);
@@ -92,12 +95,12 @@ public void testNonRecursiveFiltering() throws IOException {
}
public void testRecursiveFiltering() throws IOException {
- final ObjectWriter ow = new ObjectWriter(db);
- final ObjectId aSth = ow.writeBlob("a.sth".getBytes());
- final ObjectId aTxt = ow.writeBlob("a.txt".getBytes());
- final ObjectId bSth = ow.writeBlob("b.sth".getBytes());
- final ObjectId bTxt = ow.writeBlob("b.txt".getBytes());
- final DirCache dc = DirCache.read(db);
+ final ObjectInserter odi = db.newObjectInserter();
+ final ObjectId aSth = odi.insert(OBJ_BLOB, "a.sth".getBytes());
+ final ObjectId aTxt = odi.insert(OBJ_BLOB, "a.txt".getBytes());
+ final ObjectId bSth = odi.insert(OBJ_BLOB, "b.sth".getBytes());
+ final ObjectId bTxt = odi.insert(OBJ_BLOB, "b.txt".getBytes());
+ final DirCache dc = db.readDirCache();
final DirCacheBuilder builder = dc.builder();
final DirCacheEntry aSthEntry = new DirCacheEntry("a.sth");
aSthEntry.setFileMode(FileMode.REGULAR_FILE);
@@ -116,7 +119,8 @@ public void testRecursiveFiltering() throws IOException {
builder.add(bSthEntry);
builder.add(bTxtEntry);
builder.finish();
- final ObjectId treeId = dc.writeTree(ow);
+ final ObjectId treeId = dc.writeTree(odi);
+ odi.flush();
final TreeWalk tw = new TreeWalk(db);
diff --git a/org.eclipse.jgit.test/tst/org/eclipse/jgit/util/ChangeIdUtilTest.java b/org.eclipse.jgit.test/tst/org/eclipse/jgit/util/ChangeIdUtilTest.java
index 3b15846..a15cadf 100644
--- a/org.eclipse.jgit.test/tst/org/eclipse/jgit/util/ChangeIdUtilTest.java
+++ b/org.eclipse.jgit.test/tst/org/eclipse/jgit/util/ChangeIdUtilTest.java
@@ -123,6 +123,13 @@ public void testHasChangeid() throws Exception {
call("has changeid\n\nBug: 33\nmore text\nSigned-off-by: me@you.too\nChange-Id: I0123456789012345678901234567890123456789\nAnd then some\n"));
}
+ public void testHasChangeidWithReplacement() throws Exception {
+ assertEquals(
+ "has changeid\n\nBug: 33\nmore text\nSigned-off-by: me@you.too\nChange-Id: I988d2d7a6f2c0578fccabd4ebd3cec0768bc7f9f\nAnd then some\n",
+ call("has changeid\n\nBug: 33\nmore text\nSigned-off-by: me@you.too\nChange-Id: I0123456789012345678901234567890123456789\nAnd then some\n",
+ true));
+ }
+
public void testOneliner() throws Exception {
assertEquals(
"oneliner\n\nChange-Id: I3a98091ce4470de88d52ae317fcd297e2339f063\n",
@@ -236,6 +243,47 @@ public void testChangeIdAlreadySet() throws Exception {
"Change-Id: I4f4e2e1e8568ddc1509baecb8c1270a1fb4b6da7\n");
}
+ public void testChangeIdAlreadySetWithReplacement() throws Exception {
+ // If a Change-Id is already present in the footer, the hook
+ // replaces the Change-Id with the new value..
+ //
+ assertEquals("a\n" + //
+ "\n" + //
+ "Change-Id: Ifa324efa85bfb3c8696a46a0f67fa70c35be5f5f\n",
+ call("a\n" + //
+ "\n" + //
+ "Change-Id: Iaeac9b4149291060228ef0154db2985a31111335\n",
+ true));
+ assertEquals("fix: this thing\n" + //
+ "\n" + //
+ "Change-Id: Ib63e4990a06412a3f24bd93bb160e98ac1bd412b\n",
+ call("fix: this thing\n" + //
+ "\n" + //
+ "Change-Id: I388bdaf52ed05b55e62a22d0a20d2c1ae0d33e7e\n",
+ true));
+ assertEquals("fix-a-widget: this thing\n" + //
+ "\n" + //
+ "Change-Id: If0444e4d0cabcf41b3d3b46b7e9a7a64a82117af\n",
+ call("fix-a-widget: this thing\n" + //
+ "\n" + //
+ "Change-Id: Id3bc5359d768a6400450283e12bdfb6cd135ea4b\n",
+ true));
+ assertEquals("FIX: this thing\n" + //
+ "\n" + //
+ "Change-Id: Iba5a3b2d5e5df46448f6daf362b6bfa775c6491d\n",
+ call("FIX: this thing\n" + //
+ "\n" + //
+ "Change-Id: I1b55098b5a2cce0b3f3da783dda50d5f79f873fa\n",
+ true));
+ assertEquals("Fix-A-Widget: this thing\n" + //
+ "\n" + //
+ "Change-Id: I2573d47c62c42429fbe424d70cfba931f8f87848\n",
+ call("Fix-A-Widget: this thing\n" + //
+ "\n" + //
+ "Change-Id: I4f4e2e1e8568ddc1509baecb8c1270a1fb4b6da7\n",
+ true));
+ }
+
public void testTimeAltersId() throws Exception {
assertEquals("a\n" + //
"\n" + //
@@ -513,11 +561,15 @@ private void hookDoesNotModify(final String in) throws Exception {
}
private String call(final String body) throws Exception {
+ return call(body, false);
+ }
+
+ private String call(final String body, boolean replaceExisting) throws Exception {
ObjectId computeChangeId = ChangeIdUtil.computeChangeId(treeId1,
parentId1, author, committer, body);
if (computeChangeId == null)
return body;
- return ChangeIdUtil.insertId(body, computeChangeId);
+ return ChangeIdUtil.insertId(body, computeChangeId, replaceExisting);
}
}
diff --git a/org.eclipse.jgit/META-INF/MANIFEST.MF b/org.eclipse.jgit/META-INF/MANIFEST.MF
index 51d440e..a8f39d9 100644
--- a/org.eclipse.jgit/META-INF/MANIFEST.MF
+++ b/org.eclipse.jgit/META-INF/MANIFEST.MF
@@ -9,6 +9,7 @@
org.eclipse.jgit.api;version="0.9.0",
org.eclipse.jgit.diff;version="0.9.0",
org.eclipse.jgit.dircache;version="0.9.0",
+ org.eclipse.jgit.events;version="0.9.0",
org.eclipse.jgit.errors;version="0.9.0",
org.eclipse.jgit.fnmatch;version="0.9.0",
org.eclipse.jgit.ignore;version="0.9.0",
@@ -19,6 +20,8 @@
org.eclipse.jgit.revplot;version="0.9.0",
org.eclipse.jgit.revwalk;version="0.9.0",
org.eclipse.jgit.revwalk.filter;version="0.9.0",
+ org.eclipse.jgit.storage.file;version="0.9.0",
+ org.eclipse.jgit.storage.pack;version="0.9.0",
org.eclipse.jgit.transport;version="0.9.0",
org.eclipse.jgit.treewalk;version="0.9.0",
org.eclipse.jgit.treewalk.filter;version="0.9.0",
diff --git a/org.eclipse.jgit/resources/org/eclipse/jgit/JGitText.properties b/org.eclipse.jgit/resources/org/eclipse/jgit/JGitText.properties
index ca1832e..a9878f8 100644
--- a/org.eclipse.jgit/resources/org/eclipse/jgit/JGitText.properties
+++ b/org.eclipse.jgit/resources/org/eclipse/jgit/JGitText.properties
@@ -127,7 +127,7 @@
duplicateRef=Duplicate ref: {0}
duplicateRemoteRefUpdateIsIllegal=Duplicate remote ref update is illegal. Affected remote name: {0}
duplicateStagesNotAllowed=Duplicate stages not allowed
-eitherGIT_DIRorGIT_WORK_TREEmustBePassed=Either GIT_DIR or GIT_WORK_TREE must be passed to Repository constructor
+eitherGitDirOrWorkTreeRequired=One of setGitDir or setWorkTree must be called.
emptyPathNotPermitted=Empty path not permitted.
encryptionError=Encryption error: {0}
endOfFileInEscape=End of file in escape
@@ -224,6 +224,7 @@
mergeStrategyDoesNotSupportHeads=merge strategy {0} does not support {1} heads to be merged into HEAD
mergeUsingStrategyResultedInDescription=Merge using strategy {0} resulted in: {1}. {2}
missingAccesskey=Missing accesskey.
+missingDeltaBase=delta base
missingForwardImageInGITBinaryPatch=Missing forward-image in GIT binary patch
missingObject=Missing {0} {1}
missingPrerequisiteCommits=missing prerequisite commits:
@@ -301,9 +302,12 @@
remoteHungUpUnexpectedly=remote hung up unexpectedly
remoteNameCantBeNull=Remote name can't be null.
renamesAlreadyFound=Renames have already been found.
+renamesBreakingModifies=Breaking apart modified file pairs
renamesFindingByContent=Finding renames by content similarity
renamesFindingExact=Finding exact renames
+renamesRejoiningModifies=Rejoining modified file pairs
repositoryAlreadyExists=Repository already exists: {0}
+repositoryConfigFileInvalid=Repository config file {0} invalid {1}
repositoryIsRequired=Repository is required.
repositoryNotFound=repository not found: {0}
repositoryState_applyMailbox=Apply mailbox
@@ -331,6 +335,7 @@
staleRevFlagsOn=Stale RevFlags on {0}
startingReadStageWithoutWrittenRequestDataPendingIsNotSupported=Starting read stage without written request data pending is not supported
statelessRPCRequiresOptionToBeEnabled=stateless RPC requires {0} to be enabled
+submodulesNotSupported=Submodules are not supported
symlinkCannotBeWrittenAsTheLinkTarget=Symlink "{0}" cannot be written as the link target cannot be read from within Java.
tSizeMustBeGreaterOrEqual1=tSize must be >= 1
theFactoryMustNotBeNull=The factory must not be null
diff --git a/org.eclipse.jgit/src/org/eclipse/jgit/JGitText.java b/org.eclipse.jgit/src/org/eclipse/jgit/JGitText.java
index 71e5e4a..461242c 100644
--- a/org.eclipse.jgit/src/org/eclipse/jgit/JGitText.java
+++ b/org.eclipse.jgit/src/org/eclipse/jgit/JGitText.java
@@ -187,7 +187,7 @@ public static JGitText get() {
/***/ public String duplicateRef;
/***/ public String duplicateRemoteRefUpdateIsIllegal;
/***/ public String duplicateStagesNotAllowed;
- /***/ public String eitherGIT_DIRorGIT_WORK_TREEmustBePassed;
+ /***/ public String eitherGitDirOrWorkTreeRequired;
/***/ public String emptyPathNotPermitted;
/***/ public String encryptionError;
/***/ public String endOfFileInEscape;
@@ -284,6 +284,7 @@ public static JGitText get() {
/***/ public String mergeStrategyDoesNotSupportHeads;
/***/ public String mergeUsingStrategyResultedInDescription;
/***/ public String missingAccesskey;
+ /***/ public String missingDeltaBase;
/***/ public String missingForwardImageInGITBinaryPatch;
/***/ public String missingObject;
/***/ public String missingPrerequisiteCommits;
@@ -360,9 +361,12 @@ public static JGitText get() {
/***/ public String remoteHungUpUnexpectedly;
/***/ public String remoteNameCantBeNull;
/***/ public String renamesAlreadyFound;
+ /***/ public String renamesBreakingModifies;
/***/ public String renamesFindingByContent;
/***/ public String renamesFindingExact;
+ /***/ public String renamesRejoiningModifies;
/***/ public String repositoryAlreadyExists;
+ /***/ public String repositoryConfigFileInvalid;
/***/ public String repositoryIsRequired;
/***/ public String repositoryNotFound;
/***/ public String repositoryState_applyMailbox;
@@ -390,6 +394,7 @@ public static JGitText get() {
/***/ public String staleRevFlagsOn;
/***/ public String startingReadStageWithoutWrittenRequestDataPendingIsNotSupported;
/***/ public String statelessRPCRequiresOptionToBeEnabled;
+ /***/ public String submodulesNotSupported;
/***/ public String symlinkCannotBeWrittenAsTheLinkTarget;
/***/ public String tSizeMustBeGreaterOrEqual1;
/***/ public String theFactoryMustNotBeNull;
diff --git a/org.eclipse.jgit/src/org/eclipse/jgit/api/AddCommand.java b/org.eclipse.jgit/src/org/eclipse/jgit/api/AddCommand.java
index 238d1d2..f7d4da4 100644
--- a/org.eclipse.jgit/src/org/eclipse/jgit/api/AddCommand.java
+++ b/org.eclipse.jgit/src/org/eclipse/jgit/api/AddCommand.java
@@ -58,6 +58,7 @@
import org.eclipse.jgit.lib.Repository;
import org.eclipse.jgit.treewalk.FileTreeIterator;
import org.eclipse.jgit.treewalk.TreeWalk;
+import org.eclipse.jgit.treewalk.WorkingTreeIterator;
import org.eclipse.jgit.treewalk.filter.PathFilterGroup;
/**
@@ -73,6 +74,10 @@ public class AddCommand extends GitCommand<DirCache> {
private Collection<String> filepatterns;
+ private WorkingTreeIterator workingTreeIterator;
+
+ private boolean update = false;
+
/**
*
* @param repo
@@ -97,6 +102,16 @@ public AddCommand addFilepattern(String filepattern) {
}
/**
+ * Allow clients to provide their own implementation of a FileTreeIterator
+ * @param f
+ * @return {@code this}
+ */
+ public AddCommand setWorkingTreeIterator(WorkingTreeIterator f) {
+ workingTreeIterator = f;
+ return this;
+ }
+
+ /**
* Executes the {@code Add} command. Each instance of this class should only
* be used for one invocation of the command. Don't call this method twice
* on an instance.
@@ -109,9 +124,12 @@ public DirCache call() throws NoFilepatternException {
throw new NoFilepatternException(JGitText.get().atLeastOnePatternIsRequired);
checkCallable();
DirCache dc = null;
+ boolean addAll = false;
+ if (filepatterns.contains("."))
+ addAll = true;
try {
- dc = DirCache.lock(repo);
+ dc = repo.lockDirCache();
ObjectWriter ow = new ObjectWriter(repo);
DirCacheIterator c;
@@ -119,36 +137,43 @@ public DirCache call() throws NoFilepatternException {
final TreeWalk tw = new TreeWalk(repo);
tw.reset();
tw.addTree(new DirCacheBuildIterator(builder));
- FileTreeIterator fileTreeIterator = new FileTreeIterator(
- repo.getWorkDir(), repo.getFS());
- tw.addTree(fileTreeIterator);
+ if (workingTreeIterator == null)
+ workingTreeIterator = new FileTreeIterator(repo);
+ tw.addTree(workingTreeIterator);
tw.setRecursive(true);
- tw.setFilter(PathFilterGroup.createFromStrings(filepatterns));
+ if (!addAll)
+ tw.setFilter(PathFilterGroup.createFromStrings(filepatterns));
String lastAddedFile = null;
while (tw.next()) {
String path = tw.getPathString();
- final File file = new File(repo.getWorkDir(), path);
+ final File file = new File(repo.getWorkTree(), path);
+ WorkingTreeIterator f = tw.getTree(1, WorkingTreeIterator.class);
+ if (tw.getTree(0, DirCacheIterator.class) == null &&
+ f != null && f.isEntryIgnored()) {
+ // file is not in index but is ignored, do nothing
+ }
// In case of an existing merge conflict the
// DirCacheBuildIterator iterates over all stages of
// this path, we however want to add only one
// new DirCacheEntry per path.
- if (!(path.equals(lastAddedFile))) {
- FileTreeIterator f = tw.getTree(1, FileTreeIterator.class);
- if (f != null) { // the file exists
- DirCacheEntry entry = new DirCacheEntry(path);
- entry.setLength((int)f.getEntryLength());
- entry.setLastModified(f.getEntryLastModified());
- entry.setFileMode(f.getEntryFileMode());
- entry.setObjectId(ow.writeBlob(file));
+ else if (!(path.equals(lastAddedFile))) {
+ if (!(update && tw.getTree(0, DirCacheIterator.class) == null)) {
+ if (f != null) { // the file exists
+ DirCacheEntry entry = new DirCacheEntry(path);
+ entry.setLength((int)f.getEntryLength());
+ entry.setLastModified(f.getEntryLastModified());
+ entry.setFileMode(f.getEntryFileMode());
+ entry.setObjectId(ow.writeBlob(file));
- builder.add(entry);
- lastAddedFile = path;
- } else {
- c = tw.getTree(0, DirCacheIterator.class);
- builder.add(c.getDirCacheEntry());
+ builder.add(entry);
+ lastAddedFile = path;
+ } else if (!update){
+ c = tw.getTree(0, DirCacheIterator.class);
+ builder.add(c.getDirCacheEntry());
+ }
}
}
}
@@ -165,4 +190,29 @@ public DirCache call() throws NoFilepatternException {
return dc;
}
+ /**
+ * @param update
+ * If set to true, the command only matches {@code filepattern}
+ * against already tracked files in the index rather than the
+ * working tree. That means that it will never stage new files,
+ * but that it will stage modified new contents of tracked files
+ * and that it will remove files from the index if the
+ * corresponding files in the working tree have been removed.
+ * In contrast to the git command line a {@code filepattern} must
+ * exist also if update is set to true as there is no
+ * concept of a working directory here.
+ *
+ * @return {@code this}
+ */
+ public AddCommand setUpdate(boolean update) {
+ this.update = update;
+ return this;
+ }
+
+ /**
+ * @return is the parameter update is set
+ */
+ public boolean isUpdate() {
+ return update;
+ }
}
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 eef952e..ae4b334 100644
--- a/org.eclipse.jgit/src/org/eclipse/jgit/api/CommitCommand.java
+++ b/org.eclipse.jgit/src/org/eclipse/jgit/api/CommitCommand.java
@@ -42,7 +42,6 @@
*/
package org.eclipse.jgit.api;
-import java.io.File;
import java.io.IOException;
import java.text.MessageFormat;
import java.util.LinkedList;
@@ -54,13 +53,13 @@
import org.eclipse.jgit.lib.Commit;
import org.eclipse.jgit.lib.Constants;
import org.eclipse.jgit.lib.ObjectId;
-import org.eclipse.jgit.lib.ObjectWriter;
+import org.eclipse.jgit.lib.ObjectInserter;
import org.eclipse.jgit.lib.PersonIdent;
import org.eclipse.jgit.lib.Ref;
import org.eclipse.jgit.lib.RefUpdate;
-import org.eclipse.jgit.lib.RefUpdate.Result;
import org.eclipse.jgit.lib.Repository;
import org.eclipse.jgit.lib.RepositoryState;
+import org.eclipse.jgit.lib.RefUpdate.Result;
import org.eclipse.jgit.revwalk.RevCommit;
import org.eclipse.jgit.revwalk.RevWalk;
@@ -80,6 +79,8 @@ public class CommitCommand extends GitCommand<RevCommit> {
private String message;
+ private boolean all;
+
/**
* parents this commit should have. The current HEAD will be in this list
* and also all commits mentioned in .git/MERGE_HEAD
@@ -128,6 +129,18 @@ public RevCommit call() throws NoHeadException, NoMessageException,
processOptions(state);
try {
+ if (all && !repo.isBare() && repo.getWorkTree() != null) {
+ Git git = new Git(repo);
+ try {
+ git.add()
+ .addFilepattern(".")
+ .setUpdate(true).call();
+ } catch (NoFilepatternException e) {
+ // should really not happen
+ throw new JGitInternalException(e.getMessage(), e);
+ }
+ }
+
Ref head = repo.getRef(Constants.HEAD);
if (head == null)
throw new NoHeadException(
@@ -139,54 +152,64 @@ public RevCommit call() throws NoHeadException, NoMessageException,
parents.add(0, headId);
// lock the index
- DirCache index = DirCache.lock(repo);
+ DirCache index = repo.lockDirCache();
try {
- ObjectWriter repoWriter = new ObjectWriter(repo);
+ ObjectInserter odi = repo.newObjectInserter();
+ try {
+ // Write the index as tree to the object database. This may
+ // fail for example when the index contains unmerged paths
+ // (unresolved conflicts)
+ ObjectId indexTreeId = index.writeTree(odi);
- // Write the index as tree to the object database. This may fail
- // for example when the index contains unmerged pathes
- // (unresolved conflicts)
- ObjectId indexTreeId = index.writeTree(repoWriter);
+ // Create a Commit object, populate it and write it
+ Commit commit = new Commit(repo);
+ commit.setCommitter(committer);
+ commit.setAuthor(author);
+ commit.setMessage(message);
- // Create a Commit object, populate it and write it
- Commit commit = new Commit(repo);
- commit.setCommitter(committer);
- commit.setAuthor(author);
- commit.setMessage(message);
+ commit.setParentIds(parents.toArray(new ObjectId[] {}));
+ commit.setTreeId(indexTreeId);
+ ObjectId commitId = odi.insert(Constants.OBJ_COMMIT, odi
+ .format(commit));
+ odi.flush();
- commit.setParentIds(parents.toArray(new ObjectId[]{}));
- commit.setTreeId(indexTreeId);
- ObjectId commitId = repoWriter.writeCommit(commit);
+ 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);
- RevCommit revCommit = new RevWalk(repo).parseCommit(commitId);
- RefUpdate ru = repo.updateRef(Constants.HEAD);
- ru.setNewObjectId(commitId);
- ru.setRefLogMessage("commit : " + revCommit.getShortMessage(),
- false);
-
- ru.setExpectedOldObjectId(headId);
- Result rc = ru.update();
- switch (rc) {
- case NEW:
- case FAST_FORWARD:
- setCallable(false);
- if (state == RepositoryState.MERGING_RESOLVED) {
- // Commit was successful. Now delete the files
- // used for merge commits
- new File(repo.getDirectory(), Constants.MERGE_HEAD)
- .delete();
- new File(repo.getDirectory(), Constants.MERGE_MSG)
- .delete();
+ ru.setExpectedOldObjectId(headId);
+ Result rc = ru.update();
+ switch (rc) {
+ case NEW:
+ case FAST_FORWARD: {
+ setCallable(false);
+ if (state == RepositoryState.MERGING_RESOLVED) {
+ // Commit was successful. Now delete the files
+ // used for merge commits
+ repo.writeMergeCommitMsg(null);
+ repo.writeMergeHeads(null);
+ }
+ return revCommit;
+ }
+ case REJECTED:
+ case LOCK_FAILURE:
+ throw new ConcurrentRefUpdateException(JGitText
+ .get().couldNotLockHEAD, ru.getRef(), rc);
+ default:
+ throw new JGitInternalException(MessageFormat
+ .format(JGitText.get().updatingRefFailed,
+ Constants.HEAD,
+ commitId.toString(), rc));
+ }
+ } finally {
+ revWalk.release();
}
- return revCommit;
- case REJECTED:
- case LOCK_FAILURE:
- throw new ConcurrentRefUpdateException(
- JGitText.get().couldNotLockHEAD, ru.getRef(), rc);
- default:
- throw new JGitInternalException(MessageFormat.format(
- JGitText.get().updatingRefFailed
- , Constants.HEAD, commitId.toString(), rc));
+ } finally {
+ odi.release();
}
} finally {
index.unlock();
@@ -344,4 +367,19 @@ public CommitCommand setAuthor(String name, String email) {
public PersonIdent getAuthor() {
return author;
}
+
+ /**
+ * If set to true the Commit command automatically stages files that have
+ * been modified and deleted, but new files you not known by the repository
+ * are not affected. This corresponds to the parameter -a on the command
+ * line.
+ *
+ * @param all
+ * @return {@code this}
+ */
+ public CommitCommand setAll(boolean all) {
+ this.all = all;
+ return this;
+ }
+
}
diff --git a/org.eclipse.jgit/src/org/eclipse/jgit/api/ConcurrentRefUpdateException.java b/org.eclipse.jgit/src/org/eclipse/jgit/api/ConcurrentRefUpdateException.java
index 6f681b6..2a5f7f9 100644
--- a/org.eclipse.jgit/src/org/eclipse/jgit/api/ConcurrentRefUpdateException.java
+++ b/org.eclipse.jgit/src/org/eclipse/jgit/api/ConcurrentRefUpdateException.java
@@ -47,7 +47,7 @@
* Exception thrown when a command wants to update a ref but failed because
* another process is accessing (or even also updating) the ref.
*
- * @see RefUpdate.Result#LOCK_FAILURE
+ * @see org.eclipse.jgit.lib.RefUpdate.Result#LOCK_FAILURE
*/
public class ConcurrentRefUpdateException extends GitAPIException {
private static final long serialVersionUID = 1L;
diff --git a/org.eclipse.jgit/src/org/eclipse/jgit/api/LogCommand.java b/org.eclipse.jgit/src/org/eclipse/jgit/api/LogCommand.java
index 414fac4..61a87b4 100644
--- a/org.eclipse.jgit/src/org/eclipse/jgit/api/LogCommand.java
+++ b/org.eclipse.jgit/src/org/eclipse/jgit/api/LogCommand.java
@@ -64,7 +64,7 @@
* This is currently a very basic implementation which takes only one starting
* revision as option.
*
- * @TODO add more options (revision ranges, sorting, ...)
+ * TODO: add more options (revision ranges, sorting, ...)
*
* @see <a href="http://www.kernel.org/pub/software/scm/git/docs/git-log.html"
* >Git documentation about Log</a>
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 00a0309..972aa61 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 MergeResult call() throws NoHeadException,
// Check for FAST_FORWARD, ALREADY_UP_TO_DATE
RevWalk revWalk = new RevWalk(repo);
- RevCommit headCommit = revWalk.lookupCommit(head.getObjectId());
+ try {
+ RevCommit headCommit = revWalk.lookupCommit(head.getObjectId());
- Ref ref = commits.get(0);
+ Ref ref = commits.get(0);
- refLogMessage.append(ref.getName());
+ refLogMessage.append(ref.getName());
- // handle annotated tags
- ObjectId objectId = ref.getPeeledObjectId();
- if (objectId == null)
- objectId = ref.getObjectId();
+ // 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);
+ RevCommit srcCommit = revWalk.lookupCommit(objectId);
+ if (revWalk.isMergedInto(srcCommit, headCommit)) {
+ setCallable(false);
+ return new MergeResult(headCommit,
+ MergeStatus.ALREADY_UP_TO_DATE, mergeStrategy);
+ } else if (revWalk.isMergedInto(headCommit, srcCommit)) {
+ // FAST_FORWARD detected: skip doing a real merge but only
+ // update HEAD
+ refLogMessage.append(": " + MergeStatus.FAST_FORWARD);
+ checkoutNewHead(revWalk, headCommit, srcCommit);
+ updateHead(refLogMessage, srcCommit, head.getObjectId());
+ setCallable(false);
+ return new MergeResult(srcCommit, MergeStatus.FAST_FORWARD,
+ mergeStrategy);
+ } else {
+ return new MergeResult(
+ headCommit,
+ MergeResult.MergeStatus.NOT_SUPPORTED,
+ mergeStrategy,
+ JGitText.get().onlyAlreadyUpToDateAndFastForwardMergesAreAvailable);
+ }
+ } finally {
+ revWalk.release();
}
} catch (IOException e) {
throw new JGitInternalException(
@@ -163,7 +167,7 @@ private void checkoutNewHead(RevWalk revWalk, RevCommit headCommit,
RevCommit newHeadCommit) throws IOException, CheckoutConflictException {
GitIndex index = repo.getIndex();
- File workDir = repo.getWorkDir();
+ File workDir = repo.getWorkTree();
if (workDir != null) {
WorkDirCheckout workDirCheckout = new WorkDirCheckout(repo,
workDir, headCommit.asCommit(revWalk).getTree(), index,
diff --git a/org.eclipse.jgit/src/org/eclipse/jgit/diff/DiffEntry.java b/org.eclipse.jgit/src/org/eclipse/jgit/diff/DiffEntry.java
index 304e4cf..55ecc4e 100644
--- a/org.eclipse.jgit/src/org/eclipse/jgit/diff/DiffEntry.java
+++ b/org.eclipse.jgit/src/org/eclipse/jgit/diff/DiffEntry.java
@@ -57,7 +57,8 @@
/** A value class representing a change to a file */
public class DiffEntry {
- private static final AbbreviatedObjectId A_ZERO = AbbreviatedObjectId
+ /** Magical SHA1 used for file adds or deletes */
+ static final AbbreviatedObjectId A_ZERO = AbbreviatedObjectId
.fromObjectId(ObjectId.zeroId());
/** Magical file name used for file adds or deletes. */
@@ -111,15 +112,15 @@ public static List<DiffEntry> scan(TreeWalk walk) throws IOException {
entry.oldMode = walk.getFileMode(0);
entry.newMode = walk.getFileMode(1);
- entry.newName = entry.oldName = walk.getPathString();
+ entry.newPath = entry.oldPath = walk.getPathString();
if (entry.oldMode == FileMode.MISSING) {
- entry.oldName = DiffEntry.DEV_NULL;
+ entry.oldPath = DiffEntry.DEV_NULL;
entry.changeType = ChangeType.ADD;
r.add(entry);
} else if (entry.newMode == FileMode.MISSING) {
- entry.newName = DiffEntry.DEV_NULL;
+ entry.newPath = DiffEntry.DEV_NULL;
entry.changeType = ChangeType.DELETE;
r.add(entry);
@@ -138,11 +139,11 @@ static DiffEntry add(String path, AnyObjectId id) {
DiffEntry e = new DiffEntry();
e.oldId = A_ZERO;
e.oldMode = FileMode.MISSING;
- e.oldName = DEV_NULL;
+ e.oldPath = DEV_NULL;
e.newId = AbbreviatedObjectId.fromObjectId(id);
e.newMode = FileMode.REGULAR_FILE;
- e.newName = path;
+ e.newPath = path;
e.changeType = ChangeType.ADD;
return e;
}
@@ -151,11 +152,11 @@ static DiffEntry delete(String path, AnyObjectId id) {
DiffEntry e = new DiffEntry();
e.oldId = AbbreviatedObjectId.fromObjectId(id);
e.oldMode = FileMode.REGULAR_FILE;
- e.oldName = path;
+ e.oldPath = path;
e.newId = A_ZERO;
e.newMode = FileMode.MISSING;
- e.newName = DEV_NULL;
+ e.newPath = DEV_NULL;
e.changeType = ChangeType.DELETE;
return e;
}
@@ -163,33 +164,42 @@ static DiffEntry delete(String path, AnyObjectId id) {
static DiffEntry modify(String path) {
DiffEntry e = new DiffEntry();
e.oldMode = FileMode.REGULAR_FILE;
- e.oldName = path;
+ e.oldPath = path;
e.newMode = FileMode.REGULAR_FILE;
- e.newName = path;
+ e.newPath = path;
e.changeType = ChangeType.MODIFY;
return e;
}
+ /**
+ * Breaks apart a DiffEntry into two entries, one DELETE and one ADD.
+ *
+ * @param entry
+ * the DiffEntry to break apart.
+ * @return a list containing two entries. Calling {@link #getChangeType()}
+ * on the first entry will return ChangeType.DELETE. Calling it on
+ * the second entry will return ChangeType.ADD.
+ */
static List<DiffEntry> breakModify(DiffEntry entry) {
DiffEntry del = new DiffEntry();
del.oldId = entry.getOldId();
del.oldMode = entry.getOldMode();
- del.oldName = entry.getOldName();
+ del.oldPath = entry.getOldPath();
del.newId = A_ZERO;
del.newMode = FileMode.MISSING;
- del.newName = DiffEntry.DEV_NULL;
+ del.newPath = DiffEntry.DEV_NULL;
del.changeType = ChangeType.DELETE;
DiffEntry add = new DiffEntry();
add.oldId = A_ZERO;
add.oldMode = FileMode.MISSING;
- add.oldName = DiffEntry.DEV_NULL;
+ add.oldPath = DiffEntry.DEV_NULL;
add.newId = entry.getNewId();
add.newMode = entry.getNewMode();
- add.newName = entry.getNewName();
+ add.newPath = entry.getNewPath();
add.changeType = ChangeType.ADD;
return Arrays.asList(del, add);
}
@@ -200,11 +210,11 @@ static DiffEntry pair(ChangeType changeType, DiffEntry src, DiffEntry dst,
r.oldId = src.oldId;
r.oldMode = src.oldMode;
- r.oldName = src.oldName;
+ r.oldPath = src.oldPath;
r.newId = dst.newId;
r.newMode = dst.newMode;
- r.newName = dst.newName;
+ r.newPath = dst.newPath;
r.changeType = changeType;
r.score = score;
@@ -213,10 +223,10 @@ static DiffEntry pair(ChangeType changeType, DiffEntry src, DiffEntry dst,
}
/** File name of the old (pre-image). */
- protected String oldName;
+ protected String oldPath;
/** File name of the new (post-image). */
- protected String newName;
+ protected String newPath;
/** Old mode of the file, if described by the patch, else null. */
protected FileMode oldMode;
@@ -243,7 +253,7 @@ static DiffEntry pair(ChangeType changeType, DiffEntry src, DiffEntry dst,
* of this patch:
* <ul>
* <li><i>file add</i>: always <code>/dev/null</code></li>
- * <li><i>file modify</i>: always {@link #getNewName()}</li>
+ * <li><i>file modify</i>: always {@link #getNewPath()}</li>
* <li><i>file delete</i>: always the file being deleted</li>
* <li><i>file copy</i>: source file the copy originates from</li>
* <li><i>file rename</i>: source file the rename originates from</li>
@@ -251,8 +261,8 @@ static DiffEntry pair(ChangeType changeType, DiffEntry src, DiffEntry dst,
*
* @return old name for this file.
*/
- public String getOldName() {
- return oldName;
+ public String getOldPath() {
+ return oldPath;
}
/**
@@ -262,7 +272,7 @@ public String getOldName() {
* of this patch:
* <ul>
* <li><i>file add</i>: always the file being created</li>
- * <li><i>file modify</i>: always {@link #getOldName()}</li>
+ * <li><i>file modify</i>: always {@link #getOldPath()}</li>
* <li><i>file delete</i>: always <code>/dev/null</code></li>
* <li><i>file copy</i>: destination file the copy ends up at</li>
* <li><i>file rename</i>: destination file the rename ends up at/li>
@@ -270,8 +280,8 @@ public String getOldName() {
*
* @return new name for this file.
*/
- public String getNewName() {
- return newName;
+ public String getNewPath() {
+ return newPath;
}
/** @return the old file mode, if described in the patch */
@@ -284,14 +294,14 @@ public FileMode getNewMode() {
return newMode;
}
- /** @return the type of change this patch makes on {@link #getNewName()} */
+ /** @return the type of change this patch makes on {@link #getNewPath()} */
public ChangeType getChangeType() {
return changeType;
}
/**
- * @return similarity score between {@link #getOldName()} and
- * {@link #getNewName()} if {@link #getChangeType()} is
+ * @return similarity score between {@link #getOldPath()} and
+ * {@link #getNewPath()} if {@link #getChangeType()} is
* {@link ChangeType#COPY} or {@link ChangeType#RENAME}.
*/
public int getScore() {
@@ -324,19 +334,19 @@ public String toString() {
buf.append(" ");
switch (changeType) {
case ADD:
- buf.append(newName);
+ buf.append(newPath);
break;
case COPY:
- buf.append(oldName + "->" + newName);
+ buf.append(oldPath + "->" + newPath);
break;
case DELETE:
- buf.append(oldName);
+ buf.append(oldPath);
break;
case MODIFY:
- buf.append(oldName);
+ buf.append(oldPath);
break;
case RENAME:
- buf.append(oldName + "->" + newName);
+ buf.append(oldPath + "->" + newPath);
break;
}
buf.append("]");
diff --git a/org.eclipse.jgit/src/org/eclipse/jgit/diff/DiffFormatter.java b/org.eclipse.jgit/src/org/eclipse/jgit/diff/DiffFormatter.java
index cdcc5e6..bb4a77c 100644
--- a/org.eclipse.jgit/src/org/eclipse/jgit/diff/DiffFormatter.java
+++ b/org.eclipse.jgit/src/org/eclipse/jgit/diff/DiffFormatter.java
@@ -50,20 +50,24 @@
import java.io.ByteArrayOutputStream;
import java.io.IOException;
+import java.io.InputStream;
import java.io.OutputStream;
import java.util.List;
import org.eclipse.jgit.JGitText;
import org.eclipse.jgit.errors.CorruptObjectException;
+import org.eclipse.jgit.errors.LargeObjectException;
import org.eclipse.jgit.errors.MissingObjectException;
import org.eclipse.jgit.lib.AbbreviatedObjectId;
import org.eclipse.jgit.lib.Constants;
+import org.eclipse.jgit.lib.CoreConfig;
import org.eclipse.jgit.lib.FileMode;
import org.eclipse.jgit.lib.ObjectLoader;
import org.eclipse.jgit.lib.Repository;
import org.eclipse.jgit.patch.FileHeader;
import org.eclipse.jgit.patch.HunkHeader;
import org.eclipse.jgit.patch.FileHeader.PatchType;
+import org.eclipse.jgit.util.IO;
import org.eclipse.jgit.util.QuotedString;
import org.eclipse.jgit.util.io.DisabledOutputStream;
@@ -83,6 +87,8 @@ public class DiffFormatter {
private RawText.Factory rawTextFactory = RawText.FACTORY;
+ private long bigFileThreshold = 50 * 1024 * 1024;
+
/**
* Create a new formatter with a default level of context.
*
@@ -110,6 +116,9 @@ protected OutputStream getOutputStream() {
*/
public void setRepository(Repository repository) {
db = repository;
+
+ CoreConfig cfg = db.getConfig().get(CoreConfig.KEY);
+ bigFileThreshold = cfg.getStreamFileThreshold();
}
/**
@@ -158,6 +167,19 @@ public void setRawTextFactory(RawText.Factory type) {
}
/**
+ * Set the maximum file size that should be considered for diff output.
+ * <p>
+ * Text files that are larger than this size will not have a difference
+ * generated during output.
+ *
+ * @param bigFileThreshold
+ * the limit, in bytes.
+ */
+ public void setBigFileThreshold(long bigFileThreshold) {
+ this.bigFileThreshold = bigFileThreshold;
+ }
+
+ /**
* Flush the underlying output stream of this formatter.
*
* @throws IOException
@@ -224,8 +246,8 @@ private void writeGitLinkDiffText(OutputStream o, DiffEntry ent)
private void writeDiffHeader(OutputStream o, DiffEntry ent)
throws IOException {
- String oldName = quotePath("a/" + ent.getOldName());
- String newName = quotePath("b/" + ent.getNewName());
+ String oldName = quotePath("a/" + ent.getOldPath());
+ String newName = quotePath("b/" + ent.getNewPath());
o.write(encode("diff --git " + oldName + " " + newName + "\n"));
switch (ent.getChangeType()) {
@@ -245,10 +267,10 @@ private void writeDiffHeader(OutputStream o, DiffEntry ent)
o.write(encodeASCII("similarity index " + ent.getScore() + "%"));
o.write('\n');
- o.write(encode("rename from " + quotePath(ent.getOldName())));
+ o.write(encode("rename from " + quotePath(ent.getOldPath())));
o.write('\n');
- o.write(encode("rename to " + quotePath(ent.getNewName())));
+ o.write(encode("rename to " + quotePath(ent.getNewPath())));
o.write('\n');
break;
@@ -256,10 +278,10 @@ private void writeDiffHeader(OutputStream o, DiffEntry ent)
o.write(encodeASCII("similarity index " + ent.getScore() + "%"));
o.write('\n');
- o.write(encode("copy from " + quotePath(ent.getOldName())));
+ o.write(encode("copy from " + quotePath(ent.getOldPath())));
o.write('\n');
- o.write(encode("copy to " + quotePath(ent.getNewName())));
+ o.write(encode("copy to " + quotePath(ent.getNewPath())));
o.write('\n');
if (!ent.getOldMode().equals(ent.getNewMode())) {
@@ -268,6 +290,14 @@ private void writeDiffHeader(OutputStream o, DiffEntry ent)
o.write('\n');
}
break;
+ case MODIFY:
+ int score = ent.getScore();
+ if (0 < score && score <= 100) {
+ o.write(encodeASCII("dissimilarity index " + (100 - score)
+ + "%"));
+ o.write('\n');
+ }
+ break;
}
switch (ent.getChangeType()) {
@@ -318,9 +348,32 @@ private static String quotePath(String name) {
if (db == null)
throw new IllegalStateException(JGitText.get().repositoryIsRequired);
+
if (id.isComplete()) {
- ObjectLoader ldr = db.openObject(id.toObjectId());
- return ldr.getCachedBytes();
+ ObjectLoader ldr = db.open(id.toObjectId());
+ if (!ldr.isLarge())
+ return ldr.getCachedBytes();
+
+ long sz = ldr.getSize();
+ if (sz < bigFileThreshold && sz < Integer.MAX_VALUE) {
+ byte[] buf;
+ try {
+ buf = new byte[(int) sz];
+ } catch (OutOfMemoryError noMemory) {
+ LargeObjectException e;
+
+ e = new LargeObjectException(id.toObjectId());
+ e.initCause(noMemory);
+ throw e;
+ }
+ InputStream in = ldr.openStream();
+ try {
+ IO.readFully(in, buf, 0, buf.length);
+ } finally {
+ in.close();
+ }
+ return buf;
+ }
}
return new byte[] {};
diff --git a/org.eclipse.jgit/src/org/eclipse/jgit/diff/RenameDetector.java b/org.eclipse.jgit/src/org/eclipse/jgit/diff/RenameDetector.java
index ad37a73..9c1310a 100644
--- a/org.eclipse.jgit/src/org/eclipse/jgit/diff/RenameDetector.java
+++ b/org.eclipse.jgit/src/org/eclipse/jgit/diff/RenameDetector.java
@@ -55,8 +55,10 @@
import org.eclipse.jgit.JGitText;
import org.eclipse.jgit.diff.DiffEntry.ChangeType;
import org.eclipse.jgit.lib.AbbreviatedObjectId;
+import org.eclipse.jgit.lib.Constants;
import org.eclipse.jgit.lib.FileMode;
import org.eclipse.jgit.lib.NullProgressMonitor;
+import org.eclipse.jgit.lib.ObjectReader;
import org.eclipse.jgit.lib.ProgressMonitor;
import org.eclipse.jgit.lib.Repository;
@@ -78,8 +80,8 @@ private String nameOf(DiffEntry ent) {
// the old name.
//
if (ent.changeType == ChangeType.DELETE)
- return ent.oldName;
- return ent.newName;
+ return ent.oldPath;
+ return ent.newPath;
}
private int sortOf(ChangeType changeType) {
@@ -98,7 +100,7 @@ private int sortOf(ChangeType changeType) {
}
};
- private final List<DiffEntry> entries = new ArrayList<DiffEntry>();
+ private List<DiffEntry> entries = new ArrayList<DiffEntry>();
private List<DiffEntry> deleted = new ArrayList<DiffEntry>();
@@ -111,6 +113,13 @@ private int sortOf(ChangeType changeType) {
/** Similarity score required to pair an add/delete as a rename. */
private int renameScore = 60;
+ /**
+ * Similarity score required to keep modified file pairs together. Any
+ * modified file pairs with a similarity score below this will be broken
+ * apart.
+ */
+ private int breakScore = -1;
+
/** Limit in the number of files to consider for renames. */
private int renameLimit;
@@ -158,6 +167,29 @@ public void setRenameScore(int score) {
renameScore = score;
}
+ /**
+ * @return the similarity score required to keep modified file pairs
+ * together. Any modify pairs that score below this will be broken
+ * apart into separate add/deletes. Values less than or equal to
+ * zero indicate that no modifies will be broken apart. Values over
+ * 100 cause all modify pairs to be broken.
+ */
+ public int getBreakScore() {
+ return breakScore;
+ }
+
+ /**
+ * @param breakScore
+ * the similarity score required to keep modified file pairs
+ * together. Any modify pairs that score below this will be
+ * broken apart into separate add/deletes. Values less than or
+ * equal to zero indicate that no modifies will be broken apart.
+ * Values over 100 cause all modify pairs to be broken.
+ */
+ public void setBreakScore(int breakScore) {
+ this.breakScore = breakScore;
+ }
+
/** @return limit on number of paths to perform inexact rename detection. */
public int getRenameLimit() {
return renameLimit;
@@ -218,10 +250,13 @@ public void addAll(Collection<DiffEntry> entriesToAdd) {
break;
case MODIFY:
- if (sameType(entry.getOldMode(), entry.getNewMode()))
+ if (sameType(entry.getOldMode(), entry.getNewMode())) {
entries.add(entry);
- else
- entries.addAll(DiffEntry.breakModify(entry));
+ } else {
+ List<DiffEntry> tmp = DiffEntry.breakModify(entry);
+ deleted.add(tmp.get(0));
+ added.add(tmp.get(1));
+ }
break;
case COPY:
@@ -274,8 +309,15 @@ public List<DiffEntry> compute(ProgressMonitor pm) throws IOException {
if (pm == null)
pm = NullProgressMonitor.INSTANCE;
- findExactRenames(pm);
- findContentRenames(pm);
+ ObjectReader reader = repo.newObjectReader();
+ try {
+ breakModifies(reader, pm);
+ findExactRenames(pm);
+ findContentRenames(reader, pm);
+ rejoinModifies(pm);
+ } finally {
+ reader.release();
+ }
entries.addAll(added);
added = null;
@@ -288,7 +330,83 @@ public List<DiffEntry> compute(ProgressMonitor pm) throws IOException {
return Collections.unmodifiableList(entries);
}
- private void findContentRenames(ProgressMonitor pm) throws IOException {
+ private void breakModifies(ObjectReader reader, ProgressMonitor pm)
+ throws IOException {
+ if (breakScore <= 0)
+ return;
+
+ ArrayList<DiffEntry> newEntries = new ArrayList<DiffEntry>(entries.size());
+
+ pm.beginTask(JGitText.get().renamesBreakingModifies, entries.size());
+
+ for (int i = 0; i < entries.size(); i++) {
+ DiffEntry e = entries.get(i);
+ if (e.getChangeType() == ChangeType.MODIFY) {
+ int score = calculateModifyScore(reader, e);
+ if (score < breakScore) {
+ List<DiffEntry> tmp = DiffEntry.breakModify(e);
+ DiffEntry del = tmp.get(0);
+ del.score = score;
+ deleted.add(del);
+ added.add(tmp.get(1));
+ } else {
+ newEntries.add(e);
+ }
+ } else {
+ newEntries.add(e);
+ }
+ pm.update(1);
+ }
+
+ entries = newEntries;
+ }
+
+ private void rejoinModifies(ProgressMonitor pm) {
+ HashMap<String, DiffEntry> nameMap = new HashMap<String, DiffEntry>();
+ ArrayList<DiffEntry> newAdded = new ArrayList<DiffEntry>(added.size());
+
+ pm.beginTask(JGitText.get().renamesRejoiningModifies, added.size()
+ + deleted.size());
+
+ for (DiffEntry src : deleted) {
+ nameMap.put(src.oldPath, src);
+ pm.update(1);
+ }
+
+ for (DiffEntry dst : added) {
+ DiffEntry src = nameMap.remove(dst.newPath);
+ if (src != null) {
+ if (sameType(src.oldMode, dst.newMode)) {
+ entries.add(DiffEntry.pair(ChangeType.MODIFY, src, dst,
+ src.score));
+ } else {
+ nameMap.put(src.oldPath, src);
+ newAdded.add(dst);
+ }
+ } else {
+ newAdded.add(dst);
+ }
+ pm.update(1);
+ }
+
+ added = newAdded;
+ deleted = new ArrayList<DiffEntry>(nameMap.values());
+ }
+
+ private int calculateModifyScore(ObjectReader reader, DiffEntry d)
+ throws IOException {
+ SimilarityIndex src = new SimilarityIndex();
+ src.hash(reader.open(d.oldId.toObjectId(), Constants.OBJ_BLOB));
+ src.sort();
+
+ SimilarityIndex dst = new SimilarityIndex();
+ dst.hash(reader.open(d.newId.toObjectId(), Constants.OBJ_BLOB));
+ dst.sort();
+ return src.score(dst, 100);
+ }
+
+ private void findContentRenames(ObjectReader reader, ProgressMonitor pm)
+ throws IOException {
int cnt = Math.max(added.size(), deleted.size());
if (cnt == 0)
return;
@@ -296,7 +414,7 @@ private void findContentRenames(ProgressMonitor pm) throws IOException {
if (getRenameLimit() == 0 || cnt <= getRenameLimit()) {
SimilarityRenameDetector d;
- d = new SimilarityRenameDetector(repo, deleted, added);
+ d = new SimilarityRenameDetector(reader, deleted, added);
d.setRenameScore(getRenameScore());
d.compute(pm);
deleted = d.getLeftOverSources();
@@ -391,10 +509,10 @@ private void findExactRenames(ProgressMonitor pm) {
long[] matrix = new long[dels.size() * adds.size()];
int mNext = 0;
for (int delIdx = 0; delIdx < dels.size(); delIdx++) {
- String deletedName = dels.get(delIdx).oldName;
+ String deletedName = dels.get(delIdx).oldPath;
for (int addIdx = 0; addIdx < adds.size(); addIdx++) {
- String addedName = adds.get(addIdx).newName;
+ String addedName = adds.get(addIdx).newPath;
int score = SimilarityRenameDetector.nameScore(addedName, deletedName);
matrix[mNext] = SimilarityRenameDetector.encode(score, delIdx, addIdx);
@@ -507,7 +625,7 @@ private HashMap<AbbreviatedObjectId, Object> populateMap(
}
private static String path(DiffEntry de) {
- return de.changeType == ChangeType.DELETE ? de.oldName : de.newName;
+ return de.changeType == ChangeType.DELETE ? de.oldPath : de.newPath;
}
private static FileMode mode(DiffEntry de) {
diff --git a/org.eclipse.jgit/src/org/eclipse/jgit/diff/SimilarityIndex.java b/org.eclipse.jgit/src/org/eclipse/jgit/diff/SimilarityIndex.java
index b460d49..39bcebb 100644
--- a/org.eclipse.jgit/src/org/eclipse/jgit/diff/SimilarityIndex.java
+++ b/org.eclipse.jgit/src/org/eclipse/jgit/diff/SimilarityIndex.java
@@ -43,9 +43,14 @@
package org.eclipse.jgit.diff;
+import java.io.EOFException;
+import java.io.IOException;
+import java.io.InputStream;
import java.util.Arrays;
+import org.eclipse.jgit.errors.MissingObjectException;
import org.eclipse.jgit.lib.ObjectLoader;
+import org.eclipse.jgit.lib.ObjectStream;
/**
* Index structure of lines/blocks in one file.
@@ -107,10 +112,20 @@ void setFileSize(long size) {
fileSize = size;
}
- void hash(ObjectLoader obj) {
- byte[] raw = obj.getCachedBytes();
- setFileSize(raw.length);
- hash(raw, 0, raw.length);
+ void hash(ObjectLoader obj) throws MissingObjectException, IOException {
+ if (obj.isLarge()) {
+ ObjectStream in = obj.openStream();
+ try {
+ setFileSize(in.getSize());
+ hash(in, fileSize);
+ } finally {
+ in.close();
+ }
+ } else {
+ byte[] raw = obj.getCachedBytes();
+ setFileSize(raw.length);
+ hash(raw, 0, raw.length);
+ }
}
void hash(byte[] raw, int ptr, final int end) {
@@ -129,6 +144,35 @@ void hash(byte[] raw, int ptr, final int end) {
}
}
+ void hash(InputStream in, long remaining) throws IOException {
+ byte[] buf = new byte[4096];
+ int ptr = 0;
+ int cnt = 0;
+
+ while (0 < remaining) {
+ int hash = 5381;
+
+ // Hash one line, or one block, whichever occurs first.
+ int n = 0;
+ do {
+ if (ptr == cnt) {
+ ptr = 0;
+ cnt = in.read(buf, 0, buf.length);
+ if (cnt <= 0)
+ throw new EOFException();
+ }
+
+ n++;
+ int c = buf[ptr++] & 0xff;
+ if (c == '\n')
+ break;
+ hash = (hash << 5) ^ c;
+ } while (n < 64 && n < remaining);
+ add(hash, n);
+ remaining -= n;
+ }
+ }
+
/**
* Sort the internal table so it can be used for efficient scoring.
* <p>
diff --git a/org.eclipse.jgit/src/org/eclipse/jgit/diff/SimilarityRenameDetector.java b/org.eclipse.jgit/src/org/eclipse/jgit/diff/SimilarityRenameDetector.java
index e2115f0..643ac01 100644
--- a/org.eclipse.jgit/src/org/eclipse/jgit/diff/SimilarityRenameDetector.java
+++ b/org.eclipse.jgit/src/org/eclipse/jgit/diff/SimilarityRenameDetector.java
@@ -50,11 +50,12 @@
import org.eclipse.jgit.JGitText;
import org.eclipse.jgit.diff.DiffEntry.ChangeType;
+import org.eclipse.jgit.lib.Constants;
import org.eclipse.jgit.lib.FileMode;
import org.eclipse.jgit.lib.NullProgressMonitor;
import org.eclipse.jgit.lib.ObjectId;
+import org.eclipse.jgit.lib.ObjectReader;
import org.eclipse.jgit.lib.ProgressMonitor;
-import org.eclipse.jgit.lib.Repository;
class SimilarityRenameDetector {
/**
@@ -71,7 +72,7 @@ class SimilarityRenameDetector {
private static final int SCORE_SHIFT = 2 * BITS_PER_INDEX;
- private final Repository repo;
+ private ObjectReader reader;
/**
* All sources to consider for copies or renames.
@@ -111,9 +112,9 @@ class SimilarityRenameDetector {
private List<DiffEntry> out;
- SimilarityRenameDetector(Repository repo, List<DiffEntry> srcs,
+ SimilarityRenameDetector(ObjectReader reader, List<DiffEntry> srcs,
List<DiffEntry> dsts) {
- this.repo = repo;
+ this.reader = reader;
this.srcs = srcs;
this.dsts = dsts;
}
@@ -265,7 +266,7 @@ private int buildMatrix(ProgressMonitor pm) throws IOException {
// nameScore returns a value between 0 and 100, but we want it
// to be in the same range as the content score. This allows it
// to be dropped into the pretty formula for the final score.
- int nameScore = nameScore(srcEnt.oldName, dstEnt.newName) * 100;
+ int nameScore = nameScore(srcEnt.oldPath, dstEnt.newPath) * 100;
int score = (contentScore * 99 + nameScore * 1) / 10000;
@@ -336,13 +337,13 @@ static int nameScore(String a, String b) {
private SimilarityIndex hash(ObjectId objectId) throws IOException {
SimilarityIndex r = new SimilarityIndex();
- r.hash(repo.openObject(objectId));
+ r.hash(reader.open(objectId));
r.sort();
return r;
}
private long size(ObjectId objectId) throws IOException {
- return repo.openObject(objectId).getSize();
+ return reader.getObjectSize(objectId, Constants.OBJ_BLOB);
}
private static int score(long value) {
diff --git a/org.eclipse.jgit/src/org/eclipse/jgit/dircache/DirCache.java b/org.eclipse.jgit/src/org/eclipse/jgit/dircache/DirCache.java
index 42fea48..60238c3 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.errors.CorruptObjectException;
import org.eclipse.jgit.errors.UnmergedPathException;
import org.eclipse.jgit.lib.Constants;
-import org.eclipse.jgit.lib.LockFile;
import org.eclipse.jgit.lib.ObjectId;
-import org.eclipse.jgit.lib.ObjectWriter;
-import org.eclipse.jgit.lib.Repository;
+import org.eclipse.jgit.lib.ObjectInserter;
+import org.eclipse.jgit.storage.file.LockFile;
+import org.eclipse.jgit.util.FS;
import org.eclipse.jgit.util.IO;
import org.eclipse.jgit.util.MutableInteger;
import org.eclipse.jgit.util.NB;
@@ -130,7 +130,7 @@ static int cmp(final byte[] aPath, final int aLen, final byte[] bPath,
* memory).
*/
public static DirCache newInCore() {
- return new DirCache(null);
+ return new DirCache(null, null);
}
/**
@@ -142,6 +142,9 @@ public static DirCache newInCore() {
*
* @param indexLocation
* location of the index file on disk.
+ * @param fs
+ * the file system abstraction which will be necessary to perform
+ * certain file system operations.
* @return a cache representing the contents of the specified index file (if
* it exists) or an empty cache if the file does not exist.
* @throws IOException
@@ -150,45 +153,26 @@ public static DirCache newInCore() {
* the index file is using a format or extension that this
* library does not support.
*/
- public static DirCache read(final File indexLocation)
+ public static DirCache read(final File indexLocation, final FS fs)
throws CorruptObjectException, IOException {
- final DirCache c = new DirCache(indexLocation);
+ final DirCache c = new DirCache(indexLocation, fs);
c.read();
return c;
}
/**
- * Create a new in-core index representation and read an index from disk.
- * <p>
- * The new index will be read before it is returned to the caller. Read
- * failures are reported as exceptions and therefore prevent the method from
- * returning a partially populated index.
- *
- * @param db
- * repository the caller wants to read the default index of.
- * @return a cache representing the contents of the specified index file (if
- * it exists) or an empty cache if the file does not exist.
- * @throws IOException
- * the index file is present but could not be read.
- * @throws CorruptObjectException
- * the index file is using a format or extension that this
- * library does not support.
- */
- public static DirCache read(final Repository db)
- throws CorruptObjectException, IOException {
- return read(new File(db.getDirectory(), "index"));
- }
-
- /**
* Create a new in-core index representation, lock it, and read from disk.
* <p>
* The new index will be locked and then read before it is returned to the
* caller. Read failures are reported as exceptions and therefore prevent
- * the method from returning a partially populated index. On read failure,
+ * the method from returning a partially populated index. On read failure,
* the lock is released.
*
* @param indexLocation
* location of the index file on disk.
+ * @param fs
+ * the file system abstraction which will be necessary to perform
+ * certain file system operations.
* @return a cache representing the contents of the specified index file (if
* it exists) or an empty cache if the file does not exist.
* @throws IOException
@@ -198,9 +182,9 @@ public static DirCache read(final Repository db)
* the index file is using a format or extension that this
* library does not support.
*/
- public static DirCache lock(final File indexLocation)
+ public static DirCache lock(final File indexLocation, final FS fs)
throws CorruptObjectException, IOException {
- final DirCache c = new DirCache(indexLocation);
+ final DirCache c = new DirCache(indexLocation, fs);
if (!c.lock())
throw new IOException(MessageFormat.format(JGitText.get().cannotLock, indexLocation));
@@ -220,29 +204,6 @@ public static DirCache lock(final File indexLocation)
return c;
}
- /**
- * Create a new in-core index representation, lock it, and read from disk.
- * <p>
- * The new index will be locked and then read before it is returned to the
- * caller. Read failures are reported as exceptions and therefore prevent
- * the method from returning a partially populated index.
- *
- * @param db
- * repository the caller wants to read the default index of.
- * @return a cache representing the contents of the specified index file (if
- * it exists) or an empty cache if the file does not exist.
- * @throws IOException
- * the index file is present but could not be read, or the lock
- * could not be obtained.
- * @throws CorruptObjectException
- * the index file is using a format or extension that this
- * library does not support.
- */
- public static DirCache lock(final Repository db)
- throws CorruptObjectException, IOException {
- return lock(new File(db.getDirectory(), "index"));
- }
-
/** Location of the current version of the index file. */
private final File liveFile;
@@ -261,6 +222,9 @@ public static DirCache lock(final Repository db)
/** Our active lock (if we hold it); null if we don't have it locked. */
private LockFile myLock;
+ /** file system abstraction **/
+ private final FS fs;
+
/**
* Create a new in-core index representation.
* <p>
@@ -269,9 +233,13 @@ public static DirCache lock(final Repository db)
*
* @param indexLocation
* location of the index file on disk.
+ * @param fs
+ * the file system abstraction which will be necessary to perform
+ * certain file system operations.
*/
- public DirCache(final File indexLocation) {
+ public DirCache(final File indexLocation, final FS fs) {
liveFile = indexLocation;
+ this.fs = fs;
clear();
}
@@ -475,7 +443,7 @@ private static boolean is_DIRC(final byte[] hdr) {
public boolean lock() throws IOException {
if (liveFile == null)
throw new IOException(JGitText.get().dirCacheDoesNotHaveABackingFile);
- final LockFile tmp = new LockFile(liveFile);
+ final LockFile tmp = new LockFile(liveFile, fs);
if (tmp.lock()) {
tmp.setNeedStatInformation(true);
myLock = tmp;
@@ -768,7 +736,9 @@ public DirCacheTree getCacheTree(final boolean build) {
* Write all index trees to the object store, returning the root tree.
*
* @param ow
- * the writer to use when serializing to the store.
+ * the writer to use when serializing to the store. The caller is
+ * responsible for flushing the inserter before trying to use the
+ * returned tree identity.
* @return identity for the root tree.
* @throws UnmergedPathException
* one or more paths contain higher-order stages (stage > 0),
@@ -779,7 +749,7 @@ public DirCacheTree getCacheTree(final boolean build) {
* @throws IOException
* an unexpected error occurred writing to the object store.
*/
- public ObjectId writeTree(final ObjectWriter ow)
+ public ObjectId writeTree(final ObjectInserter ow)
throws UnmergedPathException, IOException {
return getCacheTree(true).writeTree(sortedEntries, 0, 0, ow);
}
diff --git a/org.eclipse.jgit/src/org/eclipse/jgit/dircache/DirCacheBuildIterator.java b/org.eclipse.jgit/src/org/eclipse/jgit/dircache/DirCacheBuildIterator.java
index 181192d..1eb95c4 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 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 DirCacheBuildIterator(final DirCacheBuilder dcb) {
}
@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 e6b6197..5665002 100644
--- a/org.eclipse.jgit/src/org/eclipse/jgit/dircache/DirCacheBuilder.java
+++ b/org.eclipse.jgit/src/org/eclipse/jgit/dircache/DirCacheBuilder.java
@@ -50,8 +50,7 @@
import org.eclipse.jgit.JGitText;
import org.eclipse.jgit.lib.AnyObjectId;
-import org.eclipse.jgit.lib.Repository;
-import org.eclipse.jgit.lib.WindowCursor;
+import org.eclipse.jgit.lib.ObjectReader;
import org.eclipse.jgit.treewalk.AbstractTreeIterator;
import org.eclipse.jgit.treewalk.CanonicalTreeParser;
import org.eclipse.jgit.treewalk.TreeWalk;
@@ -149,11 +148,12 @@ public void keep(final int pos, int cnt) {
* as necessary.
* @param stage
* stage of the entries when adding them.
- * @param db
- * repository the tree(s) will be read from during recursive
+ * @param reader
+ * reader the tree(s) will be read from during recursive
* traversal. This must be the same repository that the resulting
* DirCache would be written out to (or used in) otherwise the
* caller is simply asking for deferred MissingObjectExceptions.
+ * Caller is responsible for releasing this reader when done.
* @param tree
* the tree to recursively add. This tree's contents will appear
* under <code>pathPrefix</code>. The ObjectId must be that of a
@@ -163,16 +163,11 @@ public void keep(final int pos, int cnt) {
* a tree cannot be read to iterate through its entries.
*/
public void addTree(final byte[] pathPrefix, final int stage,
- final Repository db, final AnyObjectId tree) throws IOException {
- final TreeWalk tw = new TreeWalk(db);
+ final ObjectReader reader, final AnyObjectId tree) throws IOException {
+ final TreeWalk tw = new TreeWalk(reader);
tw.reset();
- final WindowCursor curs = new WindowCursor();
- try {
- tw.addTree(new CanonicalTreeParser(pathPrefix, db, tree
- .toObjectId(), curs));
- } finally {
- curs.release();
- }
+ tw.addTree(new CanonicalTreeParser(pathPrefix, reader, tree
+ .toObjectId()));
tw.setRecursive(true);
if (tw.next()) {
final DirCacheEntry newEntry = toEntry(stage, tw);
diff --git a/org.eclipse.jgit/src/org/eclipse/jgit/dircache/DirCacheEntry.java b/org.eclipse.jgit/src/org/eclipse/jgit/dircache/DirCacheEntry.java
index 7cb1472..909729d 100644
--- a/org.eclipse.jgit/src/org/eclipse/jgit/dircache/DirCacheEntry.java
+++ b/org.eclipse.jgit/src/org/eclipse/jgit/dircache/DirCacheEntry.java
@@ -2,6 +2,7 @@
* Copyright (C) 2008-2009, Google Inc.
* Copyright (C) 2008, Shawn O. Pearce <spearce@spearce.org>
* Copyright (C) 2010, Matthias Sohn <matthias.sohn@sap.com>
+ * Copyright (C) 2010, Christian Halstrick <christian.halstrick@sap.com>
* and other copyright owners as documented in the project's IP log.
*
* This program and the accompanying materials are made available
@@ -282,7 +283,7 @@ void write(final OutputStream os) throws IOException {
* nanoseconds component of the index's last modified time.
* @return true if extra careful checks should be used.
*/
- final boolean mightBeRacilyClean(final int smudge_s, final int smudge_ns) {
+ public final boolean mightBeRacilyClean(final int smudge_s, final int smudge_ns) {
// If the index has a modification time then it came from disk
// and was not generated from scratch in memory. In such cases
// the entry is 'racily clean' if the entry's cached modification
@@ -292,8 +293,6 @@ final boolean mightBeRacilyClean(final int smudge_s, final int smudge_ns) {
//
final int base = infoOffset + P_MTIME;
final int mtime = NB.decodeInt32(info, base);
- if (smudge_s < mtime)
- return true;
if (smudge_s == mtime)
return smudge_ns <= NB.decodeInt32(info, base + 4);
return false;
@@ -306,21 +305,30 @@ final boolean mightBeRacilyClean(final int smudge_s, final int smudge_ns) {
* match the file in the working directory. Later git will be forced to
* compare the file content to ensure the file matches the working tree.
*/
- final void smudgeRacilyClean() {
- // We don't use the same approach as C Git to smudge the entry,
- // as we cannot compare the working tree file to our SHA-1 and
- // thus cannot use the "size to 0" trick without accidentally
- // thinking a zero length file is clean.
- //
- // Instead we force the mtime to the largest possible value, so
- // it is certainly after the index's own modification time and
- // on a future read will cause mightBeRacilyClean to say "yes!".
- // It is also unlikely to match with the working tree file.
- //
- // I'll see you again before Jan 19, 2038, 03:14:07 AM GMT.
- //
- final int base = infoOffset + P_MTIME;
- Arrays.fill(info, base, base + 8, (byte) 127);
+ public final void smudgeRacilyClean() {
+ // To mark an entry racily clean we set its length to 0 (like native git
+ // does). Entries which are not racily clean and have zero length can be
+ // distinguished from racily clean entries by checking P_OBJECTID
+ // against the SHA1 of empty content. When length is 0 and P_OBJECTID is
+ // different from SHA1 of empty content we know the entry is marked
+ // racily clean
+ final int base = infoOffset + P_SIZE;
+ Arrays.fill(info, base, base + 4, (byte) 0);
+ }
+
+ /**
+ * Check whether this entry has been smudged or not
+ * <p>
+ * If a blob has length 0 we know his id see {@link Constants#EMPTY_BLOB_ID}. If an entry
+ * has length 0 and an ID different from the one for empty blob we know this
+ * entry was smudged.
+ *
+ * @return <code>true</code> if the entry is smudged, <code>false</code>
+ * otherwise
+ */
+ public final boolean isSmudged() {
+ final int base = infoOffset + P_OBJECTID;
+ return (getLength() == 0) && (Constants.EMPTY_BLOB_ID.compareTo(info, base) != 0);
}
final byte[] idBuffer() {
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 9c47187..b4e2d2c 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 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 DirCacheIterator(final DirCache dc) {
}
@Override
- public AbstractTreeIterator createSubtreeIterator(final Repository repo)
+ public AbstractTreeIterator createSubtreeIterator(final ObjectReader reader)
throws IncorrectObjectTypeException, IOException {
if (currentSubtree == null)
throw new IncorrectObjectTypeException(getEntryObjectId(),
diff --git a/org.eclipse.jgit/src/org/eclipse/jgit/dircache/DirCacheTree.java b/org.eclipse.jgit/src/org/eclipse/jgit/dircache/DirCacheTree.java
index 144b1a6..e04b797 100644
--- a/org.eclipse.jgit/src/org/eclipse/jgit/dircache/DirCacheTree.java
+++ b/org.eclipse.jgit/src/org/eclipse/jgit/dircache/DirCacheTree.java
@@ -57,7 +57,7 @@
import org.eclipse.jgit.lib.Constants;
import org.eclipse.jgit.lib.FileMode;
import org.eclipse.jgit.lib.ObjectId;
-import org.eclipse.jgit.lib.ObjectWriter;
+import org.eclipse.jgit.lib.ObjectInserter;
import org.eclipse.jgit.util.MutableInteger;
import org.eclipse.jgit.util.RawParseUtils;
@@ -311,7 +311,7 @@ public String getPathString() {
* an unexpected error occurred writing to the object store.
*/
ObjectId writeTree(final DirCacheEntry[] cache, int cIdx,
- final int pathOffset, final ObjectWriter ow)
+ final int pathOffset, final ObjectInserter ow)
throws UnmergedPathException, IOException {
if (id == null) {
final int endIdx = cIdx + entrySpan;
@@ -346,13 +346,13 @@ ObjectId writeTree(final DirCacheEntry[] cache, int cIdx,
entryIdx++;
}
- id = ow.writeCanonicalTree(out.toByteArray());
+ id = ow.insert(Constants.OBJ_TREE, out.toByteArray());
}
return id;
}
private int computeSize(final DirCacheEntry[] cache, int cIdx,
- final int pathOffset, final ObjectWriter ow)
+ final int pathOffset, final ObjectInserter ow)
throws UnmergedPathException, IOException {
final int endIdx = cIdx + entrySpan;
int childIdx = 0;
diff --git a/org.eclipse.jgit/src/org/eclipse/jgit/lib/RepositoryAdapter.java b/org.eclipse.jgit/src/org/eclipse/jgit/errors/LargeObjectException.java
similarity index 73%
copy from org.eclipse.jgit/src/org/eclipse/jgit/lib/RepositoryAdapter.java
copy to org.eclipse.jgit/src/org/eclipse/jgit/errors/LargeObjectException.java
index e43c33a..d897c51 100644
--- a/org.eclipse.jgit/src/org/eclipse/jgit/lib/RepositoryAdapter.java
+++ b/org.eclipse.jgit/src/org/eclipse/jgit/errors/LargeObjectException.java
@@ -1,5 +1,5 @@
/*
- * Copyright (C) 2008, Robin Rosenberg <robin.rosenberg@dewire.com>
+ * Copyright (C) 2010, Google Inc.
* and other copyright owners as documented in the project's IP log.
*
* This program and the accompanying materials are made available
@@ -41,20 +41,27 @@
* ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
*/
-package org.eclipse.jgit.lib;
+package org.eclipse.jgit.errors;
-/**
- * A default {@link RepositoryListener} that does nothing except invoke an
- * optional general method for any repository change.
- */
-public class RepositoryAdapter implements RepositoryListener {
+import org.eclipse.jgit.lib.ObjectId;
- public void indexChanged(final IndexChangedEvent e) {
- // Empty
+/** An object is too big to load into memory as a single byte array. */
+public class LargeObjectException extends RuntimeException {
+ private static final long serialVersionUID = 1L;
+
+ /** Create a large object exception, where the object isn't known. */
+ public LargeObjectException() {
+ // Do nothing.
}
- public void refsChanged(final RefsChangedEvent e) {
- // Empty
+ /**
+ * Create a large object exception, naming the object that is too big.
+ *
+ * @param id
+ * identity of the object that is too big to be loaded as a byte
+ * array in this JVM.
+ */
+ public LargeObjectException(ObjectId id) {
+ super(id.name());
}
-
}
diff --git a/org.eclipse.jgit/src/org/eclipse/jgit/lib/RepositoryAdapter.java b/org.eclipse.jgit/src/org/eclipse/jgit/errors/NoWorkTreeException.java
similarity index 78%
copy from org.eclipse.jgit/src/org/eclipse/jgit/lib/RepositoryAdapter.java
copy to org.eclipse.jgit/src/org/eclipse/jgit/errors/NoWorkTreeException.java
index e43c33a..f2980ef 100644
--- a/org.eclipse.jgit/src/org/eclipse/jgit/lib/RepositoryAdapter.java
+++ b/org.eclipse.jgit/src/org/eclipse/jgit/errors/NoWorkTreeException.java
@@ -1,5 +1,5 @@
/*
- * Copyright (C) 2008, Robin Rosenberg <robin.rosenberg@dewire.com>
+ * Copyright (C) 2010, Google Inc.
* and other copyright owners as documented in the project's IP log.
*
* This program and the accompanying materials are made available
@@ -41,20 +41,19 @@
* ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
*/
-package org.eclipse.jgit.lib;
+package org.eclipse.jgit.errors;
+
+import org.eclipse.jgit.JGitText;
+import org.eclipse.jgit.lib.Repository;
/**
- * A default {@link RepositoryListener} that does nothing except invoke an
- * optional general method for any repository change.
+ * Indicates a {@link Repository} has no working directory, and is thus bare.
*/
-public class RepositoryAdapter implements RepositoryListener {
+public class NoWorkTreeException extends IllegalStateException {
+ private static final long serialVersionUID = 1L;
- public void indexChanged(final IndexChangedEvent e) {
- // Empty
+ /** Creates an exception indicating there is no work tree for a repository. */
+ public NoWorkTreeException() {
+ super(JGitText.get().bareRepositoryNoWorkdirAndIndex);
}
-
- public void refsChanged(final RefsChangedEvent e) {
- // Empty
- }
-
}
diff --git a/org.eclipse.jgit/src/org/eclipse/jgit/lib/RepositoryAdapter.java b/org.eclipse.jgit/src/org/eclipse/jgit/errors/StoredObjectRepresentationNotAvailableException.java
similarity index 77%
copy from org.eclipse.jgit/src/org/eclipse/jgit/lib/RepositoryAdapter.java
copy to org.eclipse.jgit/src/org/eclipse/jgit/errors/StoredObjectRepresentationNotAvailableException.java
index e43c33a..e9e3f4d 100644
--- a/org.eclipse.jgit/src/org/eclipse/jgit/lib/RepositoryAdapter.java
+++ b/org.eclipse.jgit/src/org/eclipse/jgit/errors/StoredObjectRepresentationNotAvailableException.java
@@ -1,5 +1,5 @@
/*
- * Copyright (C) 2008, Robin Rosenberg <robin.rosenberg@dewire.com>
+ * Copyright (C) 2010, Google Inc.
* and other copyright owners as documented in the project's IP log.
*
* This program and the accompanying materials are made available
@@ -41,20 +41,21 @@
* ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
*/
-package org.eclipse.jgit.lib;
+package org.eclipse.jgit.errors;
-/**
- * A default {@link RepositoryListener} that does nothing except invoke an
- * optional general method for any repository change.
- */
-public class RepositoryAdapter implements RepositoryListener {
+import org.eclipse.jgit.storage.pack.ObjectToPack;
- public void indexChanged(final IndexChangedEvent e) {
- // Empty
+/** A previously selected representation is no longer available. */
+public class StoredObjectRepresentationNotAvailableException extends Exception {
+ private static final long serialVersionUID = 1L;
+
+ /**
+ * Construct an error for an object.
+ *
+ * @param otp
+ * the object whose current representation is no longer present.
+ */
+ public StoredObjectRepresentationNotAvailableException(ObjectToPack otp) {
+ // Do nothing.
}
-
- public void refsChanged(final RefsChangedEvent e) {
- // Empty
- }
-
}
diff --git a/org.eclipse.jgit/src/org/eclipse/jgit/lib/RepositoryAdapter.java b/org.eclipse.jgit/src/org/eclipse/jgit/events/ConfigChangedEvent.java
similarity index 81%
copy from org.eclipse.jgit/src/org/eclipse/jgit/lib/RepositoryAdapter.java
copy to org.eclipse.jgit/src/org/eclipse/jgit/events/ConfigChangedEvent.java
index e43c33a..79598ea 100644
--- a/org.eclipse.jgit/src/org/eclipse/jgit/lib/RepositoryAdapter.java
+++ b/org.eclipse.jgit/src/org/eclipse/jgit/events/ConfigChangedEvent.java
@@ -1,5 +1,5 @@
/*
- * Copyright (C) 2008, Robin Rosenberg <robin.rosenberg@dewire.com>
+ * Copyright (C) 2010, Google Inc.
* and other copyright owners as documented in the project's IP log.
*
* This program and the accompanying materials are made available
@@ -41,20 +41,17 @@
* ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
*/
-package org.eclipse.jgit.lib;
+package org.eclipse.jgit.events;
-/**
- * A default {@link RepositoryListener} that does nothing except invoke an
- * optional general method for any repository change.
- */
-public class RepositoryAdapter implements RepositoryListener {
-
- public void indexChanged(final IndexChangedEvent e) {
- // Empty
+/** Describes a change to one or more keys in the configuration. */
+public class ConfigChangedEvent extends RepositoryEvent<ConfigChangedListener> {
+ @Override
+ public Class<ConfigChangedListener> getListenerType() {
+ return ConfigChangedListener.class;
}
- public void refsChanged(final RefsChangedEvent e) {
- // Empty
+ @Override
+ public void dispatch(ConfigChangedListener listener) {
+ listener.onConfigChanged(this);
}
-
}
diff --git a/org.eclipse.jgit/src/org/eclipse/jgit/lib/RepositoryAdapter.java b/org.eclipse.jgit/src/org/eclipse/jgit/events/ConfigChangedListener.java
similarity index 83%
copy from org.eclipse.jgit/src/org/eclipse/jgit/lib/RepositoryAdapter.java
copy to org.eclipse.jgit/src/org/eclipse/jgit/events/ConfigChangedListener.java
index e43c33a..322cf7f 100644
--- a/org.eclipse.jgit/src/org/eclipse/jgit/lib/RepositoryAdapter.java
+++ b/org.eclipse.jgit/src/org/eclipse/jgit/events/ConfigChangedListener.java
@@ -41,20 +41,15 @@
* ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
*/
-package org.eclipse.jgit.lib;
+package org.eclipse.jgit.events;
-/**
- * A default {@link RepositoryListener} that does nothing except invoke an
- * optional general method for any repository change.
- */
-public class RepositoryAdapter implements RepositoryListener {
-
- public void indexChanged(final IndexChangedEvent e) {
- // Empty
- }
-
- public void refsChanged(final RefsChangedEvent e) {
- // Empty
- }
-
+/** Receives {@link ConfigChangedEvent}s. */
+public interface ConfigChangedListener extends RepositoryListener {
+ /**
+ * Invoked when any change is made to the configuration.
+ *
+ * @param event
+ * information about the changes.
+ */
+ void onConfigChanged(ConfigChangedEvent event);
}
diff --git a/org.eclipse.jgit/src/org/eclipse/jgit/lib/RepositoryAdapter.java b/org.eclipse.jgit/src/org/eclipse/jgit/events/IndexChangedEvent.java
similarity index 81%
copy from org.eclipse.jgit/src/org/eclipse/jgit/lib/RepositoryAdapter.java
copy to org.eclipse.jgit/src/org/eclipse/jgit/events/IndexChangedEvent.java
index e43c33a..a54288e 100644
--- a/org.eclipse.jgit/src/org/eclipse/jgit/lib/RepositoryAdapter.java
+++ b/org.eclipse.jgit/src/org/eclipse/jgit/events/IndexChangedEvent.java
@@ -1,5 +1,5 @@
/*
- * Copyright (C) 2008, Robin Rosenberg <robin.rosenberg@dewire.com>
+ * Copyright (C) 2010, Google Inc.
* and other copyright owners as documented in the project's IP log.
*
* This program and the accompanying materials are made available
@@ -41,20 +41,17 @@
* ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
*/
-package org.eclipse.jgit.lib;
+package org.eclipse.jgit.events;
-/**
- * A default {@link RepositoryListener} that does nothing except invoke an
- * optional general method for any repository change.
- */
-public class RepositoryAdapter implements RepositoryListener {
-
- public void indexChanged(final IndexChangedEvent e) {
- // Empty
+/** Describes a change to one or more paths in the index file. */
+public class IndexChangedEvent extends RepositoryEvent<IndexChangedListener> {
+ @Override
+ public Class<IndexChangedListener> getListenerType() {
+ return IndexChangedListener.class;
}
- public void refsChanged(final RefsChangedEvent e) {
- // Empty
+ @Override
+ public void dispatch(IndexChangedListener listener) {
+ listener.onIndexChanged(this);
}
-
}
diff --git a/org.eclipse.jgit/src/org/eclipse/jgit/lib/RepositoryAdapter.java b/org.eclipse.jgit/src/org/eclipse/jgit/events/IndexChangedListener.java
similarity index 83%
rename from org.eclipse.jgit/src/org/eclipse/jgit/lib/RepositoryAdapter.java
rename to org.eclipse.jgit/src/org/eclipse/jgit/events/IndexChangedListener.java
index e43c33a..d41ef74 100644
--- a/org.eclipse.jgit/src/org/eclipse/jgit/lib/RepositoryAdapter.java
+++ b/org.eclipse.jgit/src/org/eclipse/jgit/events/IndexChangedListener.java
@@ -41,20 +41,15 @@
* ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
*/
-package org.eclipse.jgit.lib;
+package org.eclipse.jgit.events;
-/**
- * A default {@link RepositoryListener} that does nothing except invoke an
- * optional general method for any repository change.
- */
-public class RepositoryAdapter implements RepositoryListener {
-
- public void indexChanged(final IndexChangedEvent e) {
- // Empty
- }
-
- public void refsChanged(final RefsChangedEvent e) {
- // Empty
- }
-
+/** Receives {@link IndexChangedEvent}s. */
+public interface IndexChangedListener extends RepositoryListener {
+ /**
+ * Invoked when any change is made to the index.
+ *
+ * @param event
+ * information about the changes.
+ */
+ void onIndexChanged(IndexChangedEvent event);
}
diff --git a/org.eclipse.jgit/src/org/eclipse/jgit/lib/RepositoryChangedEvent.java b/org.eclipse.jgit/src/org/eclipse/jgit/events/ListenerHandle.java
similarity index 75%
rename from org.eclipse.jgit/src/org/eclipse/jgit/lib/RepositoryChangedEvent.java
rename to org.eclipse.jgit/src/org/eclipse/jgit/events/ListenerHandle.java
index 495049c..ef90b22 100644
--- a/org.eclipse.jgit/src/org/eclipse/jgit/lib/RepositoryChangedEvent.java
+++ b/org.eclipse.jgit/src/org/eclipse/jgit/events/ListenerHandle.java
@@ -1,5 +1,5 @@
/*
- * Copyright (C) 2008, Robin Rosenberg <robin.rosenberg@dewire.com>
+ * Copyright (C) 2010, Google Inc.
* and other copyright owners as documented in the project's IP log.
*
* This program and the accompanying materials are made available
@@ -41,30 +41,31 @@
* ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
*/
-package org.eclipse.jgit.lib;
+package org.eclipse.jgit.events;
-/**
- * This class passes information about changed refs to a
- * {@link RepositoryListener}
- *
- * Currently only a reference to the repository is passed.
- */
-public class RepositoryChangedEvent {
- private final Repository repository;
+/** Tracks a previously registered {@link RepositoryListener}. */
+public class ListenerHandle {
+ private final ListenerList parent;
- RepositoryChangedEvent(final Repository repository) {
- this.repository = repository;
+ final Class<? extends RepositoryListener> type;
+
+ final RepositoryListener listener;
+
+ ListenerHandle(ListenerList parent,
+ Class<? extends RepositoryListener> type,
+ RepositoryListener listener) {
+ this.parent = parent;
+ this.type = type;
+ this.listener = listener;
}
- /**
- * @return the repository that was changed
- */
- public Repository getRepository() {
- return repository;
+ /** Remove the listener and stop receiving events. */
+ public void remove() {
+ parent.remove(this);
}
@Override
public String toString() {
- return "RepositoryChangedEvent[" + repository + "]";
+ return type.getSimpleName() + "[" + listener + "]";
}
}
diff --git a/org.eclipse.jgit/src/org/eclipse/jgit/events/ListenerList.java b/org.eclipse.jgit/src/org/eclipse/jgit/events/ListenerList.java
new file mode 100644
index 0000000..6ac4b0f
--- /dev/null
+++ b/org.eclipse.jgit/src/org/eclipse/jgit/events/ListenerList.java
@@ -0,0 +1,142 @@
+/*
+ * Copyright (C) 2010, Google Inc.
+ * and other copyright owners as documented in the project's IP log.
+ *
+ * This program and the accompanying materials are made available
+ * under the terms of the Eclipse Distribution License v1.0 which
+ * accompanies this distribution, is reproduced below, and is
+ * available at http://www.eclipse.org/org/documents/edl-v10.php
+ *
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or
+ * without modification, are permitted provided that the following
+ * conditions are met:
+ *
+ * - Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ *
+ * - Redistributions in binary form must reproduce the above
+ * copyright notice, this list of conditions and the following
+ * disclaimer in the documentation and/or other materials provided
+ * with the distribution.
+ *
+ * - Neither the name of the Eclipse Foundation, Inc. nor the
+ * names of its contributors may be used to endorse or promote
+ * products derived from this software without specific prior
+ * written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND
+ * CONTRIBUTORS "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES,
+ * INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
+ * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR
+ * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
+ * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT
+ * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
+ * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER
+ * CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT,
+ * STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
+ * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF
+ * ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ */
+
+package org.eclipse.jgit.events;
+
+import java.util.List;
+import java.util.concurrent.ConcurrentHashMap;
+import java.util.concurrent.ConcurrentMap;
+import java.util.concurrent.CopyOnWriteArrayList;
+
+/** Manages a thread-safe list of {@link RepositoryListener}s. */
+public class ListenerList {
+ private final ConcurrentMap<Class<? extends RepositoryListener>, CopyOnWriteArrayList<ListenerHandle>> lists = new ConcurrentHashMap<Class<? extends RepositoryListener>, CopyOnWriteArrayList<ListenerHandle>>();
+
+ /**
+ * Register an IndexChangedListener.
+ *
+ * @param listener
+ * the listener implementation.
+ * @return handle to later remove the listener.
+ */
+ public ListenerHandle addIndexChangedListener(IndexChangedListener listener) {
+ return addListener(IndexChangedListener.class, listener);
+ }
+
+ /**
+ * Register a RefsChangedListener.
+ *
+ * @param listener
+ * the listener implementation.
+ * @return handle to later remove the listener.
+ */
+ public ListenerHandle addRefsChangedListener(RefsChangedListener listener) {
+ return addListener(RefsChangedListener.class, listener);
+ }
+
+ /**
+ * Register a ConfigChangedListener.
+ *
+ * @param listener
+ * the listener implementation.
+ * @return handle to later remove the listener.
+ */
+ public ListenerHandle addConfigChangedListener(
+ ConfigChangedListener listener) {
+ return addListener(ConfigChangedListener.class, listener);
+ }
+
+ /**
+ * Add a listener to the list.
+ *
+ * @param <T>
+ * the type of listener being registered.
+ * @param type
+ * type of listener being registered.
+ * @param listener
+ * the listener instance.
+ * @return a handle to later remove the registration, if desired.
+ */
+ public <T extends RepositoryListener> ListenerHandle addListener(
+ Class<T> type, T listener) {
+ ListenerHandle handle = new ListenerHandle(this, type, listener);
+ add(handle);
+ return handle;
+ }
+
+ /**
+ * Dispatch an event to all interested listeners.
+ * <p>
+ * Listeners are selected by the type of listener the event delivers to.
+ *
+ * @param event
+ * the event to deliver.
+ */
+ @SuppressWarnings("unchecked")
+ public void dispatch(RepositoryEvent event) {
+ List<ListenerHandle> list = lists.get(event.getListenerType());
+ if (list != null) {
+ for (ListenerHandle handle : list)
+ event.dispatch(handle.listener);
+ }
+ }
+
+ private void add(ListenerHandle handle) {
+ List<ListenerHandle> list = lists.get(handle.type);
+ if (list == null) {
+ CopyOnWriteArrayList<ListenerHandle> newList;
+
+ newList = new CopyOnWriteArrayList<ListenerHandle>();
+ list = lists.putIfAbsent(handle.type, newList);
+ if (list == null)
+ list = newList;
+ }
+ list.add(handle);
+ }
+
+ void remove(ListenerHandle handle) {
+ List<ListenerHandle> list = lists.get(handle.type);
+ if (list != null)
+ list.remove(handle);
+ }
+}
diff --git a/org.eclipse.jgit/src/org/eclipse/jgit/lib/RepositoryAdapter.java b/org.eclipse.jgit/src/org/eclipse/jgit/events/RefsChangedEvent.java
similarity index 81%
copy from org.eclipse.jgit/src/org/eclipse/jgit/lib/RepositoryAdapter.java
copy to org.eclipse.jgit/src/org/eclipse/jgit/events/RefsChangedEvent.java
index e43c33a..36af3f8 100644
--- a/org.eclipse.jgit/src/org/eclipse/jgit/lib/RepositoryAdapter.java
+++ b/org.eclipse.jgit/src/org/eclipse/jgit/events/RefsChangedEvent.java
@@ -1,5 +1,5 @@
/*
- * Copyright (C) 2008, Robin Rosenberg <robin.rosenberg@dewire.com>
+ * Copyright (C) 2010, Google Inc.
* and other copyright owners as documented in the project's IP log.
*
* This program and the accompanying materials are made available
@@ -41,20 +41,17 @@
* ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
*/
-package org.eclipse.jgit.lib;
+package org.eclipse.jgit.events;
-/**
- * A default {@link RepositoryListener} that does nothing except invoke an
- * optional general method for any repository change.
- */
-public class RepositoryAdapter implements RepositoryListener {
-
- public void indexChanged(final IndexChangedEvent e) {
- // Empty
+/** Describes a change to one or more references of a repository. */
+public class RefsChangedEvent extends RepositoryEvent<RefsChangedListener> {
+ @Override
+ public Class<RefsChangedListener> getListenerType() {
+ return RefsChangedListener.class;
}
- public void refsChanged(final RefsChangedEvent e) {
- // Empty
+ @Override
+ public void dispatch(RefsChangedListener listener) {
+ listener.onRefsChanged(this);
}
-
}
diff --git a/org.eclipse.jgit/src/org/eclipse/jgit/lib/RepositoryAdapter.java b/org.eclipse.jgit/src/org/eclipse/jgit/events/RefsChangedListener.java
similarity index 83%
copy from org.eclipse.jgit/src/org/eclipse/jgit/lib/RepositoryAdapter.java
copy to org.eclipse.jgit/src/org/eclipse/jgit/events/RefsChangedListener.java
index e43c33a..9c0f4ed 100644
--- a/org.eclipse.jgit/src/org/eclipse/jgit/lib/RepositoryAdapter.java
+++ b/org.eclipse.jgit/src/org/eclipse/jgit/events/RefsChangedListener.java
@@ -41,20 +41,15 @@
* ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
*/
-package org.eclipse.jgit.lib;
+package org.eclipse.jgit.events;
-/**
- * A default {@link RepositoryListener} that does nothing except invoke an
- * optional general method for any repository change.
- */
-public class RepositoryAdapter implements RepositoryListener {
-
- public void indexChanged(final IndexChangedEvent e) {
- // Empty
- }
-
- public void refsChanged(final RefsChangedEvent e) {
- // Empty
- }
-
+/** Receives {@link RefsChangedEvent}s. */
+public interface RefsChangedListener extends RepositoryListener {
+ /**
+ * Invoked when any reference changes.
+ *
+ * @param event
+ * information about the changes.
+ */
+ void onRefsChanged(RefsChangedEvent event);
}
diff --git a/org.eclipse.jgit/src/org/eclipse/jgit/lib/RepositoryChangedEvent.java b/org.eclipse.jgit/src/org/eclipse/jgit/events/RepositoryEvent.java
similarity index 64%
copy from org.eclipse.jgit/src/org/eclipse/jgit/lib/RepositoryChangedEvent.java
copy to org.eclipse.jgit/src/org/eclipse/jgit/events/RepositoryEvent.java
index 495049c..ba1c81d 100644
--- a/org.eclipse.jgit/src/org/eclipse/jgit/lib/RepositoryChangedEvent.java
+++ b/org.eclipse.jgit/src/org/eclipse/jgit/events/RepositoryEvent.java
@@ -1,4 +1,5 @@
/*
+ * Copyright (C) 2010, Google Inc.
* Copyright (C) 2008, Robin Rosenberg <robin.rosenberg@dewire.com>
* and other copyright owners as documented in the project's IP log.
*
@@ -41,30 +42,54 @@
* ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
*/
-package org.eclipse.jgit.lib;
+package org.eclipse.jgit.events;
+
+import org.eclipse.jgit.lib.Repository;
/**
- * This class passes information about changed refs to a
- * {@link RepositoryListener}
+ * Describes a modification made to a repository.
*
- * Currently only a reference to the repository is passed.
+ * @param <T>
+ * type of listener this event dispatches to.
*/
-public class RepositoryChangedEvent {
- private final Repository repository;
-
- RepositoryChangedEvent(final Repository repository) {
- this.repository = repository;
- }
+public abstract class RepositoryEvent<T extends RepositoryListener> {
+ private Repository repository;
/**
- * @return the repository that was changed
+ * Set the repository this event occurred on.
+ * <p>
+ * This method should only be invoked once on each event object, and is
+ * automatically set by {@link Repository#fireEvent(RepositoryEvent)}.
+ *
+ * @param r
+ * the repository.
*/
+ public void setRepository(Repository r) {
+ if (repository == null)
+ repository = r;
+ }
+
+ /** @return the repository that was changed. */
public Repository getRepository() {
return repository;
}
+ /** @return type of listener this event dispatches to. */
+ public abstract Class<T> getListenerType();
+
+ /**
+ * Dispatch this event to the given listener.
+ *
+ * @param listener
+ * listener that wants this event.
+ */
+ public abstract void dispatch(T listener);
+
@Override
public String toString() {
- return "RepositoryChangedEvent[" + repository + "]";
+ String type = getClass().getSimpleName();
+ if (repository == null)
+ return type;
+ return type + "[" + repository + "]";
}
}
diff --git a/org.eclipse.jgit/src/org/eclipse/jgit/lib/RepositoryAdapter.java b/org.eclipse.jgit/src/org/eclipse/jgit/events/RepositoryListener.java
similarity index 80%
copy from org.eclipse.jgit/src/org/eclipse/jgit/lib/RepositoryAdapter.java
copy to org.eclipse.jgit/src/org/eclipse/jgit/events/RepositoryListener.java
index e43c33a..4f951e5 100644
--- a/org.eclipse.jgit/src/org/eclipse/jgit/lib/RepositoryAdapter.java
+++ b/org.eclipse.jgit/src/org/eclipse/jgit/events/RepositoryListener.java
@@ -1,5 +1,5 @@
/*
- * Copyright (C) 2008, Robin Rosenberg <robin.rosenberg@dewire.com>
+ * Copyright (C) 2010, Google Inc.
* and other copyright owners as documented in the project's IP log.
*
* This program and the accompanying materials are made available
@@ -41,20 +41,9 @@
* ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
*/
-package org.eclipse.jgit.lib;
+package org.eclipse.jgit.events;
-/**
- * A default {@link RepositoryListener} that does nothing except invoke an
- * optional general method for any repository change.
- */
-public class RepositoryAdapter implements RepositoryListener {
-
- public void indexChanged(final IndexChangedEvent e) {
- // Empty
- }
-
- public void refsChanged(final RefsChangedEvent e) {
- // Empty
- }
-
+/** A listener can register for event delivery. */
+public interface RepositoryListener {
+ // Empty marker interface; see extensions for actual methods.
}
diff --git a/org.eclipse.jgit/src/org/eclipse/jgit/ignore/IgnoreNode.java b/org.eclipse.jgit/src/org/eclipse/jgit/ignore/IgnoreNode.java
index f29fa1e..575b9d6 100644
--- a/org.eclipse.jgit/src/org/eclipse/jgit/ignore/IgnoreNode.java
+++ b/org.eclipse.jgit/src/org/eclipse/jgit/ignore/IgnoreNode.java
@@ -43,194 +43,104 @@
package org.eclipse.jgit.ignore;
import java.io.BufferedReader;
-import java.io.File;
-import java.io.FileReader;
import java.io.IOException;
+import java.io.InputStream;
+import java.io.InputStreamReader;
import java.util.ArrayList;
+import java.util.Collections;
+import java.util.List;
+
+import org.eclipse.jgit.lib.Constants;
/**
* Represents a bundle of ignore rules inherited from a base directory.
- * Each IgnoreNode corresponds to one directory. Most IgnoreNodes will have
- * at most one source of ignore information -- its .gitignore file.
- * <br><br>
- * At the root of the repository, there may be an additional source of
- * ignore information (the exclude file)
- * <br><br>
- * It is recommended that implementers call the {@link #isIgnored(String)} method
- * rather than try to use the rules manually. The method will handle rule priority
- * automatically.
*
+ * This class is not thread safe, it maintains state about the last match.
*/
public class IgnoreNode {
- //The base directory will be used to find the .gitignore file
- private File baseDir;
- //Only used for root node.
- private File secondaryFile;
- private ArrayList<IgnoreRule> rules;
- //Indicates whether a match was made. Necessary to terminate early when a negation is encountered
- private boolean matched;
- //Indicates whether a match was made. Necessary to terminate early when a negation is encountered
- private long lastModified;
+ /** Result from {@link IgnoreNode#isIgnored(String, boolean)}. */
+ public static enum MatchResult {
+ /** The file is not ignored, due to a rule saying its not ignored. */
+ NOT_IGNORED,
+
+ /** The file is ignored due to a rule in this node. */
+ IGNORED,
+
+ /** The ignore status is unknown, check inherited rules. */
+ CHECK_PARENT;
+ }
+
+ /** The rules that have been parsed into this node. */
+ private final List<IgnoreRule> rules;
+
+ /** Create an empty ignore node with no rules. */
+ public IgnoreNode() {
+ rules = new ArrayList<IgnoreRule>();
+ }
/**
- * Create a new ignore node based on the given directory. The node's
- * ignore file will be the .gitignore file in the directory (if any)
- * Rules contained within this node will only be applied to files
- * which are descendants of this directory.
+ * Create an ignore node with given rules.
*
- * @param baseDir
- * base directory of this ignore node
- */
- public IgnoreNode(File baseDir) {
- this.baseDir = baseDir;
- rules = new ArrayList<IgnoreRule>();
- secondaryFile = null;
- lastModified = 0l;
+ * @param rules
+ * list of rules.
+ **/
+ public IgnoreNode(List<IgnoreRule> rules) {
+ this.rules = rules;
}
/**
* Parse files according to gitignore standards.
*
+ * @param in
+ * input stream holding the standard ignore format. The caller is
+ * responsible for closing the stream.
* @throws IOException
- * Error thrown when reading an ignore file.
+ * Error thrown when reading an ignore file.
*/
- private void parse() throws IOException {
- if (secondaryFile != null && secondaryFile.exists())
- parse(secondaryFile);
-
- parse(new File(baseDir.getAbsolutePath(), ".gitignore"));
- }
-
- private void parse(File targetFile) throws IOException {
- if (!targetFile.exists())
- return;
-
- BufferedReader br = new BufferedReader(new FileReader(targetFile));
+ public void parse(InputStream in) throws IOException {
+ BufferedReader br = asReader(in);
String txt;
- try {
- while ((txt = br.readLine()) != null) {
- txt = txt.trim();
- if (txt.length() > 0 && !txt.startsWith("#"))
- rules.add(new IgnoreRule(txt));
- }
- } finally {
- br.close();
+ while ((txt = br.readLine()) != null) {
+ txt = txt.trim();
+ if (txt.length() > 0 && !txt.startsWith("#"))
+ rules.add(new IgnoreRule(txt));
}
}
- /**
- * @return
- * Base directory to which these rules apply, absolute path
- */
- public File getBaseDir() {
- return baseDir;
+ private static BufferedReader asReader(InputStream in) {
+ return new BufferedReader(new InputStreamReader(in, Constants.CHARSET));
}
-
- /**
- *
- * @return
- * List of all ignore rules held by this node
- */
- public ArrayList<IgnoreRule> getRules() {
- return rules;
+ /** @return list of all ignore rules held by this node. */
+ public List<IgnoreRule> getRules() {
+ return Collections.unmodifiableList(rules);
}
-
/**
+ * Determine if an entry path matches an ignore rule.
*
- * Returns whether or not a target is matched as being ignored by
- * any patterns in this directory.
- * <br>
- * Will return false if the file is not a descendant of this directory.
- * <br>
- *
- * @param target
- * Absolute path to the file. This makes stripping common path elements easier.
- * @return
- * true if target is ignored, false if the target is explicitly not
- * ignored or if no rules exist for the target.
- * @throws IOException
- * Failed to parse rules
- *
+ * @param entryPath
+ * the path to test. The path must be relative to this ignore
+ * node's own repository path, and in repository path format
+ * (uses '/' and not '\').
+ * @param isDirectory
+ * true if the target item is a directory.
+ * @return status of the path.
*/
- public boolean isIgnored(String target) throws IOException {
- matched = false;
- File targetFile = new File(target);
- String tar = baseDir.toURI().relativize(targetFile.toURI()).getPath();
-
- if (tar.length() == target.length())
- //target is not a derivative of baseDir, this node has no jurisdiction
- return false;
-
- if (rules.isEmpty()) {
- //Either we haven't parsed yet, or the file is empty.
- //Empty file should be very fast to parse
- parse();
- }
+ public MatchResult isIgnored(String entryPath, boolean isDirectory) {
if (rules.isEmpty())
- return false;
+ return MatchResult.CHECK_PARENT;
- /*
- * Boolean matched is necessary because we may have encountered
- * a negation ("!/test.c").
- */
-
- int i;
- //Parse rules in the reverse order that they were read
- for (i = rules.size() -1; i > -1; i--) {
- matched = rules.get(i).isMatch(tar, targetFile.isDirectory());
- if (matched)
- break;
+ // Parse rules in the reverse order that they were read
+ for (int i = rules.size() - 1; i > -1; i--) {
+ IgnoreRule rule = rules.get(i);
+ if (rule.isMatch(entryPath, isDirectory)) {
+ if (rule.getResult())
+ return MatchResult.IGNORED;
+ else
+ return MatchResult.NOT_IGNORED;
+ }
}
-
- if (i > -1 && rules.get(i) != null)
- return rules.get(i).getResult();
-
- return false;
- }
-
- /**
- * @return
- * True if the previous call to isIgnored resulted in a match,
- * false otherwise.
- */
- public boolean wasMatched() {
- return matched;
- }
-
- /**
- * Adds another file as a source of ignore rules for this file. The
- * secondary file will have a lower priority than the first file, and
- * the parent directory of this node will be regarded as firstFile.getParent()
- *
- * @param f
- * Secondary source of gitignore information for this node
- */
- public void addSecondarySource(File f) {
- secondaryFile = f;
- }
-
- /**
- * Clear all rules in this node.
- */
- public void clear() {
- rules.clear();
- }
-
- /**
- * @param val
- * Set the last modified time of this node.
- */
- public void setLastModified(long val) {
- lastModified = val;
- }
-
- /**
- * @return
- * Last modified time of this node.
- */
- public long getLastModified() {
- return lastModified;
+ return MatchResult.CHECK_PARENT;
}
}
diff --git a/org.eclipse.jgit/src/org/eclipse/jgit/ignore/IgnoreRule.java b/org.eclipse.jgit/src/org/eclipse/jgit/ignore/IgnoreRule.java
index 982ce06..ac3ec66 100644
--- a/org.eclipse.jgit/src/org/eclipse/jgit/ignore/IgnoreRule.java
+++ b/org.eclipse.jgit/src/org/eclipse/jgit/ignore/IgnoreRule.java
@@ -91,10 +91,11 @@ private void setup() {
endIndex --;
dirOnly = true;
}
+ boolean hasSlash = pattern.contains("/");
pattern = pattern.substring(startIndex, endIndex);
- if (!pattern.contains("/"))
+ if (!hasSlash)
nameOnly = true;
else if (!pattern.startsWith("/")) {
//Contains "/" but does not start with one
diff --git a/org.eclipse.jgit/src/org/eclipse/jgit/ignore/SimpleIgnoreCache.java b/org.eclipse.jgit/src/org/eclipse/jgit/ignore/SimpleIgnoreCache.java
deleted file mode 100644
index be37a9a..0000000
--- a/org.eclipse.jgit/src/org/eclipse/jgit/ignore/SimpleIgnoreCache.java
+++ /dev/null
@@ -1,308 +0,0 @@
-/*
- * Copyright (C) 2010, Red Hat 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.ignore;
-
-import java.io.File;
-import java.io.IOException;
-import java.net.URI;
-import java.util.HashMap;
-import java.util.HashSet;
-
-import org.eclipse.jgit.lib.Repository;
-import org.eclipse.jgit.treewalk.FileTreeIterator;
-import org.eclipse.jgit.treewalk.TreeWalk;
-import org.eclipse.jgit.util.FS;
-
-/**
- * A simple ignore cache. Stores ignore information on .gitignore and exclude files.
- * <br><br>
- * The cache can be initialized by calling {@link #initialize()} on a
- * target file.
- *
- * Inspiration from: Ferry Huberts
- */
-public class SimpleIgnoreCache {
-
- /**
- * Map of ignore nodes, indexed by base directory. By convention, the
- * base directory string should NOT start or end with a "/". Use
- * {@link #relativize(File)} before appending nodes to the ignoreMap
- * <br>
- * e.g: path/to/directory is a valid String
- */
- private HashMap<String, IgnoreNode> ignoreMap;
-
- //Repository associated with this cache
- private Repository repository;
-
- //Base directory of this cache
- private URI rootFileURI;
-
- /**
- * Creates a base implementation of an ignore cache. This default implementation
- * will search for all .gitignore files in all children of the base directory,
- * and grab the exclude file from baseDir/.git/info/exclude.
- * <br><br>
- * Call {@link #initialize()} to fetch the ignore information relevant
- * to a target file.
- * @param repository
- * Repository to associate this cache with. The cache's base directory will
- * be set to this repository's GIT_DIR
- *
- */
- public SimpleIgnoreCache(Repository repository) {
- ignoreMap = new HashMap<String, IgnoreNode>();
- this.repository = repository;
- this.rootFileURI = repository.getWorkDir().toURI();
- }
-
- /**
- * Initializes the ignore map for the target file and all parents.
- * This will delete existing ignore information for all folders
- * on the partial initialization path. Will only function for files
- * that are children of the cache's basePath.
- * <br><br>
- * Note that this does not initialize the ignore rules. Ignore rules will
- * be parsed when needed during a call to {@link #isIgnored(String)}
- *
- * @throws IOException
- * The tree could not be walked.
- */
- public void initialize() throws IOException {
- TreeWalk tw = new TreeWalk(repository);
- tw.reset();
- tw.addTree(new FileTreeIterator(repository.getWorkDir(), FS.DETECTED));
- tw.setRecursive(true);
-
- //Don't waste time trying to add iterators that already exist
- HashSet<FileTreeIterator> toAdd = new HashSet<FileTreeIterator>();
- while (tw.next()) {
- FileTreeIterator t = tw.getTree(0, FileTreeIterator.class);
- if (t.hasGitIgnore()) {
- toAdd.add(t);
- //TODO: Account for and test the removal of .gitignore files
- }
- }
- for (FileTreeIterator t : toAdd)
- addNodeFromTree(t);
-
- //The base is special
- //TODO: Test alternate locations for GIT_DIR
- readRulesAtBase();
- }
-
- /**
- * Creates rules for .git/info/exclude and .gitignore to the base node.
- * It will overwrite the existing base ignore node. There will always
- * be a base ignore node, even if there is no .gitignore file
- */
- private void readRulesAtBase() {
- //Add .gitignore rules
- String path = new File(repository.getWorkDir(), ".gitignore").getAbsolutePath();
- File f = new File(path);
- IgnoreNode n = new IgnoreNode(f.getParentFile());
-
- //Add exclude rules
- //TODO: Get /info directory without string concat
- path = new File(repository.getWorkDir(), ".git/info/exclude").getAbsolutePath();
- f = new File(path);
- if (f.canRead())
- n.addSecondarySource(f);
-
- ignoreMap.put("", n);
- }
-
- /**
- * Adds a node located at the FileTreeIterator's root directory.
- * <br>
- * Will check for the presence of a .gitignore using {@link FileTreeIterator#hasGitIgnore()}.
- * If no .gitignore file exists, nothing will be done.
- * <br>
- * Will check the last time of modification using {@link FileTreeIterator#hasGitIgnore()}.
- * If a node already exists and the time stamp has not changed, do nothing.
- * <br>
- * Note: This can be extended later if necessary to AbstractTreeIterator by using
- * byte[] path instead of File directory.
- *
- * @param t
- * AbstractTreeIterator to check for ignore info. The name of the node
- * should be .gitignore
- */
- protected void addNodeFromTree(FileTreeIterator t) {
- IgnoreNode n = ignoreMap.get(relativize(t.getDirectory()));
- long time = t.getGitIgnoreLastModified();
- if (n != null) {
- if (n.getLastModified() == time)
- //TODO: Test and optimize
- return;
- }
- n = addIgnoreNode(t.getDirectory());
- n.setLastModified(time);
- }
-
- /**
- * Maps the directory to an IgnoreNode, but does not initialize
- * the IgnoreNode. If a node already exists it will be emptied. Empty nodes
- * will be initialized when needed, see {@link #isIgnored(String)}
- *
- * @param dir
- * directory to load rules from
- * @return
- * true if set successfully, false if directory does not exist
- * or if directory does not contain a .gitignore file.
- */
- protected IgnoreNode addIgnoreNode(File dir) {
- String relativeDir = relativize(dir);
- IgnoreNode n = ignoreMap.get(relativeDir);
- if (n != null)
- n.clear();
- else {
- n = new IgnoreNode(dir);
- ignoreMap.put(relativeDir, n);
- }
- return n;
- }
-
- /**
- * Returns the ignored status of the file based on the current state
- * of the ignore nodes. Ignore nodes will not be updated and new ignore
- * nodes will not be created.
- * <br><br>
- * Traverses from highest to lowest priority and quits as soon as a match
- * is made. If no match is made anywhere, the file is assumed
- * to be not ignored.
- *
- * @param file
- * Path string relative to Repository.getWorkDir();
- * @return true
- * True if file is ignored, false if the file matches a negation statement
- * or if there are no rules pertaining to the file.
- * @throws IOException
- * Failed to check ignore status
- */
- public boolean isIgnored(String file) throws IOException{
- String currentPriority = file;
-
- boolean ignored = false;
- String target = rootFileURI.getPath() + file;
- while (currentPriority.length() > 1) {
- currentPriority = getParent(currentPriority);
- IgnoreNode n = ignoreMap.get(currentPriority);
-
- if (n != null) {
- ignored = n.isIgnored(target);
-
- if (n.wasMatched()) {
- if (ignored)
- return ignored;
- else
- target = getParent(target);
- }
- }
- }
-
- return false;
- }
-
- /**
- * String manipulation to get the parent directory of the given path.
- * It may be more efficient to make a file and call File.getParent().
- * This function is only called in {@link #initialize}
- *
- * @param filePath
- * Will seek parent directory for this path. Returns empty string
- * if the filePath does not contain a File.separator
- * @return
- * Parent of the filePath, or blank string if non-existent
- */
- private String getParent(String filePath) {
- int lastSlash = filePath.lastIndexOf("/");
- if (filePath.length() > 0 && lastSlash != -1)
- return filePath.substring(0, lastSlash);
- else
- //This line should be unreachable with the current partiallyInitialize
- return "";
- }
-
- /**
- * @param relativePath
- * Directory to find rules for, should be relative to the repository root
- * @return
- * Ignore rules for given base directory, contained in an IgnoreNode
- */
- public IgnoreNode getRules(String relativePath) {
- return ignoreMap.get(relativePath);
- }
-
- /**
- * @return
- * True if there are no ignore rules in this cache
- */
- public boolean isEmpty() {
- return ignoreMap.isEmpty();
- }
-
- /**
- * Clears the cache
- */
- public void clear() {
- ignoreMap.clear();
- }
-
- /**
- * Returns the relative path versus the repository root.
- *
- * @param directory
- * Directory to find relative path for.
- * @return
- * Relative path versus the repository root. This function will
- * strip the last trailing "/" from its return string
- */
- private String relativize(File directory) {
- String retVal = rootFileURI.relativize(directory.toURI()).getPath();
- if (retVal.endsWith("/"))
- retVal = retVal.substring(0, retVal.length() - 1);
- return retVal;
- }
-
-}
diff --git a/org.eclipse.jgit/src/org/eclipse/jgit/lib/AlternateRepositoryDatabase.java b/org.eclipse.jgit/src/org/eclipse/jgit/lib/AlternateRepositoryDatabase.java
deleted file mode 100644
index 40f1106..0000000
--- a/org.eclipse.jgit/src/org/eclipse/jgit/lib/AlternateRepositoryDatabase.java
+++ /dev/null
@@ -1,142 +0,0 @@
-/*
- * Copyright (C) 2010, Constantine Plotnikov <constantine.plotnikov@gmail.com>
- * Copyright (C) 2009, Google Inc.
- * and other copyright owners as documented in the project's IP log.
- *
- * This program and the accompanying materials are made available
- * under the terms of the Eclipse Distribution License v1.0 which
- * accompanies this distribution, is reproduced below, and is
- * available at http://www.eclipse.org/org/documents/edl-v10.php
- *
- * All rights reserved.
- *
- * Redistribution and use in source and binary forms, with or
- * without modification, are permitted provided that the following
- * conditions are met:
- *
- * - Redistributions of source code must retain the above copyright
- * notice, this list of conditions and the following disclaimer.
- *
- * - Redistributions in binary form must reproduce the above
- * copyright notice, this list of conditions and the following
- * disclaimer in the documentation and/or other materials provided
- * with the distribution.
- *
- * - Neither the name of the Eclipse Foundation, Inc. nor the
- * names of its contributors may be used to endorse or promote
- * products derived from this software without specific prior
- * written permission.
- *
- * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND
- * CONTRIBUTORS "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES,
- * INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
- * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
- * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR
- * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
- * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT
- * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
- * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER
- * CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT,
- * STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
- * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF
- * ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
- */
-
-package org.eclipse.jgit.lib;
-
-import java.io.IOException;
-import java.util.Collection;
-
-/**
- * An ObjectDatabase of another {@link Repository}.
- * <p>
- * This {@code ObjectDatabase} wraps around another {@code Repository}'s object
- * database, providing its contents to the caller, and closing the Repository
- * when this database is closed. The primary user of this class is
- * {@link ObjectDirectory}, when the {@code info/alternates} file points at the
- * {@code objects/} directory of another repository.
- */
-public final class AlternateRepositoryDatabase extends ObjectDatabase {
- private final Repository repository;
-
- private final ObjectDatabase odb;
-
- /**
- * @param alt
- * the alternate repository to wrap and export.
- */
- public AlternateRepositoryDatabase(final Repository alt) {
- repository = alt;
- odb = repository.getObjectDatabase();
- }
-
- /** @return the alternate repository objects are borrowed from. */
- public Repository getRepository() {
- return repository;
- }
-
- @Override
- public void closeSelf() {
- repository.close();
- }
-
- @Override
- public void create() throws IOException {
- repository.create();
- }
-
- @Override
- public boolean exists() {
- return odb.exists();
- }
-
- @Override
- protected boolean hasObject1(final AnyObjectId objectId) {
- return odb.hasObject1(objectId);
- }
-
- @Override
- protected boolean tryAgain1() {
- return odb.tryAgain1();
- }
-
- @Override
- protected boolean hasObject2(final String objectName) {
- return odb.hasObject2(objectName);
- }
-
- @Override
- protected ObjectLoader openObject1(final WindowCursor curs,
- final AnyObjectId objectId) throws IOException {
- return odb.openObject1(curs, objectId);
- }
-
- @Override
- protected ObjectLoader openObject2(final WindowCursor curs,
- final String objectName, final AnyObjectId objectId)
- throws IOException {
- return odb.openObject2(curs, objectName, objectId);
- }
-
- @Override
- void openObjectInAllPacks1(final Collection<PackedObjectLoader> out,
- final WindowCursor curs, final AnyObjectId objectId)
- throws IOException {
- odb.openObjectInAllPacks1(out, curs, objectId);
- }
-
- @Override
- protected ObjectDatabase[] loadAlternates() throws IOException {
- return odb.getAlternates();
- }
-
- @Override
- protected void closeAlternates(final ObjectDatabase[] alt) {
- // Do nothing; these belong to odb to close, not us.
- }
-
- @Override
- public ObjectDatabase newCachedDatabase() {
- return odb.newCachedDatabase();
- }
-}
diff --git a/org.eclipse.jgit/src/org/eclipse/jgit/lib/AnyObjectId.java b/org.eclipse.jgit/src/org/eclipse/jgit/lib/AnyObjectId.java
index 7d08f3d..ecaa82b 100644
--- a/org.eclipse.jgit/src/org/eclipse/jgit/lib/AnyObjectId.java
+++ b/org.eclipse.jgit/src/org/eclipse/jgit/lib/AnyObjectId.java
@@ -113,7 +113,7 @@ public final int getFirstByte() {
* @return < 0 if this id comes before other; 0 if this id is equal to
* other; > 0 if this id comes after other.
*/
- public int compareTo(final ObjectId other) {
+ public int compareTo(final AnyObjectId other) {
if (this == other)
return 0;
@@ -139,7 +139,7 @@ public int compareTo(final ObjectId other) {
}
public int compareTo(final Object other) {
- return compareTo(((ObjectId) other));
+ return compareTo(((AnyObjectId) other));
}
/**
diff --git a/org.eclipse.jgit/src/org/eclipse/jgit/lib/BaseRepositoryBuilder.java b/org.eclipse.jgit/src/org/eclipse/jgit/lib/BaseRepositoryBuilder.java
new file mode 100644
index 0000000..410c85f
--- /dev/null
+++ b/org.eclipse.jgit/src/org/eclipse/jgit/lib/BaseRepositoryBuilder.java
@@ -0,0 +1,616 @@
+package org.eclipse.jgit.lib;
+
+import static org.eclipse.jgit.lib.ConfigConstants.CONFIG_CORE_SECTION;
+import static org.eclipse.jgit.lib.ConfigConstants.CONFIG_KEY_BARE;
+import static org.eclipse.jgit.lib.ConfigConstants.CONFIG_KEY_WORKTREE;
+import static org.eclipse.jgit.lib.Constants.DOT_GIT;
+import static org.eclipse.jgit.lib.Constants.GIT_ALTERNATE_OBJECT_DIRECTORIES_KEY;
+import static org.eclipse.jgit.lib.Constants.GIT_CEILING_DIRECTORIES_KEY;
+import static org.eclipse.jgit.lib.Constants.GIT_DIR_KEY;
+import static org.eclipse.jgit.lib.Constants.GIT_INDEX_KEY;
+import static org.eclipse.jgit.lib.Constants.GIT_OBJECT_DIRECTORY_KEY;
+import static org.eclipse.jgit.lib.Constants.GIT_WORK_TREE_KEY;
+
+import java.io.File;
+import java.io.IOException;
+import java.text.MessageFormat;
+import java.util.Collection;
+import java.util.LinkedList;
+import java.util.List;
+
+import org.eclipse.jgit.JGitText;
+import org.eclipse.jgit.errors.ConfigInvalidException;
+import org.eclipse.jgit.lib.RepositoryCache.FileKey;
+import org.eclipse.jgit.storage.file.FileBasedConfig;
+import org.eclipse.jgit.storage.file.FileRepository;
+import org.eclipse.jgit.storage.file.FileRepositoryBuilder;
+import org.eclipse.jgit.util.FS;
+import org.eclipse.jgit.util.SystemReader;
+
+/**
+ * Base builder to customize repository construction.
+ * <p>
+ * Repository implementations may subclass this builder in order to add custom
+ * repository detection methods.
+ *
+ * @param <B>
+ * type of the repository builder.
+ * @param <R>
+ * type of the repository that is constructed.
+ * @see RepositoryBuilder
+ * @see FileRepositoryBuilder
+ */
+public class BaseRepositoryBuilder<B extends BaseRepositoryBuilder, R extends Repository> {
+ private FS fs;
+
+ private File gitDir;
+
+ private File objectDirectory;
+
+ private List<File> alternateObjectDirectories;
+
+ private File indexFile;
+
+ private File workTree;
+
+ /** Directories limiting the search for a Git repository. */
+ private List<File> ceilingDirectories;
+
+ /** True only if the caller wants to force bare behavior. */
+ private boolean bare;
+
+ /** Configuration file of target repository, lazily loaded if required. */
+ private Config config;
+
+ /**
+ * Set the file system abstraction needed by this repository.
+ *
+ * @param fs
+ * the abstraction.
+ * @return {@code this} (for chaining calls).
+ */
+ public B setFS(FS fs) {
+ this.fs = fs;
+ return self();
+ }
+
+ /** @return the file system abstraction, or null if not set. */
+ public FS getFS() {
+ return fs;
+ }
+
+ /**
+ * Set the Git directory storing the repository metadata.
+ * <p>
+ * The meta directory stores the objects, references, and meta files like
+ * {@code MERGE_HEAD}, or the index file. If {@code null} the path is
+ * assumed to be {@code workTree/.git}.
+ *
+ * @param gitDir
+ * {@code GIT_DIR}, the repository meta directory.
+ * @return {@code this} (for chaining calls).
+ */
+ public B setGitDir(File gitDir) {
+ this.gitDir = gitDir;
+ this.config = null;
+ return self();
+ }
+
+ /** @return the meta data directory; null if not set. */
+ public File getGitDir() {
+ return gitDir;
+ }
+
+ /**
+ * Set the directory storing the repository's objects.
+ *
+ * @param objectDirectory
+ * {@code GIT_OBJECT_DIRECTORY}, the directory where the
+ * repository's object files are stored.
+ * @return {@code this} (for chaining calls).
+ */
+ public B setObjectDirectory(File objectDirectory) {
+ this.objectDirectory = objectDirectory;
+ return self();
+ }
+
+ /** @return the object directory; null if not set. */
+ public File getObjectDirectory() {
+ return objectDirectory;
+ }
+
+ /**
+ * Add an alternate object directory to the search list.
+ * <p>
+ * This setting handles one alternate directory at a time, and is provided
+ * to support {@code GIT_ALTERNATE_OBJECT_DIRECTORIES}.
+ *
+ * @param other
+ * another objects directory to search after the standard one.
+ * @return {@code this} (for chaining calls).
+ */
+ public B addAlternateObjectDirectory(File other) {
+ if (other != null) {
+ if (alternateObjectDirectories == null)
+ alternateObjectDirectories = new LinkedList<File>();
+ alternateObjectDirectories.add(other);
+ }
+ return self();
+ }
+
+ /**
+ * Add alternate object directories to the search list.
+ * <p>
+ * This setting handles several alternate directories at once, and is
+ * provided to support {@code GIT_ALTERNATE_OBJECT_DIRECTORIES}.
+ *
+ * @param inList
+ * other object directories to search after the standard one. The
+ * collection's contents is copied to an internal list.
+ * @return {@code this} (for chaining calls).
+ */
+ public B addAlternateObjectDirectories(Collection<File> inList) {
+ if (inList != null) {
+ for (File path : inList)
+ addAlternateObjectDirectory(path);
+ }
+ return self();
+ }
+
+ /**
+ * Add alternate object directories to the search list.
+ * <p>
+ * This setting handles several alternate directories at once, and is
+ * provided to support {@code GIT_ALTERNATE_OBJECT_DIRECTORIES}.
+ *
+ * @param inList
+ * other object directories to search after the standard one. The
+ * array's contents is copied to an internal list.
+ * @return {@code this} (for chaining calls).
+ */
+ public B addAlternateObjectDirectories(File[] inList) {
+ if (inList != null) {
+ for (File path : inList)
+ addAlternateObjectDirectory(path);
+ }
+ return self();
+ }
+
+ /** @return ordered array of alternate directories; null if non were set. */
+ public File[] getAlternateObjectDirectories() {
+ final List<File> alts = alternateObjectDirectories;
+ if (alts == null)
+ return null;
+ return alts.toArray(new File[alts.size()]);
+ }
+
+ /**
+ * Force the repository to be treated as bare (have no working directory).
+ * <p>
+ * If bare the working directory aspects of the repository won't be
+ * configured, and will not be accessible.
+ *
+ * @return {@code this} (for chaining calls).
+ */
+ public B setBare() {
+ setIndexFile(null);
+ setWorkTree(null);
+ bare = true;
+ return self();
+ }
+
+ /** @return true if this repository was forced bare by {@link #setBare()}. */
+ public boolean isBare() {
+ return bare;
+ }
+
+ /**
+ * Set the top level directory of the working files.
+ *
+ * @param workTree
+ * {@code GIT_WORK_TREE}, the working directory of the checkout.
+ * @return {@code this} (for chaining calls).
+ */
+ public B setWorkTree(File workTree) {
+ this.workTree = workTree;
+ return self();
+ }
+
+ /** @return the work tree directory, or null if not set. */
+ public File getWorkTree() {
+ return workTree;
+ }
+
+ /**
+ * Set the local index file that is caching checked out file status.
+ * <p>
+ * The location of the index file tracking the status information for each
+ * checked out file in {@code workTree}. This may be null to assume the
+ * default {@code gitDiir/index}.
+ *
+ * @param indexFile
+ * {@code GIT_INDEX_FILE}, the index file location.
+ * @return {@code this} (for chaining calls).
+ */
+ public B setIndexFile(File indexFile) {
+ this.indexFile = indexFile;
+ return self();
+ }
+
+ /** @return the index file location, or null if not set. */
+ public File getIndexFile() {
+ return indexFile;
+ }
+
+ /**
+ * Read standard Git environment variables and configure from those.
+ * <p>
+ * This method tries to read the standard Git environment variables, such as
+ * {@code GIT_DIR} and {@code GIT_WORK_TREE} to configure this builder
+ * instance. If an environment variable is set, it overrides the value
+ * already set in this builder.
+ *
+ * @return {@code this} (for chaining calls).
+ */
+ public B readEnvironment() {
+ return readEnvironment(SystemReader.getInstance());
+ }
+
+ /**
+ * Read standard Git environment variables and configure from those.
+ * <p>
+ * This method tries to read the standard Git environment variables, such as
+ * {@code GIT_DIR} and {@code GIT_WORK_TREE} to configure this builder
+ * instance. If a property is already set in the builder, the environment
+ * variable is not used.
+ *
+ * @param sr
+ * the SystemReader abstraction to access the environment.
+ * @return {@code this} (for chaining calls).
+ */
+ public B readEnvironment(SystemReader sr) {
+ if (getGitDir() == null) {
+ String val = sr.getenv(GIT_DIR_KEY);
+ if (val != null)
+ setGitDir(new File(val));
+ }
+
+ if (getObjectDirectory() == null) {
+ String val = sr.getenv(GIT_OBJECT_DIRECTORY_KEY);
+ if (val != null)
+ setObjectDirectory(new File(val));
+ }
+
+ if (getAlternateObjectDirectories() == null) {
+ String val = sr.getenv(GIT_ALTERNATE_OBJECT_DIRECTORIES_KEY);
+ if (val != null) {
+ for (String path : val.split(File.pathSeparator))
+ addAlternateObjectDirectory(new File(path));
+ }
+ }
+
+ if (getWorkTree() == null) {
+ String val = sr.getenv(GIT_WORK_TREE_KEY);
+ if (val != null)
+ setWorkTree(new File(val));
+ }
+
+ if (getIndexFile() == null) {
+ String val = sr.getenv(GIT_INDEX_KEY);
+ if (val != null)
+ setIndexFile(new File(val));
+ }
+
+ if (ceilingDirectories == null) {
+ String val = sr.getenv(GIT_CEILING_DIRECTORIES_KEY);
+ if (val != null) {
+ for (String path : val.split(File.pathSeparator))
+ addCeilingDirectory(new File(path));
+ }
+ }
+
+ return self();
+ }
+
+ /**
+ * Add a ceiling directory to the search limit list.
+ * <p>
+ * This setting handles one ceiling directory at a time, and is provided to
+ * support {@code GIT_CEILING_DIRECTORIES}.
+ *
+ * @param root
+ * a path to stop searching at; its parent will not be searched.
+ * @return {@code this} (for chaining calls).
+ */
+ public B addCeilingDirectory(File root) {
+ if (root != null) {
+ if (ceilingDirectories == null)
+ ceilingDirectories = new LinkedList<File>();
+ ceilingDirectories.add(root);
+ }
+ return self();
+ }
+
+ /**
+ * Add ceiling directories to the search list.
+ * <p>
+ * This setting handles several ceiling directories at once, and is provided
+ * to support {@code GIT_CEILING_DIRECTORIES}.
+ *
+ * @param inList
+ * directory paths to stop searching at. The collection's
+ * contents is copied to an internal list.
+ * @return {@code this} (for chaining calls).
+ */
+ public B addCeilingDirectories(Collection<File> inList) {
+ if (inList != null) {
+ for (File path : inList)
+ addCeilingDirectory(path);
+ }
+ return self();
+ }
+
+ /**
+ * Add ceiling directories to the search list.
+ * <p>
+ * This setting handles several ceiling directories at once, and is provided
+ * to support {@code GIT_CEILING_DIRECTORIES}.
+ *
+ * @param inList
+ * directory paths to stop searching at. The array's contents is
+ * copied to an internal list.
+ * @return {@code this} (for chaining calls).
+ */
+ public B addCeilingDirectories(File[] inList) {
+ if (inList != null) {
+ for (File path : inList)
+ addCeilingDirectory(path);
+ }
+ return self();
+ }
+
+ /**
+ * Configure {@code GIT_DIR} by searching up the file system.
+ * <p>
+ * Starts from the current working directory of the JVM and scans up through
+ * the directory tree until a Git repository is found. Success can be
+ * determined by checking for {@code getGitDir() != null}.
+ * <p>
+ * The search can be limited to specific spaces of the local filesystem by
+ * {@link #addCeilingDirectory(File)}, or inheriting the list through a
+ * prior call to {@link #readEnvironment()}.
+ *
+ * @return {@code this} (for chaining calls).
+ */
+ public B findGitDir() {
+ if (getGitDir() == null)
+ findGitDir(new File("").getAbsoluteFile());
+ return self();
+ }
+
+ /**
+ * Configure {@code GIT_DIR} by searching up the file system.
+ * <p>
+ * Starts from the supplied directory path and scans up through the parent
+ * directory tree until a Git repository is found. Success can be determined
+ * by checking for {@code getGitDir() != null}.
+ * <p>
+ * The search can be limited to specific spaces of the local filesystem by
+ * {@link #addCeilingDirectory(File)}, or inheriting the list through a
+ * prior call to {@link #readEnvironment()}.
+ *
+ * @param current
+ * directory to begin searching in.
+ * @return {@code this} (for chaining calls).
+ */
+ public B findGitDir(File current) {
+ if (getGitDir() == null) {
+ FS tryFS = safeFS();
+ while (current != null) {
+ File dir = new File(current, DOT_GIT);
+ if (FileKey.isGitRepository(dir, tryFS)) {
+ setGitDir(dir);
+ break;
+ }
+
+ current = current.getParentFile();
+ if (current != null && ceilingDirectories.contains(current))
+ break;
+ }
+ }
+ return self();
+ }
+
+ /**
+ * Guess and populate all parameters not already defined.
+ * <p>
+ * If an option was not set, the setup method will try to default the option
+ * based on other options. If insufficient information is available, an
+ * exception is thrown to the caller.
+ *
+ * @return {@code this}
+ * @throws IllegalArgumentException
+ * insufficient parameters were set, or some parameters are
+ * incompatible with one another.
+ * @throws IOException
+ * the repository could not be accessed to configure the rest of
+ * the builder's parameters.
+ */
+ public B setup() throws IllegalArgumentException, IOException {
+ requireGitDirOrWorkTree();
+ setupGitDir();
+ setupWorkTree();
+ setupInternals();
+ return self();
+ }
+
+ /**
+ * Create a repository matching the configuration in this builder.
+ * <p>
+ * If an option was not set, the build method will try to default the option
+ * based on other options. If insufficient information is available, an
+ * exception is thrown to the caller.
+ *
+ * @return a repository matching this configuration.
+ * @throws IllegalArgumentException
+ * insufficient parameters were set.
+ * @throws IOException
+ * the repository could not be accessed to configure the rest of
+ * the builder's parameters.
+ */
+ @SuppressWarnings("unchecked")
+ public R build() throws IOException {
+ return (R) new FileRepository(setup());
+ }
+
+ /** Require either {@code gitDir} or {@code workTree} to be set. */
+ protected void requireGitDirOrWorkTree() {
+ if (getGitDir() == null && getWorkTree() == null)
+ throw new IllegalArgumentException(
+ JGitText.get().eitherGitDirOrWorkTreeRequired);
+ }
+
+ /**
+ * Perform standard gitDir initialization.
+ *
+ * @throws IOException
+ * the repository could not be accessed
+ */
+ protected void setupGitDir() throws IOException {
+ // No gitDir? Try to assume its under the workTree.
+ //
+ if (getGitDir() == null && getWorkTree() != null)
+ setGitDir(new File(getWorkTree(), DOT_GIT));
+ }
+
+ /**
+ * Perform standard work-tree initialization.
+ * <p>
+ * This is a method typically invoked inside of {@link #setup()}, near the
+ * end after the repository has been identified and its configuration is
+ * available for inspection.
+ *
+ * @throws IOException
+ * the repository configuration could not be read.
+ */
+ protected void setupWorkTree() throws IOException {
+ if (getFS() == null)
+ setFS(FS.DETECTED);
+
+ // If we aren't bare, we should have a work tree.
+ //
+ if (!isBare() && getWorkTree() == null)
+ setWorkTree(guessWorkTreeOrFail());
+
+ if (!isBare()) {
+ // If after guessing we're still not bare, we must have
+ // a metadata directory to hold the repository. Assume
+ // its at the work tree.
+ //
+ if (getGitDir() == null)
+ setGitDir(getWorkTree().getParentFile());
+ if (getIndexFile() == null)
+ setIndexFile(new File(getGitDir(), "index"));
+ }
+ }
+
+ /**
+ * Configure the internal implementation details of the repository.
+ *
+ * @throws IOException
+ * the repository could not be accessed
+ */
+ protected void setupInternals() throws IOException {
+ if (getObjectDirectory() == null && getGitDir() != null)
+ setObjectDirectory(safeFS().resolve(getGitDir(), "objects"));
+ }
+
+ /**
+ * Get the cached repository configuration, loading if not yet available.
+ *
+ * @return the configuration of the repository.
+ * @throws IOException
+ * the configuration is not available, or is badly formed.
+ */
+ protected Config getConfig() throws IOException {
+ if (config == null)
+ config = loadConfig();
+ return config;
+ }
+
+ /**
+ * Parse and load the repository specific configuration.
+ * <p>
+ * The default implementation reads {@code gitDir/config}, or returns an
+ * empty configuration if gitDir was not set.
+ *
+ * @return the repository's configuration.
+ * @throws IOException
+ * the configuration is not available.
+ */
+ protected Config loadConfig() throws IOException {
+ if (getGitDir() != null) {
+ // We only want the repository's configuration file, and not
+ // the user file, as these parameters must be unique to this
+ // repository and not inherited from other files.
+ //
+ File path = safeFS().resolve(getGitDir(), "config");
+ FileBasedConfig cfg = new FileBasedConfig(path, safeFS());
+ 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/BinaryDelta.java b/org.eclipse.jgit/src/org/eclipse/jgit/lib/BinaryDelta.java
deleted file mode 100644
index a59b335..0000000
--- a/org.eclipse.jgit/src/org/eclipse/jgit/lib/BinaryDelta.java
+++ /dev/null
@@ -1,143 +0,0 @@
-/*
- * Copyright (C) 2007, Robin Rosenberg <robin.rosenberg@dewire.com>
- * Copyright (C) 2006-2007, Shawn O. Pearce <spearce@spearce.org>
- * and other copyright owners as documented in the project's IP log.
- *
- * This program and the accompanying materials are made available
- * under the terms of the Eclipse Distribution License v1.0 which
- * accompanies this distribution, is reproduced below, and is
- * available at http://www.eclipse.org/org/documents/edl-v10.php
- *
- * All rights reserved.
- *
- * Redistribution and use in source and binary forms, with or
- * without modification, are permitted provided that the following
- * conditions are met:
- *
- * - Redistributions of source code must retain the above copyright
- * notice, this list of conditions and the following disclaimer.
- *
- * - Redistributions in binary form must reproduce the above
- * copyright notice, this list of conditions and the following
- * disclaimer in the documentation and/or other materials provided
- * with the distribution.
- *
- * - Neither the name of the Eclipse Foundation, Inc. nor the
- * names of its contributors may be used to endorse or promote
- * products derived from this software without specific prior
- * written permission.
- *
- * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND
- * CONTRIBUTORS "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES,
- * INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
- * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
- * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR
- * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
- * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT
- * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
- * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER
- * CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT,
- * STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
- * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF
- * ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
- */
-
-package org.eclipse.jgit.lib;
-
-import org.eclipse.jgit.JGitText;
-
-/**
- * Recreate a stream from a base stream and a GIT pack delta.
- * <p>
- * This entire class is heavily cribbed from <code>patch-delta.c</code> in the
- * GIT project. The original delta patching code was written by Nicolas Pitre
- * (<nico@cam.org>).
- * </p>
- */
-public class BinaryDelta {
-
- /**
- * Apply the changes defined by delta to the data in base, yielding a new
- * array of bytes.
- *
- * @param base
- * some byte representing an object of some kind.
- * @param delta
- * a git pack delta defining the transform from one version to
- * another.
- * @return patched base
- */
- public static final byte[] apply(final byte[] base, final byte[] delta) {
- int deltaPtr = 0;
-
- // Length of the base object (a variable length int).
- //
- int baseLen = 0;
- int c, shift = 0;
- do {
- c = delta[deltaPtr++] & 0xff;
- baseLen |= (c & 0x7f) << shift;
- shift += 7;
- } while ((c & 0x80) != 0);
- if (base.length != baseLen)
- throw new IllegalArgumentException(JGitText.get().baseLengthIncorrect);
-
- // Length of the resulting object (a variable length int).
- //
- int resLen = 0;
- shift = 0;
- do {
- c = delta[deltaPtr++] & 0xff;
- resLen |= (c & 0x7f) << shift;
- shift += 7;
- } while ((c & 0x80) != 0);
-
- final byte[] result = new byte[resLen];
- int resultPtr = 0;
- while (deltaPtr < delta.length) {
- final int cmd = delta[deltaPtr++] & 0xff;
- if ((cmd & 0x80) != 0) {
- // Determine the segment of the base which should
- // be copied into the output. The segment is given
- // as an offset and a length.
- //
- int copyOffset = 0;
- if ((cmd & 0x01) != 0)
- copyOffset = delta[deltaPtr++] & 0xff;
- if ((cmd & 0x02) != 0)
- copyOffset |= (delta[deltaPtr++] & 0xff) << 8;
- if ((cmd & 0x04) != 0)
- copyOffset |= (delta[deltaPtr++] & 0xff) << 16;
- if ((cmd & 0x08) != 0)
- copyOffset |= (delta[deltaPtr++] & 0xff) << 24;
-
- int copySize = 0;
- if ((cmd & 0x10) != 0)
- copySize = delta[deltaPtr++] & 0xff;
- if ((cmd & 0x20) != 0)
- copySize |= (delta[deltaPtr++] & 0xff) << 8;
- if ((cmd & 0x40) != 0)
- copySize |= (delta[deltaPtr++] & 0xff) << 16;
- if (copySize == 0)
- copySize = 0x10000;
-
- System.arraycopy(base, copyOffset, result, resultPtr, copySize);
- resultPtr += copySize;
- } else if (cmd != 0) {
- // Anything else the data is literal within the delta
- // itself.
- //
- System.arraycopy(delta, deltaPtr, result, resultPtr, cmd);
- deltaPtr += cmd;
- resultPtr += cmd;
- } else {
- // cmd == 0 has been reserved for future encoding but
- // for now its not acceptable.
- //
- throw new IllegalArgumentException(JGitText.get().unsupportedCommand0);
- }
- }
-
- return result;
- }
-}
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 b05942b..b56966f 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 BlobBasedConfig(Config base, final byte[] blob)
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 BlobBasedConfig(Config base, final Commit commit, final String path)
if (tree == null)
throw new FileNotFoundException(MessageFormat.format(JGitText.get().entryNotFoundByPath, path));
final ObjectId blobId = tree.getObjectId(0);
- final ObjectLoader loader = tree.getRepository().openBlob(blobId);
- if (loader == null)
- throw new IOException(MessageFormat.format(JGitText.get().blobNotFoundForPath
- , blobId, path));
- fromText(RawParseUtils.decode(loader.getBytes()));
+ ObjectLoader loader = r.open(blobId,Constants.OBJ_BLOB);
+ fromText(RawParseUtils.decode(loader.getCachedBytes()));
}
}
diff --git a/org.eclipse.jgit/src/org/eclipse/jgit/lib/CachedObjectDatabase.java b/org.eclipse.jgit/src/org/eclipse/jgit/lib/CachedObjectDatabase.java
deleted file mode 100644
index 3dcea16..0000000
--- a/org.eclipse.jgit/src/org/eclipse/jgit/lib/CachedObjectDatabase.java
+++ /dev/null
@@ -1,132 +0,0 @@
-/*
- * Copyright (C) 2010, Constantine Plotnikov <constantine.plotnikov@gmail.com>
- * Copyright (C) 2010, JetBrains s.r.o.
- * and other copyright owners as documented in the project's IP log.
- *
- * This program and the accompanying materials are made available
- * under the terms of the Eclipse Distribution License v1.0 which
- * accompanies this distribution, is reproduced below, and is
- * available at http://www.eclipse.org/org/documents/edl-v10.php
- *
- * All rights reserved.
- *
- * Redistribution and use in source and binary forms, with or
- * without modification, are permitted provided that the following
- * conditions are met:
- *
- * - Redistributions of source code must retain the above copyright
- * notice, this list of conditions and the following disclaimer.
- *
- * - Redistributions in binary form must reproduce the above
- * copyright notice, this list of conditions and the following
- * disclaimer in the documentation and/or other materials provided
- * with the distribution.
- *
- * - Neither the name of the Eclipse Foundation, Inc. nor the
- * names of its contributors may be used to endorse or promote
- * products derived from this software without specific prior
- * written permission.
- *
- * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND
- * CONTRIBUTORS "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES,
- * INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
- * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
- * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR
- * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
- * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT
- * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
- * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER
- * CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT,
- * STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
- * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF
- * ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
- */
-
-package org.eclipse.jgit.lib;
-
-import java.io.IOException;
-import java.util.Collection;
-
-/**
- * {@link ObjectDatabase} wrapper providing temporary lookup caching.
- * <p>
- * The base class for {@code ObjectDatabase}s that wrap other database instances
- * and optimize querying for objects by caching some database dependent
- * information. Instances of this class (or any of its subclasses) can be
- * returned from the method {@link ObjectDatabase#newCachedDatabase()}. This
- * class can be used in scenarios where the database does not change, or when
- * changes in the database while some operation is in progress is an acceptable
- * risk.
- * <p>
- * The default implementation delegates all requests to the wrapped database.
- * The instance might be indirectly invalidated if the wrapped instance is
- * closed. Closing the delegating instance does not implies closing the wrapped
- * instance. For alternative databases, cached instances are used as well.
- */
-public class CachedObjectDatabase extends ObjectDatabase {
- /**
- * The wrapped database instance
- */
- protected final ObjectDatabase wrapped;
-
- /**
- * Create the delegating database instance
- *
- * @param wrapped
- * the wrapped object database
- */
- public CachedObjectDatabase(ObjectDatabase wrapped) {
- this.wrapped = wrapped;
- }
-
- @Override
- protected boolean hasObject1(AnyObjectId objectId) {
- return wrapped.hasObject1(objectId);
- }
-
- @Override
- protected ObjectLoader openObject1(WindowCursor curs, AnyObjectId objectId)
- throws IOException {
- return wrapped.openObject1(curs, objectId);
- }
-
- @Override
- protected boolean hasObject2(String objectName) {
- return wrapped.hasObject2(objectName);
- }
-
- @Override
- protected ObjectDatabase[] loadAlternates() throws IOException {
- ObjectDatabase[] loaded = wrapped.getAlternates();
- ObjectDatabase[] result = new ObjectDatabase[loaded.length];
- for (int i = 0; i < loaded.length; i++) {
- result[i] = loaded[i].newCachedDatabase();
- }
- return result;
- }
-
- @Override
- protected ObjectLoader openObject2(WindowCursor curs, String objectName,
- AnyObjectId objectId) throws IOException {
- return wrapped.openObject2(curs, objectName, objectId);
- }
-
- @Override
- void openObjectInAllPacks1(Collection<PackedObjectLoader> out,
- WindowCursor curs, AnyObjectId objectId) throws IOException {
- wrapped.openObjectInAllPacks1(out, curs, objectId);
- }
-
- @Override
- protected boolean tryAgain1() {
- return wrapped.tryAgain1();
- }
-
- @Override
- public ObjectDatabase newCachedDatabase() {
- // Note that "this" is not returned since subclasses might actually do something,
- // on closeSelf() (for example closing database connections or open repositories).
- // The situation might become even more tricky if we will consider alternates.
- return wrapped.newCachedDatabase();
- }
-}
diff --git a/org.eclipse.jgit/src/org/eclipse/jgit/lib/CachedObjectDirectory.java b/org.eclipse.jgit/src/org/eclipse/jgit/lib/CachedObjectDirectory.java
deleted file mode 100644
index 3724f84..0000000
--- a/org.eclipse.jgit/src/org/eclipse/jgit/lib/CachedObjectDirectory.java
+++ /dev/null
@@ -1,112 +0,0 @@
-/*
- * Copyright (C) 2010, Constantine Plotnikov <constantine.plotnikov@gmail.com>
- * Copyright (C) 2010, JetBrains s.r.o.
- * and other copyright owners as documented in the project's IP log.
- *
- * This program and the accompanying materials are made available
- * under the terms of the Eclipse Distribution License v1.0 which
- * accompanies this distribution, is reproduced below, and is
- * available at http://www.eclipse.org/org/documents/edl-v10.php
- *
- * All rights reserved.
- *
- * Redistribution and use in source and binary forms, with or
- * without modification, are permitted provided that the following
- * conditions are met:
- *
- * - Redistributions of source code must retain the above copyright
- * notice, this list of conditions and the following disclaimer.
- *
- * - Redistributions in binary form must reproduce the above
- * copyright notice, this list of conditions and the following
- * disclaimer in the documentation and/or other materials provided
- * with the distribution.
- *
- * - Neither the name of the Eclipse Foundation, Inc. nor the
- * names of its contributors may be used to endorse or promote
- * products derived from this software without specific prior
- * written permission.
- *
- * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND
- * CONTRIBUTORS "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES,
- * INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
- * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
- * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR
- * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
- * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT
- * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
- * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER
- * CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT,
- * STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
- * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF
- * ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
- */
-
-package org.eclipse.jgit.lib;
-
-import java.io.File;
-import java.io.IOException;
-
-/**
- * The cached instance of an {@link ObjectDirectory}.
- * <p>
- * This class caches the list of loose objects in memory, so the file system is
- * not queried with stat calls.
- */
-public class CachedObjectDirectory extends CachedObjectDatabase {
- /**
- * The set that contains unpacked objects identifiers, it is created when
- * the cached instance is created.
- */
- private final ObjectIdSubclassMap<ObjectId> unpackedObjects = new ObjectIdSubclassMap<ObjectId>();
-
- /**
- * The constructor
- *
- * @param wrapped
- * the wrapped database
- */
- public CachedObjectDirectory(ObjectDirectory wrapped) {
- super(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
- protected ObjectLoader openObject2(WindowCursor curs, String objectName,
- AnyObjectId objectId) throws IOException {
- if (unpackedObjects.get(objectId) == null)
- return null;
- return super.openObject2(curs, objectName, objectId);
- }
-
- @Override
- protected boolean hasObject1(AnyObjectId objectId) {
- if (unpackedObjects.get(objectId) != null)
- return true; // known to be loose
- return super.hasObject1(objectId);
- }
-
- @Override
- protected boolean hasObject2(String name) {
- return false; // loose objects were tested by hasObject1
- }
-}
diff --git a/org.eclipse.jgit/src/org/eclipse/jgit/lib/Commit.java b/org.eclipse.jgit/src/org/eclipse/jgit/lib/Commit.java
index 66dd891..eeffb08 100644
--- a/org.eclipse.jgit/src/org/eclipse/jgit/lib/Commit.java
+++ b/org.eclipse.jgit/src/org/eclipse/jgit/lib/Commit.java
@@ -339,7 +339,14 @@ public void setMessage(final String m) {
public void commit() throws IOException {
if (getCommitId() != null)
throw new IllegalStateException(MessageFormat.format(JGitText.get().commitAlreadyExists, getCommitId()));
- setCommitId(new ObjectWriter(objdb).writeCommit(this));
+ ObjectInserter odi = objdb.newObjectInserter();
+ try {
+ ObjectId id = odi.insert(Constants.OBJ_COMMIT, odi.format(this));
+ odi.flush();
+ setCommitId(id);
+ } finally {
+ odi.release();
+ }
}
public String toString() {
diff --git a/org.eclipse.jgit/src/org/eclipse/jgit/lib/Config.java b/org.eclipse.jgit/src/org/eclipse/jgit/lib/Config.java
index ccb2516..2e1ab9a 100644
--- a/org.eclipse.jgit/src/org/eclipse/jgit/lib/Config.java
+++ b/org.eclipse.jgit/src/org/eclipse/jgit/lib/Config.java
@@ -221,6 +221,21 @@ public int getInt(final String section, String subsection,
*
* @param section
* section the key is grouped within.
+ * @param name
+ * name of the key to get.
+ * @param defaultValue
+ * default value to return if no value was present.
+ * @return an integer value from the configuration, or defaultValue.
+ */
+ public long getLong(String section, String name, long defaultValue) {
+ return getLong(section, null, name, defaultValue);
+ }
+
+ /**
+ * Obtain an integer value from the configuration.
+ *
+ * @param section
+ * section the key is grouped within.
* @param subsection
* subsection name, such a remote or branch name.
* @param name
diff --git a/org.eclipse.jgit/src/org/eclipse/jgit/lib/Constants.java b/org.eclipse.jgit/src/org/eclipse/jgit/lib/Constants.java
index a5b3d95..2269655 100644
--- a/org.eclipse.jgit/src/org/eclipse/jgit/lib/Constants.java
+++ b/org.eclipse.jgit/src/org/eclipse/jgit/lib/Constants.java
@@ -527,6 +527,10 @@ public static int decodeTypeString(final AnyObjectId id,
/** name of the file containing the IDs of the parents of a merge commit */
public static final String MERGE_HEAD = "MERGE_HEAD";
+ /** objectid for the empty blob */
+ public static final ObjectId EMPTY_BLOB_ID = ObjectId
+ .fromString("e69de29bb2d1d6434b8b29ae775ad8c2e48c5391");
+
private Constants() {
// Hide the default constructor
}
diff --git a/org.eclipse.jgit/src/org/eclipse/jgit/lib/CoreConfig.java b/org.eclipse.jgit/src/org/eclipse/jgit/lib/CoreConfig.java
index 9080ec1..5ad7910 100644
--- a/org.eclipse.jgit/src/org/eclipse/jgit/lib/CoreConfig.java
+++ b/org.eclipse.jgit/src/org/eclipse/jgit/lib/CoreConfig.java
@@ -47,6 +47,7 @@
package org.eclipse.jgit.lib;
import static java.util.zip.Deflater.DEFAULT_COMPRESSION;
+import static org.eclipse.jgit.lib.ObjectLoader.STREAM_THRESHOLD;
import org.eclipse.jgit.lib.Config.SectionParser;
@@ -67,14 +68,21 @@ public CoreConfig parse(final Config cfg) {
private final boolean logAllRefUpdates;
+ private final int streamFileThreshold;
+
private CoreConfig(final Config rc) {
compression = rc.getInt("core", "compression", DEFAULT_COMPRESSION);
packIndexVersion = rc.getInt("pack", "indexversion", 2);
logAllRefUpdates = rc.getBoolean("core", "logallrefupdates", true);
+
+ long maxMem = Runtime.getRuntime().maxMemory();
+ long sft = rc.getLong("core", null, "streamfilethreshold", STREAM_THRESHOLD);
+ sft = Math.min(sft, maxMem / 4); // don't use more than 1/4 of the heap
+ sft = Math.min(sft, Integer.MAX_VALUE); // cannot exceed array length
+ streamFileThreshold = (int) sft;
}
/**
- * @see ObjectWriter
* @return The compression level to use when storing loose objects
*/
public int getCompression() {
@@ -95,4 +103,9 @@ public int getPackIndexVersion() {
public boolean isLogAllRefUpdates() {
return logAllRefUpdates;
}
+
+ /** @return the size threshold beyond which objects must be streamed. */
+ public int getStreamFileThreshold() {
+ return streamFileThreshold;
+ }
}
diff --git a/org.eclipse.jgit/src/org/eclipse/jgit/lib/DeltaOfsPackedObjectLoader.java b/org.eclipse.jgit/src/org/eclipse/jgit/lib/DeltaOfsPackedObjectLoader.java
deleted file mode 100644
index d0e98a2..0000000
--- 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 <marek.zawirski@gmail.com>
- * Copyright (C) 2007, Robin Rosenberg <robin.rosenberg@dewire.com>
- * Copyright (C) 2006-2008, Shawn O. Pearce <spearce@spearce.org>
- * and other copyright owners as documented in the project's IP log.
- *
- * This program and the accompanying materials are made available
- * under the terms of the Eclipse Distribution License v1.0 which
- * accompanies this distribution, is reproduced below, and is
- * available at http://www.eclipse.org/org/documents/edl-v10.php
- *
- * All rights reserved.
- *
- * Redistribution and use in source and binary forms, with or
- * without modification, are permitted provided that the following
- * conditions are met:
- *
- * - Redistributions of source code must retain the above copyright
- * notice, this list of conditions and the following disclaimer.
- *
- * - Redistributions in binary form must reproduce the above
- * copyright notice, this list of conditions and the following
- * disclaimer in the documentation and/or other materials provided
- * with the distribution.
- *
- * - Neither the name of the Eclipse Foundation, Inc. nor the
- * names of its contributors may be used to endorse or promote
- * products derived from this software without specific prior
- * written permission.
- *
- * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND
- * CONTRIBUTORS "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES,
- * INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
- * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
- * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR
- * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
- * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT
- * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
- * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER
- * CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT,
- * STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
- * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF
- * ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
- */
-
-package org.eclipse.jgit.lib;
-
-import java.io.IOException;
-
-import 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 bbc1c62..0000000
--- a/org.eclipse.jgit/src/org/eclipse/jgit/lib/DeltaPackedObjectLoader.java
+++ /dev/null
@@ -1,118 +0,0 @@
-/*
- * Copyright (C) 2008-2009, Google Inc.
- * Copyright (C) 2008, Marek Zawirski <marek.zawirski@gmail.com>
- * Copyright (C) 2007, Robin Rosenberg <robin.rosenberg@dewire.com>
- * Copyright (C) 2006-2008, Shawn O. Pearce <spearce@spearce.org>
- * and other copyright owners as documented in the project's IP log.
- *
- * This program and the accompanying materials are made available
- * under the terms of the Eclipse Distribution License v1.0 which
- * accompanies this distribution, is reproduced below, and is
- * available at http://www.eclipse.org/org/documents/edl-v10.php
- *
- * All rights reserved.
- *
- * Redistribution and use in source and binary forms, with or
- * without modification, are permitted provided that the following
- * conditions are met:
- *
- * - Redistributions of source code must retain the above copyright
- * notice, this list of conditions and the following disclaimer.
- *
- * - Redistributions in binary form must reproduce the above
- * copyright notice, this list of conditions and the following
- * disclaimer in the documentation and/or other materials provided
- * with the distribution.
- *
- * - Neither the name of the Eclipse Foundation, Inc. nor the
- * names of its contributors may be used to endorse or promote
- * products derived from this software without specific prior
- * written permission.
- *
- * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND
- * CONTRIBUTORS "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES,
- * INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
- * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
- * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR
- * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
- * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT
- * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
- * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER
- * CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT,
- * STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
- * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF
- * ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
- */
-
-package org.eclipse.jgit.lib;
-
-import java.io.IOException;
-import java.text.MessageFormat;
-import java.util.zip.DataFormatException;
-
-import org.eclipse.jgit.JGitText;
-import org.eclipse.jgit.errors.CorruptObjectException;
-
-/** Reader for a deltified object stored in a pack file. */
-abstract class DeltaPackedObjectLoader extends PackedObjectLoader {
- private static final int OBJ_COMMIT = Constants.OBJ_COMMIT;
-
- private final int deltaSize;
-
- DeltaPackedObjectLoader(final PackFile pr, final long objectOffset,
- final int headerSize, final int deltaSz) {
- super(pr, objectOffset, headerSize);
- objectType = -1;
- deltaSize = deltaSz;
- }
-
- @Override
- public void materialize(final WindowCursor curs) throws IOException {
- if (cachedBytes != null) {
- return;
- }
-
- if (objectType != OBJ_COMMIT) {
- UnpackedObjectCache.Entry cache = pack.readCache(objectOffset);
- if (cache != null) {
- curs.release();
- objectType = cache.type;
- objectSize = cache.data.length;
- cachedBytes = cache.data;
- return;
- }
- }
-
- try {
- final PackedObjectLoader baseLoader = getBaseLoader(curs);
- baseLoader.materialize(curs);
- cachedBytes = BinaryDelta.apply(baseLoader.getCachedBytes(), pack
- .decompress(objectOffset + headerSize, deltaSize, curs));
- curs.release();
- objectType = baseLoader.getType();
- objectSize = cachedBytes.length;
- if (objectType != OBJ_COMMIT)
- pack.saveCache(objectOffset, cachedBytes, objectType);
- } catch (DataFormatException dfe) {
- final CorruptObjectException coe;
- coe = new CorruptObjectException(MessageFormat.format(JGitText.get().objectAtHasBadZlibStream,
- objectOffset, pack.getPackFile()));
- coe.initCause(dfe);
- throw coe;
- }
- }
-
- @Override
- public long getRawSize() {
- return deltaSize;
- }
-
- /**
- * @param curs
- * temporary thread storage during data access.
- * @return the object loader for the base object
- * @throws IOException
- */
- protected abstract PackedObjectLoader getBaseLoader(WindowCursor curs)
- throws IOException;
-}
diff --git a/org.eclipse.jgit/src/org/eclipse/jgit/lib/DeltaRefPackedObjectLoader.java b/org.eclipse.jgit/src/org/eclipse/jgit/lib/DeltaRefPackedObjectLoader.java
deleted file mode 100644
index 9f7589c..0000000
--- 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 <marek.zawirski@gmail.com>
- * Copyright (C) 2007, Robin Rosenberg <robin.rosenberg@dewire.com>
- * Copyright (C) 2006-2008, Shawn O. Pearce <spearce@spearce.org>
- * and other copyright owners as documented in the project's IP log.
- *
- * This program and the accompanying materials are made available
- * under the terms of the Eclipse Distribution License v1.0 which
- * accompanies this distribution, is reproduced below, and is
- * available at http://www.eclipse.org/org/documents/edl-v10.php
- *
- * All rights reserved.
- *
- * Redistribution and use in source and binary forms, with or
- * without modification, are permitted provided that the following
- * conditions are met:
- *
- * - Redistributions of source code must retain the above copyright
- * notice, this list of conditions and the following disclaimer.
- *
- * - Redistributions in binary form must reproduce the above
- * copyright notice, this list of conditions and the following
- * disclaimer in the documentation and/or other materials provided
- * with the distribution.
- *
- * - Neither the name of the Eclipse Foundation, Inc. nor the
- * names of its contributors may be used to endorse or promote
- * products derived from this software without specific prior
- * written permission.
- *
- * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND
- * CONTRIBUTORS "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES,
- * INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
- * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
- * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR
- * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
- * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT
- * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
- * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER
- * CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT,
- * STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
- * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF
- * ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
- */
-
-package org.eclipse.jgit.lib;
-
-import java.io.IOException;
-
-import 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/FileTreeEntry.java b/org.eclipse.jgit/src/org/eclipse/jgit/lib/FileTreeEntry.java
index ed1b51d..7990956 100644
--- a/org.eclipse.jgit/src/org/eclipse/jgit/lib/FileTreeEntry.java
+++ b/org.eclipse.jgit/src/org/eclipse/jgit/lib/FileTreeEntry.java
@@ -100,7 +100,7 @@ public void setExecutable(final boolean execute) {
* @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 defda14..bf293d1 100644
--- a/org.eclipse.jgit/src/org/eclipse/jgit/lib/GitIndex.java
+++ b/org.eclipse.jgit/src/org/eclipse/jgit/lib/GitIndex.java
@@ -71,6 +71,7 @@
import org.eclipse.jgit.dircache.DirCache;
import org.eclipse.jgit.errors.CorruptObjectException;
import org.eclipse.jgit.errors.NotSupportedException;
+import org.eclipse.jgit.events.IndexChangedEvent;
import org.eclipse.jgit.util.RawParseUtils;
/**
@@ -155,7 +156,7 @@ public boolean isChanged() {
public void rereadIfNecessary() throws IOException {
if (cacheFile.exists() && cacheFile.lastModified() != lastCacheTime) {
read();
- db.fireIndexChanged();
+ db.fireEvent(new IndexChangedEvent());
}
}
@@ -297,17 +298,39 @@ public void write() throws IOException {
fc.write(buf);
fc.close();
fileOutputStream.close();
- if (cacheFile.exists())
- if (!cacheFile.delete())
- throw new IOException(
- JGitText.get().couldNotRenameDeleteOldIndex);
+ if (cacheFile.exists()) {
+ if (db.getFS().retryFailedLockFileCommit()) {
+ // file deletion fails on windows if another
+ // thread is reading the file concurrently
+ // So let's try 10 times...
+ boolean deleted = false;
+ for (int i = 0; i < 10; i++) {
+ if (cacheFile.delete()) {
+ deleted = true;
+ break;
+ }
+ try {
+ Thread.sleep(100);
+ } catch (InterruptedException e) {
+ // ignore
+ }
+ }
+ if (!deleted)
+ throw new IOException(
+ JGitText.get().couldNotRenameDeleteOldIndex);
+ } else {
+ if (!cacheFile.delete())
+ throw new IOException(
+ JGitText.get().couldNotRenameDeleteOldIndex);
+ }
+ }
if (!tmpIndex.renameTo(cacheFile))
throw new IOException(
JGitText.get().couldNotRenameTemporaryIndexFileToIndex);
changed = false;
statDirty = false;
lastCacheTime = cacheFile.lastModified();
- db.fireIndexChanged();
+ db.fireEvent(new IndexChangedEvent());
} finally {
if (!lock.delete())
throw new IOException(
@@ -352,7 +375,7 @@ private boolean config_filemode() {
// to change this for testing.
if (filemode != null)
return filemode.booleanValue();
- RepositoryConfig config = db.getConfig();
+ Config config = db.getConfig();
filemode = Boolean.valueOf(config.getBoolean("core", null, "filemode", true));
return filemode.booleanValue();
}
@@ -437,7 +460,7 @@ public class Entry {
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;
@@ -877,17 +900,16 @@ public void checkout(File wd) throws IOException {
* @throws IOException
*/
public void checkoutEntry(File wd, Entry e) throws IOException {
- ObjectLoader ol = db.openBlob(e.sha1);
- byte[] bytes = ol.getBytes();
+ ObjectLoader ol = db.open(e.sha1, Constants.OBJ_BLOB);
File file = new File(wd, e.getName());
file.delete();
file.getParentFile().mkdirs();
- FileChannel channel = new FileOutputStream(file).getChannel();
- ByteBuffer buffer = ByteBuffer.wrap(bytes);
- int j = channel.write(buffer);
- if (j != bytes.length)
- throw new IOException(MessageFormat.format(JGitText.get().couldNotWriteFile, file));
- channel.close();
+ FileOutputStream dst = new FileOutputStream(file);
+ try {
+ ol.copyTo(dst);
+ } finally {
+ dst.close();
+ }
if (config_filemode() && File_hasExecute()) {
if (FileMode.EXECUTABLE_FILE.equals(e.mode)) {
if (!File_canExecute(file))
diff --git a/org.eclipse.jgit/src/org/eclipse/jgit/lib/IndexChangedEvent.java b/org.eclipse.jgit/src/org/eclipse/jgit/lib/IndexChangedEvent.java
deleted file mode 100644
index c866db5..0000000
--- a/org.eclipse.jgit/src/org/eclipse/jgit/lib/IndexChangedEvent.java
+++ /dev/null
@@ -1,67 +0,0 @@
-/*
- * Copyright (C) 2008, Robin Rosenberg <robin.rosenberg@dewire.com>
- * and other copyright owners as documented in the project's IP log.
- *
- * This program and the accompanying materials are made available
- * under the terms of the Eclipse Distribution License v1.0 which
- * accompanies this distribution, is reproduced below, and is
- * available at http://www.eclipse.org/org/documents/edl-v10.php
- *
- * All rights reserved.
- *
- * Redistribution and use in source and binary forms, with or
- * without modification, are permitted provided that the following
- * conditions are met:
- *
- * - Redistributions of source code must retain the above copyright
- * notice, this list of conditions and the following disclaimer.
- *
- * - Redistributions in binary form must reproduce the above
- * copyright notice, this list of conditions and the following
- * disclaimer in the documentation and/or other materials provided
- * with the distribution.
- *
- * - Neither the name of the Eclipse Foundation, Inc. nor the
- * names of its contributors may be used to endorse or promote
- * products derived from this software without specific prior
- * written permission.
- *
- * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND
- * CONTRIBUTORS "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES,
- * INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
- * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
- * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR
- * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
- * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT
- * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
- * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER
- * CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT,
- * STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
- * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF
- * ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
- */
-
-package org.eclipse.jgit.lib;
-
-/**
- * This class passes information about a changed Git index to a
- * {@link RepositoryListener}
- *
- * Currently only a reference to the repository is passed.
- */
-public class IndexChangedEvent extends RepositoryChangedEvent {
- /**
- * Create an event describing index changes in a repository.
- *
- * @param repository
- * the repository whose index (DirCache) recently changed.
- */
- public IndexChangedEvent(final Repository repository) {
- super(repository);
- }
-
- @Override
- public String toString() {
- return "IndexChangedEvent[" + getRepository() + "]";
- }
-}
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 030b942..d4e6ac9 100644
--- a/org.eclipse.jgit/src/org/eclipse/jgit/lib/IndexDiff.java
+++ b/org.eclipse.jgit/src/org/eclipse/jgit/lib/IndexDiff.java
@@ -1,6 +1,7 @@
/*
* Copyright (C) 2007, Dave Watson <dwatson@mimvista.com>
* Copyright (C) 2007-2008, Robin Rosenberg <robin.rosenberg@dewire.com>
+ * Copyright (C) 2010, Jens Baumgart <jens.baumgart@sap.com>
* and other copyright owners as documented in the project's IP log.
*
* This program and the accompanying materials are made available
@@ -44,97 +45,163 @@
package org.eclipse.jgit.lib;
-import java.io.File;
import java.io.IOException;
import java.util.HashSet;
-import org.eclipse.jgit.lib.GitIndex.Entry;
+import org.eclipse.jgit.dircache.DirCache;
+import org.eclipse.jgit.dircache.DirCacheIterator;
+import org.eclipse.jgit.revwalk.RevTree;
+import org.eclipse.jgit.revwalk.RevWalk;
+import org.eclipse.jgit.treewalk.AbstractTreeIterator;
+import org.eclipse.jgit.treewalk.TreeWalk;
+import org.eclipse.jgit.treewalk.WorkingTreeIterator;
+import org.eclipse.jgit.treewalk.filter.TreeFilter;
/**
- * Compares the Index, a Tree, and the working directory
- *
- * @deprecated Use {@link org.eclipse.jgit.treewalk.TreeWalk} instead, with at
- * least the {@link org.eclipse.jgit.dircache.DirCacheIterator} and
- * {@link org.eclipse.jgit.treewalk.FileTreeIterator} iterators, and setting
- * the filter {@link org.eclipse.jgit.treewalk.filter.TreeFilter#ANY_DIFF}.
+ * Compares the index, a tree, and the working directory
+ * Ignored files are not taken into account.
+ * The following information is retrieved:
+ * <li> added files
+ * <li> changed files
+ * <li> removed files
+ * <li> missing files
+ * <li> modified files
+ * <li> untracked files
*/
-@Deprecated
public class IndexDiff {
- private GitIndex index;
- private Tree tree;
+
+ private final static int TREE = 0;
+
+ private final static int INDEX = 1;
+
+ private final static int WORKDIR = 2;
+
+ private final Repository repository;
+
+ private final RevTree tree;
+
+ private final WorkingTreeIterator initialWorkingTreeIterator;
+
+ private HashSet<String> added = new HashSet<String>();
+
+ private HashSet<String> changed = new HashSet<String>();
+
+ private HashSet<String> removed = new HashSet<String>();
+
+ private HashSet<String> missing = new HashSet<String>();
+
+ private HashSet<String> modified = new HashSet<String>();
+
+ private HashSet<String> untracked = new HashSet<String>();
/**
- * Construct an indexdiff for diffing the workdir against
- * the index.
+ * Construct an IndexDiff
*
* @param repository
+ * @param revstr
+ * symbolic name e.g. HEAD
+ * @param workingTreeIterator
+ * iterator for working directory
* @throws IOException
*/
- public IndexDiff(Repository repository) throws IOException {
- this.tree = repository.mapTree(Constants.HEAD);
- this.index = repository.getIndex();
+ public IndexDiff(Repository repository, String revstr,
+ WorkingTreeIterator workingTreeIterator) throws IOException {
+ this.repository = repository;
+ ObjectId objectId = repository.resolve(revstr);
+ tree = new RevWalk(repository).parseTree(objectId);
+ this.initialWorkingTreeIterator = workingTreeIterator;
}
/**
- * Construct an indexdiff for diffing the workdir against both
- * the index and a tree.
+ * Construct an Indexdiff
*
- * @param tree
- * @param index
+ * @param repository
+ * @param objectId
+ * tree id
+ * @param workingTreeIterator
+ * iterator for working directory
+ * @throws IOException
*/
- public IndexDiff(Tree tree, GitIndex index) {
- this.tree = tree;
- this.index = index;
+ public IndexDiff(Repository repository, ObjectId objectId,
+ WorkingTreeIterator workingTreeIterator) throws IOException {
+ this.repository = repository;
+ tree = new RevWalk(repository).parseTree(objectId);
+ this.initialWorkingTreeIterator = workingTreeIterator;
}
- boolean anyChanges = false;
-
/**
* Run the diff operation. Until this is called, all lists will be empty
+ *
* @return if anything is different between index, tree, and workdir
* @throws IOException
*/
public boolean diff() throws IOException {
- final File root = index.getRepository().getWorkDir();
- new IndexTreeWalker(index, tree, root, new AbstractIndexTreeVisitor() {
- public void visitEntry(TreeEntry treeEntry, Entry indexEntry, File file) {
- if (treeEntry == null) {
- added.add(indexEntry.getName());
- anyChanges = true;
- } else if (indexEntry == null) {
- if (!(treeEntry instanceof Tree))
- removed.add(treeEntry.getFullName());
- anyChanges = true;
+ boolean changesExist = false;
+ DirCache dirCache = repository.readDirCache();
+ TreeWalk treeWalk = new TreeWalk(repository);
+ treeWalk.reset();
+ treeWalk.setRecursive(true);
+ // add the trees (tree, dirchache, workdir)
+ treeWalk.addTree(tree);
+ treeWalk.addTree(new DirCacheIterator(dirCache));
+ treeWalk.addTree(initialWorkingTreeIterator);
+ treeWalk.setFilter(TreeFilter.ANY_DIFF);
+ while (treeWalk.next()) {
+ AbstractTreeIterator treeIterator = treeWalk.getTree(TREE,
+ AbstractTreeIterator.class);
+ DirCacheIterator dirCacheIterator = treeWalk.getTree(INDEX,
+ DirCacheIterator.class);
+ WorkingTreeIterator workingTreeIterator = treeWalk.getTree(WORKDIR,
+ WorkingTreeIterator.class);
+ FileMode fileModeTree = treeWalk.getFileMode(TREE);
+
+ if (treeIterator != null) {
+ if (dirCacheIterator != null) {
+ if (!treeIterator.getEntryObjectId().equals(
+ dirCacheIterator.getEntryObjectId())) {
+ // in repo, in index, content diff => changed
+ changed.add(dirCacheIterator.getEntryPathString());
+ changesExist = true;
+ }
} else {
- if (!treeEntry.getId().equals(indexEntry.getObjectId())) {
- changed.add(indexEntry.getName());
- anyChanges = true;
+ // in repo, not in index => removed
+ if (!fileModeTree.equals(FileMode.TYPE_TREE)) {
+ removed.add(treeIterator.getEntryPathString());
+ changesExist = true;
}
}
-
- if (indexEntry != null) {
- if (!file.exists()) {
- missing.add(indexEntry.getName());
- anyChanges = true;
- } else {
- if (indexEntry.isModified(root, true)) {
- modified.add(indexEntry.getName());
- anyChanges = true;
- }
+ } else {
+ if (dirCacheIterator != null) {
+ // not in repo, in index => added
+ added.add(dirCacheIterator.getEntryPathString());
+ changesExist = true;
+ } else {
+ // not in repo, not in index => untracked
+ if (workingTreeIterator != null
+ && !workingTreeIterator.isEntryIgnored()) {
+ untracked.add(workingTreeIterator.getEntryPathString());
+ changesExist = true;
}
}
}
- }).walk();
- return anyChanges;
+ if (dirCacheIterator != null) {
+ if (workingTreeIterator == null) {
+ // in index, not in workdir => missing
+ missing.add(dirCacheIterator.getEntryPathString());
+ changesExist = true;
+ } else {
+ if (!dirCacheIterator.idEqual(workingTreeIterator)) {
+ // in index, in workdir, content differs => modified
+ modified.add(dirCacheIterator.getEntryPathString());
+ changesExist = true;
+ }
+ }
+ }
+ }
+ return changesExist;
}
- HashSet<String> added = new HashSet<String>();
- HashSet<String> changed = new HashSet<String>();
- HashSet<String> removed = new HashSet<String>();
- HashSet<String> missing = new HashSet<String>();
- HashSet<String> modified = new HashSet<String>();
-
/**
* @return list of files added to the index, not in the tree
*/
@@ -169,4 +236,11 @@ public HashSet<String> getMissing() {
public HashSet<String> getModified() {
return modified;
}
+
+ /**
+ * @return list of files on modified on disk relative to the index
+ */
+ public HashSet<String> getUntracked() {
+ return untracked;
+ }
}
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 7eac79f..15d118c 100644
--- a/org.eclipse.jgit/src/org/eclipse/jgit/lib/ObjectDatabase.java
+++ b/org.eclipse.jgit/src/org/eclipse/jgit/lib/ObjectDatabase.java
@@ -44,31 +44,20 @@
package org.eclipse.jgit.lib;
import java.io.IOException;
-import java.util.Collection;
-import java.util.concurrent.atomic.AtomicReference;
+
+import org.eclipse.jgit.errors.IncorrectObjectTypeException;
+import org.eclipse.jgit.errors.MissingObjectException;
/**
* Abstraction of arbitrary object storage.
* <p>
* An object database stores one or more Git objects, indexed by their unique
- * {@link ObjectId}. Optionally an object database can reference one or more
- * alternates; other ObjectDatabase instances that are searched in addition to
- * the current database.
- * <p>
- * Databases are usually divided into two halves: a half that is considered to
- * be fast to search, and a half that is considered to be slow to search. When
- * alternates are present the fast half is fully searched (recursively through
- * all alternates) before the slow half is considered.
+ * {@link ObjectId}.
*/
public abstract class ObjectDatabase {
- /** Constant indicating no alternate databases exist. */
- protected static final ObjectDatabase[] NO_ALTERNATES = {};
-
- private final AtomicReference<ObjectDatabase[]> alternates;
-
/** Initialize a new database instance for access. */
protected ObjectDatabase() {
- alternates = new AtomicReference<ObjectDatabase[]>();
+ // Protected to force extension.
}
/**
@@ -92,292 +81,101 @@ public void create() throws IOException {
}
/**
- * Close any resources held by this database and its active alternates.
+ * Create a new {@code ObjectInserter} to insert new objects.
+ * <p>
+ * The returned inserter is not itself thread-safe, but multiple concurrent
+ * inserter instances created from the same {@code ObjectDatabase} must be
+ * thread-safe.
+ *
+ * @return writer the caller can use to create objects in this database.
*/
- public final void close() {
- closeSelf();
- closeAlternates();
- }
+ public abstract ObjectInserter newInserter();
/**
- * Close any resources held by this database only; ignoring alternates.
+ * Create a new {@code ObjectReader} to read existing objects.
* <p>
- * To fully close this database and its referenced alternates, the caller
- * should instead invoke {@link #close()}.
+ * 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 void closeSelf() {
- // Assume no action is required.
- }
+ public abstract ObjectReader newReader();
- /** Fully close all loaded alternates and clear the alternate list. */
- public final void closeAlternates() {
- ObjectDatabase[] alt = alternates.get();
- if (alt != null) {
- alternates.set(null);
- closeAlternates(alt);
- }
- }
+ /**
+ * Close any resources held by this database.
+ */
+ public abstract void close();
/**
* Does the requested object exist in this database?
* <p>
- * Alternates (if present) are searched automatically.
- *
- * @param objectId
- * identity of the object to test for existence of.
- * @return true if the specified object is stored in this database, or any
- * of the alternate databases.
- */
- public final boolean hasObject(final AnyObjectId objectId) {
- return hasObjectImpl1(objectId) || hasObjectImpl2(objectId.name());
- }
-
- private final boolean hasObjectImpl1(final AnyObjectId objectId) {
- if (hasObject1(objectId)) {
- return true;
- }
- for (final ObjectDatabase alt : getAlternates()) {
- if (alt.hasObjectImpl1(objectId)) {
- return true;
- }
- }
- return tryAgain1() && hasObject1(objectId);
- }
-
- private final boolean hasObjectImpl2(final String objectId) {
- if (hasObject2(objectId)) {
- return true;
- }
- for (final ObjectDatabase alt : getAlternates()) {
- if (alt.hasObjectImpl2(objectId)) {
- return true;
- }
- }
- return false;
- }
-
- /**
- * Fast half of {@link #hasObject(AnyObjectId)}.
+ * 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.
*/
- protected abstract boolean hasObject1(AnyObjectId objectId);
-
- /**
- * Slow half of {@link #hasObject(AnyObjectId)}.
- *
- * @param objectName
- * identity of the object to test for existence of.
- * @return true if the specified object is stored in this database.
- */
- protected boolean hasObject2(String objectName) {
- // Assume the search took place during hasObject1.
- return false;
+ public boolean has(final AnyObjectId objectId) throws IOException {
+ final ObjectReader or = newReader();
+ try {
+ return or.has(objectId);
+ } finally {
+ or.release();
+ }
}
/**
* Open an object from this database.
* <p>
- * Alternates (if present) are searched automatically.
+ * This is a one-shot call interface which may be faster than allocating a
+ * {@link #newReader()} to perform the lookup.
*
- * @param curs
- * temporary working space associated with the calling thread.
* @param objectId
* identity of the object to open.
- * @return a {@link ObjectLoader} for accessing the data of the named
- * object, or null if the object does not exist.
+ * @return a {@link ObjectLoader} for accessing the object.
+ * @throws MissingObjectException
+ * the object does not exist.
* @throws IOException
+ * the object store cannot be accessed.
*/
- public final ObjectLoader openObject(final WindowCursor curs,
- final AnyObjectId objectId) throws IOException {
- ObjectLoader ldr;
-
- ldr = openObjectImpl1(curs, objectId);
- if (ldr != null) {
- return ldr;
- }
-
- ldr = openObjectImpl2(curs, objectId.name(), objectId);
- if (ldr != null) {
- return ldr;
- }
- return null;
- }
-
- private ObjectLoader openObjectImpl1(final WindowCursor curs,
- final AnyObjectId objectId) throws IOException {
- ObjectLoader ldr;
-
- ldr = openObject1(curs, objectId);
- if (ldr != null) {
- return ldr;
- }
- for (final ObjectDatabase alt : getAlternates()) {
- ldr = alt.openObjectImpl1(curs, objectId);
- if (ldr != null) {
- return ldr;
- }
- }
- if (tryAgain1()) {
- ldr = openObject1(curs, objectId);
- if (ldr != null) {
- return ldr;
- }
- }
- return null;
- }
-
- private ObjectLoader openObjectImpl2(final WindowCursor curs,
- final String objectName, final AnyObjectId objectId)
+ public ObjectLoader open(final AnyObjectId objectId)
throws IOException {
- ObjectLoader ldr;
-
- ldr = openObject2(curs, objectName, objectId);
- if (ldr != null) {
- return ldr;
- }
- for (final ObjectDatabase alt : getAlternates()) {
- ldr = alt.openObjectImpl2(curs, objectName, objectId);
- if (ldr != null) {
- return ldr;
- }
- }
- return null;
+ return open(objectId, ObjectReader.OBJ_ANY);
}
/**
- * Fast half of {@link #openObject(WindowCursor, AnyObjectId)}.
+ * Open an object from this database.
+ * <p>
+ * 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.
+ * @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.
*/
- protected abstract ObjectLoader openObject1(WindowCursor curs,
- AnyObjectId objectId) throws IOException;
-
- /**
- * Slow half of {@link #openObject(WindowCursor, AnyObjectId)}.
- *
- * @param curs
- * temporary working space associated with the calling thread.
- * @param objectName
- * name of the object to open.
- * @param objectId
- * identity of the object to open.
- * @return a {@link ObjectLoader} for accessing the data of the named
- * object, or null if the object does not exist.
- * @throws IOException
- */
- protected ObjectLoader openObject2(WindowCursor curs, String objectName,
- AnyObjectId objectId) throws IOException {
- // Assume the search took place during openObject1.
- return null;
- }
-
- /**
- * Open the object from all packs containing it.
- * <p>
- * If any alternates are present, their packs are also considered.
- *
- * @param out
- * result collection of loaders for this object, filled with
- * loaders from all packs containing specified object
- * @param curs
- * temporary working space associated with the calling thread.
- * @param objectId
- * id of object to search for
- * @throws IOException
- */
- final void openObjectInAllPacks(final Collection<PackedObjectLoader> out,
- final WindowCursor curs, final AnyObjectId objectId)
- throws IOException {
- openObjectInAllPacks1(out, curs, objectId);
- for (final ObjectDatabase alt : getAlternates()) {
- alt.openObjectInAllPacks1(out, curs, objectId);
- }
- }
-
- /**
- * Open the object from all packs containing it.
- *
- * @param out
- * result collection of loaders for this object, filled with
- * loaders from all packs containing specified object
- * @param curs
- * temporary working space associated with the calling thread.
- * @param objectId
- * id of object to search for
- * @throws IOException
- */
- void openObjectInAllPacks1(Collection<PackedObjectLoader> out,
- WindowCursor curs, AnyObjectId objectId) throws IOException {
- // Assume no pack support
- }
-
- /**
- * @return true if the fast-half search should be tried again.
- */
- protected boolean tryAgain1() {
- return false;
- }
-
- /**
- * Get the alternate databases known to this database.
- *
- * @return the alternate list. Never null, but may be an empty array.
- */
- public final ObjectDatabase[] getAlternates() {
- ObjectDatabase[] r = alternates.get();
- if (r == null) {
- synchronized (alternates) {
- r = alternates.get();
- if (r == null) {
- try {
- r = loadAlternates();
- } catch (IOException e) {
- r = NO_ALTERNATES;
- }
- alternates.set(r);
- }
- }
- }
- return r;
- }
-
- /**
- * Load the list of alternate databases into memory.
- * <p>
- * This method is invoked by {@link #getAlternates()} if the alternate list
- * has not yet been populated, or if {@link #closeAlternates()} has been
- * called on this instance and the alternate list is needed again.
- * <p>
- * If the alternate array is empty, implementors should consider using the
- * constant {@link #NO_ALTERNATES}.
- *
- * @return the alternate list for this database.
- * @throws IOException
- * the alternate list could not be accessed. The empty alternate
- * array {@link #NO_ALTERNATES} will be assumed by the caller.
- */
- protected ObjectDatabase[] loadAlternates() throws IOException {
- return NO_ALTERNATES;
- }
-
- /**
- * Close the list of alternates returned by {@link #loadAlternates()}.
- *
- * @param alt
- * the alternate list, from {@link #loadAlternates()}.
- */
- protected void closeAlternates(ObjectDatabase[] alt) {
- for (final ObjectDatabase d : alt) {
- d.close();
+ public ObjectLoader open(AnyObjectId objectId, int typeHint)
+ throws MissingObjectException, IncorrectObjectTypeException,
+ IOException {
+ final ObjectReader or = newReader();
+ try {
+ return or.open(objectId, typeHint);
+ } finally {
+ or.release();
}
}
@@ -387,9 +185,8 @@ protected void closeAlternates(ObjectDatabase[] alt) {
* done after instance creation might fail to be noticed.
*
* @return new cached database instance
- * @see CachedObjectDatabase
*/
public ObjectDatabase newCachedDatabase() {
- return new CachedObjectDatabase(this);
+ return this;
}
}
diff --git a/org.eclipse.jgit/src/org/eclipse/jgit/lib/ObjectInserter.java b/org.eclipse.jgit/src/org/eclipse/jgit/lib/ObjectInserter.java
new file mode 100644
index 0000000..fd99d39
--- /dev/null
+++ b/org.eclipse.jgit/src/org/eclipse/jgit/lib/ObjectInserter.java
@@ -0,0 +1,395 @@
+/*
+ * Copyright (C) 2007, Robin Rosenberg <robin.rosenberg@dewire.com>
+ * Copyright (C) 2008, Shawn O. Pearce <spearce@spearce.org>
+ * Copyright (C) 2009, Google Inc.
+ * and other copyright owners as documented in the project's IP log.
+ *
+ * This program and the accompanying materials are made available
+ * under the terms of the Eclipse Distribution License v1.0 which
+ * accompanies this distribution, is reproduced below, and is
+ * available at http://www.eclipse.org/org/documents/edl-v10.php
+ *
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or
+ * without modification, are permitted provided that the following
+ * conditions are met:
+ *
+ * - Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ *
+ * - Redistributions in binary form must reproduce the above
+ * copyright notice, this list of conditions and the following
+ * disclaimer in the documentation and/or other materials provided
+ * with the distribution.
+ *
+ * - Neither the name of the Eclipse Foundation, Inc. nor the
+ * names of its contributors may be used to endorse or promote
+ * products derived from this software without specific prior
+ * written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND
+ * CONTRIBUTORS "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES,
+ * INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
+ * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR
+ * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
+ * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT
+ * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
+ * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER
+ * CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT,
+ * STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
+ * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF
+ * ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ */
+
+package org.eclipse.jgit.lib;
+
+import java.io.ByteArrayInputStream;
+import java.io.ByteArrayOutputStream;
+import java.io.EOFException;
+import java.io.IOException;
+import java.io.InputStream;
+import java.io.OutputStreamWriter;
+import java.io.UnsupportedEncodingException;
+import java.security.MessageDigest;
+import java.text.MessageFormat;
+
+import org.eclipse.jgit.JGitText;
+import org.eclipse.jgit.errors.ObjectWritingException;
+
+/**
+ * Inserts objects into an existing {@code ObjectDatabase}.
+ * <p>
+ * An inserter is not thread-safe. Individual threads should each obtain their
+ * own unique inserter instance, or must arrange for locking at a higher level
+ * to ensure the inserter is in use by no more than one thread at a time.
+ * <p>
+ * Objects written by an inserter may not be immediately visible for reading
+ * after the insert method completes. Callers must invoke either
+ * {@link #release()} or {@link #flush()} prior to updating references or
+ * otherwise making the returned ObjectIds visible to other code.
+ */
+public abstract class ObjectInserter {
+ private static final byte[] htree = Constants.encodeASCII("tree");
+
+ private static final byte[] hparent = Constants.encodeASCII("parent");
+
+ private static final byte[] hauthor = Constants.encodeASCII("author");
+
+ private static final byte[] hcommitter = Constants.encodeASCII("committer");
+
+ private static final byte[] hencoding = Constants.encodeASCII("encoding");
+
+ /** An inserter that can be used for formatting and id generation only. */
+ public static class Formatter extends ObjectInserter {
+ @Override
+ public ObjectId insert(int objectType, long length, InputStream in)
+ throws IOException {
+ throw new UnsupportedOperationException();
+ }
+
+ @Override
+ public void flush() throws IOException {
+ // Do nothing.
+ }
+
+ @Override
+ public void release() {
+ // Do nothing.
+ }
+ }
+
+ /** Digest to compute the name of an object. */
+ private final MessageDigest digest;
+
+ /** Temporary working buffer for streaming data through. */
+ private byte[] tempBuffer;
+
+ /** Create a new inserter for a database. */
+ protected ObjectInserter() {
+ digest = Constants.newMessageDigest();
+ }
+
+ /** @return a temporary byte array for use by the caller. */
+ protected byte[] buffer() {
+ if (tempBuffer == null)
+ tempBuffer = new byte[8192];
+ return tempBuffer;
+ }
+
+ /** @return digest to help compute an ObjectId */
+ protected MessageDigest digest() {
+ digest.reset();
+ return digest;
+ }
+
+ /**
+ * Compute the name of an object, without inserting it.
+ *
+ * @param type
+ * type code of the object to store.
+ * @param data
+ * complete content of the object.
+ * @return the name of the object.
+ */
+ public ObjectId idFor(int type, byte[] data) {
+ return idFor(type, data, 0, data.length);
+ }
+
+ /**
+ * Compute the name of an object, without inserting it.
+ *
+ * @param type
+ * type code of the object to store.
+ * @param data
+ * complete content of the object.
+ * @param off
+ * first position within {@code data}.
+ * @param len
+ * number of bytes to copy from {@code data}.
+ * @return the name of the object.
+ */
+ public ObjectId idFor(int type, byte[] data, int off, int len) {
+ MessageDigest md = digest();
+ md.update(Constants.encodedTypeString(type));
+ md.update((byte) ' ');
+ md.update(Constants.encodeASCII(len));
+ md.update((byte) 0);
+ md.update(data, off, len);
+ return ObjectId.fromRaw(md.digest());
+ }
+
+ /**
+ * Compute the name of an object, without inserting it.
+ *
+ * @param objectType
+ * type code of the object to store.
+ * @param length
+ * number of bytes to scan from {@code in}.
+ * @param in
+ * stream providing the object content. The caller is responsible
+ * for closing the stream.
+ * @return the name of the object.
+ * @throws IOException
+ * the source stream could not be read.
+ */
+ public ObjectId idFor(int objectType, long length, InputStream in)
+ throws IOException {
+ MessageDigest md = digest();
+ md.update(Constants.encodedTypeString(objectType));
+ md.update((byte) ' ');
+ md.update(Constants.encodeASCII(length));
+ md.update((byte) 0);
+ byte[] buf = buffer();
+ while (length > 0) {
+ int n = in.read(buf, 0, (int) Math.min(length, buf.length));
+ if (n < 0)
+ throw new EOFException("Unexpected end of input");
+ md.update(buf, 0, n);
+ length -= n;
+ }
+ return ObjectId.fromRaw(md.digest());
+ }
+
+ /**
+ * Insert a single object into the store, returning its unique name.
+ *
+ * @param type
+ * type code of the object to store.
+ * @param data
+ * complete content of the object.
+ * @return the name of the object.
+ * @throws IOException
+ * the object could not be stored.
+ */
+ public ObjectId insert(final int type, final byte[] data)
+ throws IOException {
+ return insert(type, data, 0, data.length);
+ }
+
+ /**
+ * Insert a single object into the store, returning its unique name.
+ *
+ * @param type
+ * type code of the object to store.
+ * @param data
+ * complete content of the object.
+ * @param off
+ * first position within {@code data}.
+ * @param len
+ * number of bytes to copy from {@code data}.
+ * @return the name of the object.
+ * @throws IOException
+ * the object could not be stored.
+ */
+ public ObjectId insert(int type, byte[] data, int off, int len)
+ throws IOException {
+ return insert(type, len, new ByteArrayInputStream(data, off, len));
+ }
+
+ /**
+ * Insert a single object into the store, returning its unique name.
+ *
+ * @param objectType
+ * type code of the object to store.
+ * @param length
+ * number of bytes to copy from {@code in}.
+ * @param in
+ * stream providing the object content. The caller is responsible
+ * for closing the stream.
+ * @return the name of the object.
+ * @throws IOException
+ * the object could not be stored, or the source stream could
+ * not be read.
+ */
+ public abstract ObjectId insert(int objectType, long length, InputStream in)
+ throws IOException;
+
+ /**
+ * Make all inserted objects visible.
+ * <p>
+ * The flush may take some period of time to make the objects available to
+ * other threads.
+ *
+ * @throws IOException
+ * the flush could not be completed; objects inserted thus far
+ * are in an indeterminate state.
+ */
+ public abstract void flush() throws IOException;
+
+ /**
+ * Release any resources used by this inserter.
+ * <p>
+ * An inserter that has been released can be used again, but may need to be
+ * released after the subsequent usage.
+ */
+ public abstract void release();
+
+ /**
+ * Format a Tree in canonical format.
+ *
+ * @param tree
+ * the tree object to format
+ * @return canonical encoding of the tree object.
+ * @throws IOException
+ * the tree cannot be loaded, or its not in a writable state.
+ */
+ public final byte[] format(Tree tree) throws IOException {
+ ByteArrayOutputStream o = new ByteArrayOutputStream();
+ for (TreeEntry e : tree.members()) {
+ ObjectId id = e.getId();
+ if (id == null)
+ throw new ObjectWritingException(MessageFormat.format(JGitText
+ .get().objectAtPathDoesNotHaveId, e.getFullName()));
+
+ e.getMode().copyTo(o);
+ o.write(' ');
+ o.write(e.getNameUTF8());
+ o.write(0);
+ id.copyRawTo(o);
+ }
+ return o.toByteArray();
+ }
+
+ /**
+ * Format a Commit in canonical format.
+ *
+ * @param commit
+ * the commit object to format
+ * @return canonical encoding of the commit object.
+ * @throws UnsupportedEncodingException
+ * the commit's chosen encoding isn't supported on this JVM.
+ */
+ public final byte[] format(Commit commit)
+ throws UnsupportedEncodingException {
+ String encoding = commit.getEncoding();
+ if (encoding == null)
+ encoding = Constants.CHARACTER_ENCODING;
+
+ ByteArrayOutputStream os = new ByteArrayOutputStream();
+ OutputStreamWriter w = new OutputStreamWriter(os, encoding);
+ try {
+ os.write(htree);
+ os.write(' ');
+ commit.getTreeId().copyTo(os);
+ os.write('\n');
+
+ ObjectId[] ps = commit.getParentIds();
+ for (int i = 0; i < ps.length; ++i) {
+ os.write(hparent);
+ os.write(' ');
+ ps[i].copyTo(os);
+ os.write('\n');
+ }
+
+ os.write(hauthor);
+ os.write(' ');
+ w.write(commit.getAuthor().toExternalString());
+ w.flush();
+ os.write('\n');
+
+ os.write(hcommitter);
+ os.write(' ');
+ w.write(commit.getCommitter().toExternalString());
+ w.flush();
+ os.write('\n');
+
+ if (!encoding.equals(Constants.CHARACTER_ENCODING)) {
+ os.write(hencoding);
+ os.write(' ');
+ os.write(Constants.encodeASCII(encoding));
+ os.write('\n');
+ }
+
+ os.write('\n');
+ w.write(commit.getMessage());
+ w.flush();
+ } catch (IOException err) {
+ // This should never occur, the only way to get it above is
+ // for the ByteArrayOutputStream to throw, but it doesn't.
+ //
+ throw new RuntimeException(err);
+ }
+ return os.toByteArray();
+ }
+
+ /**
+ * Format a Tag in canonical format.
+ *
+ * @param tag
+ * the tag object to format
+ * @return canonical encoding of the tag object.
+ */
+ public final byte[] format(Tag tag) {
+ ByteArrayOutputStream os = new ByteArrayOutputStream();
+ OutputStreamWriter w = new OutputStreamWriter(os, Constants.CHARSET);
+ try {
+ w.write("object ");
+ tag.getObjId().copyTo(w);
+ w.write('\n');
+
+ w.write("type ");
+ w.write(tag.getType());
+ w.write("\n");
+
+ w.write("tag ");
+ w.write(tag.getTag());
+ w.write("\n");
+
+ w.write("tagger ");
+ w.write(tag.getAuthor().toExternalString());
+ w.write('\n');
+
+ w.write('\n');
+ w.write(tag.getMessage());
+ w.close();
+ } catch (IOException err) {
+ // This should never occur, the only way to get it above is
+ // for the ByteArrayOutputStream to throw, but it doesn't.
+ //
+ throw new RuntimeException(err);
+ }
+ return os.toByteArray();
+ }
+}
diff --git a/org.eclipse.jgit/src/org/eclipse/jgit/lib/ObjectLoader.java b/org.eclipse.jgit/src/org/eclipse/jgit/lib/ObjectLoader.java
index 4839a1c..e19bfc4 100644
--- a/org.eclipse.jgit/src/org/eclipse/jgit/lib/ObjectLoader.java
+++ b/org.eclipse.jgit/src/org/eclipse/jgit/lib/ObjectLoader.java
@@ -47,6 +47,12 @@
package org.eclipse.jgit.lib;
+import java.io.EOFException;
+import java.io.IOException;
+import java.io.OutputStream;
+
+import org.eclipse.jgit.errors.LargeObjectException;
+import org.eclipse.jgit.errors.MissingObjectException;
/**
* Base class for a set of loaders for different representations of Git objects.
@@ -54,6 +60,14 @@
*/
public abstract class ObjectLoader {
/**
+ * Default setting for the large object threshold.
+ * <p>
+ * Objects larger than this size must be accessed as a stream through the
+ * loader's {@link #openStream()} method.
+ */
+ public static final int STREAM_THRESHOLD = 1024 * 1024;
+
+ /**
* @return Git in pack object type, see {@link Constants}.
*/
public abstract int getType();
@@ -64,14 +78,32 @@ public abstract class ObjectLoader {
public abstract long getSize();
/**
+ * @return true if this object is too large to obtain as a byte array.
+ * Objects over a certain threshold should be accessed only by their
+ * {@link #openStream()} to prevent overflowing the JVM heap.
+ */
+ public boolean isLarge() {
+ try {
+ getCachedBytes();
+ return false;
+ } catch (LargeObjectException tooBig) {
+ return true;
+ }
+ }
+
+ /**
* Obtain a copy of the bytes of this object.
* <p>
* Unlike {@link #getCachedBytes()} this method returns an array that might
* be modified by the caller.
*
* @return the bytes of this object.
+ * @throws LargeObjectException
+ * if the object won't fit into a byte array, because
+ * {@link #isLarge()} returns true. Callers should use
+ * {@link #openStream()} instead to access the contents.
*/
- public final byte[] getBytes() {
+ public final byte[] getBytes() throws LargeObjectException {
final byte[] data = getCachedBytes();
final byte[] copy = new byte[data.length];
System.arraycopy(data, 0, copy, 0, data.length);
@@ -87,19 +119,120 @@ public abstract class ObjectLoader {
* Changes (if made) will affect the cache but not the repository itself.
*
* @return the cached bytes of this object. Do not modify it.
+ * @throws LargeObjectException
+ * if the object won't fit into a byte array, because
+ * {@link #isLarge()} returns true. Callers should use
+ * {@link #openStream()} instead to access the contents.
*/
- public abstract byte[] getCachedBytes();
+ public abstract byte[] getCachedBytes() throws LargeObjectException;
/**
- * @return raw object type from object header, as stored in storage (pack,
- * loose file). This may be different from {@link #getType()} result
- * for packs (see {@link Constants}).
+ * Obtain an input stream to read this object's data.
+ *
+ * @return a stream of this object's data. Caller must close the stream when
+ * through with it. The returned stream is buffered with a
+ * reasonable buffer size.
+ * @throws MissingObjectException
+ * the object no longer exists.
+ * @throws IOException
+ * the object store cannot be accessed.
*/
- public abstract int getRawType();
+ public abstract ObjectStream openStream() throws MissingObjectException,
+ IOException;
/**
- * @return raw size of object from object header (pack, loose file).
- * Interpretation of this value depends on {@link #getRawType()}.
+ * Copy this object to the output stream.
+ * <p>
+ * For some object store implementations, this method may be more efficient
+ * than reading from {@link #openStream()} into a temporary byte array, then
+ * writing to the destination stream.
+ * <p>
+ * The default implementation of this method is to copy with a temporary
+ * byte array for large objects, or to pass through the cached byte array
+ * for small objects.
+ *
+ * @param out
+ * stream to receive the complete copy of this object's data.
+ * Caller is responsible for flushing or closing this stream
+ * after this method returns.
+ * @throws MissingObjectException
+ * the object no longer exists.
+ * @throws IOException
+ * the object store cannot be accessed, or the stream cannot be
+ * written to.
*/
- public abstract long getRawSize();
+ public void copyTo(OutputStream out) throws MissingObjectException,
+ IOException {
+ if (isLarge()) {
+ ObjectStream in = openStream();
+ try {
+ byte[] tmp = new byte[1024];
+ long copied = 0;
+ for (;;) {
+ int n = in.read(tmp);
+ if (n < 0)
+ break;
+ out.write(tmp, 0, n);
+ copied += n;
+ }
+ if (copied != getSize())
+ throw new EOFException();
+ } finally {
+ in.close();
+ }
+ } else {
+ out.write(getCachedBytes());
+ }
+ }
+
+ /**
+ * Simple loader around the cached byte array.
+ * <p>
+ * ObjectReader implementations can use this stream type when the object's
+ * content is small enough to be accessed as a single byte array.
+ */
+ public static class SmallObject extends ObjectLoader {
+ private final int type;
+
+ private final byte[] data;
+
+ /**
+ * Construct a small object loader.
+ *
+ * @param type
+ * type of the object.
+ * @param data
+ * the object's data array. This array will be returned as-is
+ * for the {@link #getCachedBytes()} method.
+ */
+ public SmallObject(int type, byte[] data) {
+ this.type = type;
+ this.data = data;
+ }
+
+ @Override
+ public int getType() {
+ return type;
+ }
+
+ @Override
+ public long getSize() {
+ return getCachedBytes().length;
+ }
+
+ @Override
+ public boolean isLarge() {
+ return false;
+ }
+
+ @Override
+ public byte[] getCachedBytes() {
+ return data;
+ }
+
+ @Override
+ public ObjectStream openStream() {
+ return new ObjectStream.SmallStream(this);
+ }
+ }
}
diff --git a/org.eclipse.jgit/src/org/eclipse/jgit/lib/ObjectReader.java b/org.eclipse.jgit/src/org/eclipse/jgit/lib/ObjectReader.java
new file mode 100644
index 0000000..ae70638
--- /dev/null
+++ b/org.eclipse.jgit/src/org/eclipse/jgit/lib/ObjectReader.java
@@ -0,0 +1,185 @@
+/*
+ * Copyright (C) 2010, Google Inc.
+ * and other copyright owners as documented in the project's IP log.
+ *
+ * This program and the accompanying materials are made available
+ * under the terms of the Eclipse Distribution License v1.0 which
+ * accompanies this distribution, is reproduced below, and is
+ * available at http://www.eclipse.org/org/documents/edl-v10.php
+ *
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or
+ * without modification, are permitted provided that the following
+ * conditions are met:
+ *
+ * - Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ *
+ * - Redistributions in binary form must reproduce the above
+ * copyright notice, this list of conditions and the following
+ * disclaimer in the documentation and/or other materials provided
+ * with the distribution.
+ *
+ * - Neither the name of the Eclipse Foundation, Inc. nor the
+ * names of its contributors may be used to endorse or promote
+ * products derived from this software without specific prior
+ * written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND
+ * CONTRIBUTORS "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES,
+ * INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
+ * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR
+ * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
+ * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT
+ * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
+ * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER
+ * CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT,
+ * STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
+ * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF
+ * ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ */
+
+package org.eclipse.jgit.lib;
+
+import java.io.IOException;
+
+import org.eclipse.jgit.errors.IncorrectObjectTypeException;
+import org.eclipse.jgit.errors.MissingObjectException;
+import org.eclipse.jgit.storage.pack.ObjectReuseAsIs;
+
+/**
+ * Reads an {@link ObjectDatabase} for a single thread.
+ * <p>
+ * Readers that can support efficient reuse of pack encoded objects should also
+ * implement the companion interface {@link ObjectReuseAsIs}.
+ */
+public abstract class ObjectReader {
+ /** Type hint indicating the caller doesn't know the type. */
+ protected static final int OBJ_ANY = -1;
+
+ /**
+ * Construct a new reader from the same data.
+ * <p>
+ * Applications can use this method to build a new reader from the same data
+ * source, but for an different thread.
+ *
+ * @return a brand new reader, using the same data source.
+ */
+ public abstract ObjectReader newReader();
+
+ /**
+ * Does the requested object exist in this database?
+ *
+ * @param objectId
+ * identity of the object to test for existence of.
+ * @return true if the specified object is stored in this database.
+ * @throws IOException
+ * the object store cannot be accessed.
+ */
+ public boolean has(AnyObjectId objectId) throws IOException {
+ return has(objectId, OBJ_ANY);
+ }
+
+ /**
+ * Does the requested object exist in this database?
+ *
+ * @param objectId
+ * identity of the object to test for existence of.
+ * @param typeHint
+ * hint about the type of object being requested;
+ * {@link #OBJ_ANY} if the object type is not known, or does not
+ * matter to the caller.
+ * @return true if the specified object is stored in this database.
+ * @throws IncorrectObjectTypeException
+ * typeHint was not OBJ_ANY, and the object's actual type does
+ * not match typeHint.
+ * @throws IOException
+ * the object store cannot be accessed.
+ */
+ public boolean has(AnyObjectId objectId, int typeHint) throws IOException {
+ try {
+ open(objectId, typeHint);
+ return true;
+ } catch (MissingObjectException notFound) {
+ return false;
+ }
+ }
+
+ /**
+ * Open an object from this database.
+ *
+ * @param objectId
+ * identity of the object to open.
+ * @return a {@link ObjectLoader} for accessing the object.
+ * @throws MissingObjectException
+ * the object does not exist.
+ * @throws IOException
+ * the object store cannot be accessed.
+ */
+ public ObjectLoader open(AnyObjectId objectId)
+ throws MissingObjectException, IOException {
+ return open(objectId, OBJ_ANY);
+ }
+
+ /**
+ * Open an object from this database.
+ *
+ * @param objectId
+ * identity of the object to open.
+ * @param typeHint
+ * hint about the type of object being requested;
+ * {@link #OBJ_ANY} if the object type is not known, or does not
+ * matter to the caller.
+ * @return a {@link ObjectLoader} for accessing the object.
+ * @throws MissingObjectException
+ * the object does not exist.
+ * @throws IncorrectObjectTypeException
+ * typeHint was not OBJ_ANY, and the object's actual type does
+ * not match typeHint.
+ * @throws IOException
+ * the object store cannot be accessed.
+ */
+ public abstract ObjectLoader open(AnyObjectId objectId, int typeHint)
+ throws MissingObjectException, IncorrectObjectTypeException,
+ IOException;
+
+ /**
+ * Get only the size of an object.
+ * <p>
+ * The default implementation of this method opens an ObjectLoader.
+ * Databases are encouraged to override this if a faster access method is
+ * available to them.
+ *
+ * @param objectId
+ * identity of the object to open.
+ * @param typeHint
+ * hint about the type of object being requested;
+ * {@link #OBJ_ANY} if the object type is not known, or does not
+ * matter to the caller.
+ * @return size of object in bytes.
+ * @throws MissingObjectException
+ * the object does not exist.
+ * @throws IncorrectObjectTypeException
+ * typeHint was not OBJ_ANY, and the object's actual type does
+ * not match typeHint.
+ * @throws IOException
+ * the object store cannot be accessed.
+ */
+ public long getObjectSize(AnyObjectId objectId, int typeHint)
+ throws MissingObjectException, IncorrectObjectTypeException,
+ IOException {
+ return open(objectId, typeHint).getSize();
+ }
+
+ /**
+ * Release any resources used by this reader.
+ * <p>
+ * A reader that has been released can be used again, but may need to be
+ * released after the subsequent usage.
+ */
+ public void release() {
+ // Do nothing.
+ }
+}
diff --git a/org.eclipse.jgit/src/org/eclipse/jgit/lib/ObjectStream.java b/org.eclipse.jgit/src/org/eclipse/jgit/lib/ObjectStream.java
new file mode 100644
index 0000000..86d6643
--- /dev/null
+++ b/org.eclipse.jgit/src/org/eclipse/jgit/lib/ObjectStream.java
@@ -0,0 +1,220 @@
+/*
+ * Copyright (C) 2010, Google Inc.
+ * and other copyright owners as documented in the project's IP log.
+ *
+ * This program and the accompanying materials are made available
+ * under the terms of the Eclipse Distribution License v1.0 which
+ * accompanies this distribution, is reproduced below, and is
+ * available at http://www.eclipse.org/org/documents/edl-v10.php
+ *
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or
+ * without modification, are permitted provided that the following
+ * conditions are met:
+ *
+ * - Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ *
+ * - Redistributions in binary form must reproduce the above
+ * copyright notice, this list of conditions and the following
+ * disclaimer in the documentation and/or other materials provided
+ * with the distribution.
+ *
+ * - Neither the name of the Eclipse Foundation, Inc. nor the
+ * names of its contributors may be used to endorse or promote
+ * products derived from this software without specific prior
+ * written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND
+ * CONTRIBUTORS "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES,
+ * INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
+ * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR
+ * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
+ * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT
+ * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
+ * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER
+ * CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT,
+ * STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
+ * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF
+ * ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ */
+
+package org.eclipse.jgit.lib;
+
+import java.io.IOException;
+import java.io.InputStream;
+
+/** Stream of data coming from an object loaded by {@link ObjectLoader}. */
+public abstract class ObjectStream extends InputStream {
+ /** @return Git object type, see {@link Constants}. */
+ public abstract int getType();
+
+ /** @return total size of object in bytes */
+ public abstract long getSize();
+
+ /**
+ * Simple stream around the cached byte array created by a loader.
+ * <p>
+ * ObjectLoader implementations can use this stream type when the object's
+ * content is small enough to be accessed as a single byte array, but the
+ * application has still requested it in stream format.
+ */
+ public static class SmallStream extends ObjectStream {
+ private final int type;
+
+ private final byte[] data;
+
+ private int ptr;
+
+ private int mark;
+
+ /**
+ * Create the stream from an existing loader's cached bytes.
+ *
+ * @param loader
+ * the loader.
+ */
+ public SmallStream(ObjectLoader loader) {
+ this.type = loader.getType();
+ this.data = loader.getCachedBytes();
+ }
+
+ @Override
+ public int getType() {
+ return type;
+ }
+
+ @Override
+ public long getSize() {
+ return data.length;
+ }
+
+ @Override
+ public int available() {
+ return data.length - ptr;
+ }
+
+ @Override
+ public long skip(long n) {
+ int s = (int) Math.min(available(), Math.max(0, n));
+ ptr += s;
+ return s;
+ }
+
+ @Override
+ public int read() {
+ if (ptr == data.length)
+ return -1;
+ return data[ptr++] & 0xff;
+ }
+
+ @Override
+ public int read(byte[] b, int off, int len) {
+ if (ptr == data.length)
+ return -1;
+ int n = Math.min(available(), len);
+ System.arraycopy(data, ptr, b, off, n);
+ ptr += n;
+ return n;
+ }
+
+ @Override
+ public boolean markSupported() {
+ return true;
+ }
+
+ @Override
+ public void mark(int readlimit) {
+ mark = ptr;
+ }
+
+ @Override
+ public void reset() {
+ ptr = mark;
+ }
+ }
+
+ /**
+ * Simple filter stream around another stream.
+ * <p>
+ * ObjectLoader implementations can use this stream type when the object's
+ * content is available from a standard InputStream.
+ */
+ public static class Filter extends ObjectStream {
+ private final int type;
+
+ private final long size;
+
+ private final InputStream in;
+
+ /**
+ * Create a filter stream for an object.
+ *
+ * @param type
+ * the type of the object.
+ * @param size
+ * total size of the object, in bytes.
+ * @param in
+ * stream the object's raw data is available from. This
+ * stream should be buffered with some reasonable amount of
+ * buffering.
+ */
+ public Filter(int type, long size, InputStream in) {
+ this.type = type;
+ this.size = size;
+ this.in = in;
+ }
+
+ @Override
+ public int getType() {
+ return type;
+ }
+
+ @Override
+ public long getSize() {
+ return size;
+ }
+
+ @Override
+ public int available() throws IOException {
+ return in.available();
+ }
+
+ @Override
+ public long skip(long n) throws IOException {
+ return in.skip(n);
+ }
+
+ @Override
+ public int read() throws IOException {
+ return in.read();
+ }
+
+ @Override
+ public int read(byte[] b, int off, int len) throws IOException {
+ return in.read(b, off, len);
+ }
+
+ @Override
+ public boolean markSupported() {
+ return in.markSupported();
+ }
+
+ @Override
+ public void mark(int readlimit) {
+ in.mark(readlimit);
+ }
+
+ @Override
+ public void reset() throws IOException {
+ in.reset();
+ }
+
+ @Override
+ public void close() throws IOException {
+ in.close();
+ }
+ }
+}
diff --git a/org.eclipse.jgit/src/org/eclipse/jgit/lib/ObjectWriter.java b/org.eclipse.jgit/src/org/eclipse/jgit/lib/ObjectWriter.java
index 20147ed..ce91efb 100644
--- a/org.eclipse.jgit/src/org/eclipse/jgit/lib/ObjectWriter.java
+++ b/org.eclipse.jgit/src/org/eclipse/jgit/lib/ObjectWriter.java
@@ -1,6 +1,7 @@
/*
* Copyright (C) 2007, Robin Rosenberg <robin.rosenberg@dewire.com>
* Copyright (C) 2006-2008, Shawn O. Pearce <spearce@spearce.org>
+ * Copyright (C) 2009, Google Inc.
* and other copyright owners as documented in the project's IP log.
*
* This program and the accompanying materials are made available
@@ -44,61 +45,49 @@
package org.eclipse.jgit.lib;
-import java.io.ByteArrayInputStream;
-import java.io.ByteArrayOutputStream;
+import static org.eclipse.jgit.lib.Constants.OBJ_BLOB;
+import static org.eclipse.jgit.lib.Constants.OBJ_COMMIT;
+import static org.eclipse.jgit.lib.Constants.OBJ_TAG;
+import static org.eclipse.jgit.lib.Constants.OBJ_TREE;
+
import java.io.File;
import java.io.FileInputStream;
-import java.io.FileOutputStream;
import java.io.IOException;
import java.io.InputStream;
-import java.io.OutputStreamWriter;
-import java.security.MessageDigest;
-import java.text.MessageFormat;
-import java.util.zip.Deflater;
-import java.util.zip.DeflaterOutputStream;
-
-import org.eclipse.jgit.JGitText;
-import org.eclipse.jgit.errors.ObjectWritingException;
/**
* A class for writing loose objects.
+ *
+ * @deprecated Use {@link Repository#newObjectInserter()}.
*/
public class ObjectWriter {
- private static final byte[] htree = Constants.encodeASCII("tree");
-
- private static final byte[] hparent = Constants.encodeASCII("parent");
-
- private static final byte[] hauthor = Constants.encodeASCII("author");
-
- private static final byte[] hcommitter = Constants.encodeASCII("committer");
-
- private static final byte[] hencoding = Constants.encodeASCII("encoding");
-
- private final Repository r;
-
- private final byte[] buf;
-
- private final MessageDigest md;
+ private final ObjectInserter inserter;
/**
* Construct an Object writer for the specified repository
+ *
* @param d
*/
public ObjectWriter(final Repository d) {
- r = d;
- buf = new byte[8192];
- md = Constants.newMessageDigest();
+ inserter = d.newObjectInserter();
}
/**
* Write a blob with the specified data
*
- * @param b bytes of the blob
+ * @param b
+ * bytes of the blob
* @return SHA-1 of the blob
* @throws IOException
*/
public ObjectId writeBlob(final byte[] b) throws IOException {
- return writeBlob(b.length, new ByteArrayInputStream(b));
+ try {
+ ObjectId id = inserter.insert(OBJ_BLOB, b);
+ inserter.flush();
+ return id;
+ } finally {
+ inserter.release();
+ }
}
/**
@@ -130,174 +119,101 @@ public ObjectId writeBlob(final File f) throws IOException {
*/
public ObjectId writeBlob(final long len, final InputStream is)
throws IOException {
- return writeObject(Constants.OBJ_BLOB, len, is, true);
+ try {
+ ObjectId id = inserter.insert(OBJ_BLOB, len, is);
+ inserter.flush();
+ return id;
+ } finally {
+ inserter.release();
+ }
}
/**
* Write a Tree to the object database.
*
- * @param t
+ * @param tree
* Tree
* @return SHA-1 of the tree
* @throws IOException
*/
- public ObjectId writeTree(final Tree t) throws IOException {
- final ByteArrayOutputStream o = new ByteArrayOutputStream();
- final TreeEntry[] items = t.members();
- for (int k = 0; k < items.length; k++) {
- final TreeEntry e = items[k];
- final ObjectId id = e.getId();
-
- if (id == null)
- throw new ObjectWritingException(MessageFormat.format(
- JGitText.get().objectAtPathDoesNotHaveId, e.getFullName()));
-
- e.getMode().copyTo(o);
- o.write(' ');
- o.write(e.getNameUTF8());
- o.write(0);
- id.copyRawTo(o);
+ public ObjectId writeTree(Tree tree) throws IOException {
+ try {
+ ObjectId id = inserter.insert(OBJ_TREE, inserter.format(tree));
+ inserter.flush();
+ return id;
+ } finally {
+ inserter.release();
}
- return writeCanonicalTree(o.toByteArray());
}
/**
* Write a canonical tree to the object database.
*
- * @param b
+ * @param treeData
* the canonical encoding of the tree object.
* @return SHA-1 of the tree
* @throws IOException
*/
- public ObjectId writeCanonicalTree(final byte[] b) throws IOException {
- return writeTree(b.length, new ByteArrayInputStream(b));
- }
-
- private ObjectId writeTree(final long len, final InputStream is)
- throws IOException {
- return writeObject(Constants.OBJ_TREE, len, is, true);
+ public ObjectId writeCanonicalTree(byte[] treeData) throws IOException {
+ try {
+ ObjectId id = inserter.insert(OBJ_TREE, treeData);
+ inserter.flush();
+ return id;
+ } finally {
+ inserter.release();
+ }
}
/**
* Write a Commit to the object database
*
- * @param c
+ * @param commit
* Commit to store
* @return SHA-1 of the commit
* @throws IOException
*/
- public ObjectId writeCommit(final Commit c) throws IOException {
- final ByteArrayOutputStream os = new ByteArrayOutputStream();
- String encoding = c.getEncoding();
- if (encoding == null)
- encoding = Constants.CHARACTER_ENCODING;
- final OutputStreamWriter w = new OutputStreamWriter(os, encoding);
-
- os.write(htree);
- os.write(' ');
- c.getTreeId().copyTo(os);
- os.write('\n');
-
- ObjectId[] ps = c.getParentIds();
- for (int i=0; i<ps.length; ++i) {
- os.write(hparent);
- os.write(' ');
- ps[i].copyTo(os);
- os.write('\n');
+ public ObjectId writeCommit(Commit commit) throws IOException {
+ try {
+ ObjectId id = inserter.insert(OBJ_COMMIT, inserter.format(commit));
+ inserter.flush();
+ return id;
+ } finally {
+ inserter.release();
}
-
- os.write(hauthor);
- os.write(' ');
- w.write(c.getAuthor().toExternalString());
- w.flush();
- os.write('\n');
-
- os.write(hcommitter);
- os.write(' ');
- w.write(c.getCommitter().toExternalString());
- w.flush();
- os.write('\n');
-
- if (!encoding.equals(Constants.CHARACTER_ENCODING)) {
- os.write(hencoding);
- os.write(' ');
- os.write(Constants.encodeASCII(encoding));
- os.write('\n');
- }
-
- os.write('\n');
- w.write(c.getMessage());
- w.flush();
-
- return writeCommit(os.toByteArray());
- }
-
- private ObjectId writeTag(final byte[] b) throws IOException {
- return writeTag(b.length, new ByteArrayInputStream(b));
}
/**
* Write an annotated Tag to the object database
*
- * @param c
+ * @param tag
* Tag
* @return SHA-1 of the tag
* @throws IOException
*/
- public ObjectId writeTag(final Tag c) throws IOException {
- final ByteArrayOutputStream os = new ByteArrayOutputStream();
- final OutputStreamWriter w = new OutputStreamWriter(os,
- Constants.CHARSET);
-
- w.write("object ");
- c.getObjId().copyTo(w);
- w.write('\n');
-
- w.write("type ");
- w.write(c.getType());
- w.write("\n");
-
- w.write("tag ");
- w.write(c.getTag());
- w.write("\n");
-
- w.write("tagger ");
- w.write(c.getAuthor().toExternalString());
- w.write('\n');
-
- w.write('\n');
- w.write(c.getMessage());
- w.close();
-
- return writeTag(os.toByteArray());
- }
-
- private ObjectId writeCommit(final byte[] b) throws IOException {
- return writeCommit(b.length, new ByteArrayInputStream(b));
- }
-
- private ObjectId writeCommit(final long len, final InputStream is)
- throws IOException {
- return writeObject(Constants.OBJ_COMMIT, len, is, true);
- }
-
- private ObjectId writeTag(final long len, final InputStream is)
- throws IOException {
- return writeObject(Constants.OBJ_TAG, len, is, true);
+ public ObjectId writeTag(Tag tag) throws IOException {
+ try {
+ ObjectId id = inserter.insert(OBJ_TAG, inserter.format(tag));
+ inserter.flush();
+ return id;
+ } finally {
+ inserter.release();
+ }
}
/**
* Compute the SHA-1 of a blob without creating an object. This is for
* figuring out if we already have a blob or not.
*
- * @param len number of bytes to consume
- * @param is stream for read blob data from
+ * @param len
+ * number of bytes to consume
+ * @param is
+ * stream for read blob data from
* @return SHA-1 of a looked for blob
* @throws IOException
*/
- public ObjectId computeBlobSha1(final long len, final InputStream is)
+ public ObjectId computeBlobSha1(long len, InputStream is)
throws IOException {
- return writeObject(Constants.OBJ_BLOB, len, is, false);
+ return computeObjectSha1(OBJ_BLOB, len, is);
}
/**
@@ -313,119 +229,12 @@ public ObjectId computeBlobSha1(final long len, final InputStream is)
* @return SHA-1 of data combined with type information
* @throws IOException
*/
- public ObjectId computeObjectSha1(final int type, final long len, final InputStream is)
+ public ObjectId computeObjectSha1(int type, long len, InputStream is)
throws IOException {
- return writeObject(type, len, is, false);
- }
-
- ObjectId writeObject(final int type, long len, final InputStream is,
- boolean store) throws IOException {
- final File t;
- final DeflaterOutputStream deflateStream;
- final FileOutputStream fileStream;
- ObjectId id = null;
- Deflater def = null;
-
- if (store) {
- t = File.createTempFile("noz", null, r.getObjectsDirectory());
- fileStream = new FileOutputStream(t);
- } else {
- t = null;
- fileStream = null;
- }
-
- md.reset();
- if (store) {
- def = new Deflater(r.getConfig().getCore().getCompression());
- deflateStream = new DeflaterOutputStream(fileStream, def);
- } else
- deflateStream = null;
-
try {
- byte[] header;
- int n;
-
- header = Constants.encodedTypeString(type);
- md.update(header);
- if (deflateStream != null)
- deflateStream.write(header);
-
- md.update((byte) ' ');
- if (deflateStream != null)
- deflateStream.write((byte) ' ');
-
- header = Constants.encodeASCII(len);
- md.update(header);
- if (deflateStream != null)
- deflateStream.write(header);
-
- md.update((byte) 0);
- if (deflateStream != null)
- deflateStream.write((byte) 0);
-
- while (len > 0
- && (n = is.read(buf, 0, (int) Math.min(len, buf.length))) > 0) {
- md.update(buf, 0, n);
- if (deflateStream != null)
- deflateStream.write(buf, 0, n);
- len -= n;
- }
-
- if (len != 0)
- throw new IOException("Input did not match supplied length. "
- + len + " bytes are missing.");
-
- if (deflateStream != null ) {
- deflateStream.close();
- if (t != null)
- t.setReadOnly();
- }
-
- id = ObjectId.fromRaw(md.digest());
+ return inserter.idFor(type, len, is);
} finally {
- if (id == null && deflateStream != null) {
- try {
- deflateStream.close();
- } finally {
- t.delete();
- }
- }
- if (def != null) {
- def.end();
- }
+ inserter.release();
}
-
- if (t == null)
- return id;
-
- if (r.hasObject(id)) {
- // Object is already in the repository so remove
- // the temporary file.
- //
- t.delete();
- } else {
- final File o = r.toFile(id);
- if (!t.renameTo(o)) {
- // Maybe the directory doesn't exist yet as the object
- // directories are always lazily created. Note that we
- // try the rename first as the directory likely does exist.
- //
- o.getParentFile().mkdir();
- if (!t.renameTo(o)) {
- if (!r.hasObject(id)) {
- // The object failed to be renamed into its proper
- // location and it doesn't exist in the repository
- // either. We really don't know what went wrong, so
- // fail.
- //
- t.delete();
- throw new ObjectWritingException("Unable to"
- + " create new object: " + o);
- }
- }
- }
- }
-
- return id;
}
}
diff --git a/org.eclipse.jgit/src/org/eclipse/jgit/lib/PackFile.java b/org.eclipse.jgit/src/org/eclipse/jgit/lib/PackFile.java
deleted file mode 100644
index 829832e..0000000
--- a/org.eclipse.jgit/src/org/eclipse/jgit/lib/PackFile.java
+++ /dev/null
@@ -1,526 +0,0 @@
-/*
- * Copyright (C) 2008-2009, Google Inc.
- * Copyright (C) 2007, Robin Rosenberg <robin.rosenberg@dewire.com>
- * Copyright (C) 2006-2008, Shawn O. Pearce <spearce@spearce.org>
- * and other copyright owners as documented in the project's IP log.
- *
- * This program and the accompanying materials are made available
- * under the terms of the Eclipse Distribution License v1.0 which
- * accompanies this distribution, is reproduced below, and is
- * available at http://www.eclipse.org/org/documents/edl-v10.php
- *
- * All rights reserved.
- *
- * Redistribution and use in source and binary forms, with or
- * without modification, are permitted provided that the following
- * conditions are met:
- *
- * - Redistributions of source code must retain the above copyright
- * notice, this list of conditions and the following disclaimer.
- *
- * - Redistributions in binary form must reproduce the above
- * copyright notice, this list of conditions and the following
- * disclaimer in the documentation and/or other materials provided
- * with the distribution.
- *
- * - Neither the name of the Eclipse Foundation, Inc. nor the
- * names of its contributors may be used to endorse or promote
- * products derived from this software without specific prior
- * written permission.
- *
- * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND
- * CONTRIBUTORS "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES,
- * INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
- * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
- * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR
- * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
- * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT
- * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
- * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER
- * CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT,
- * STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
- * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF
- * ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
- */
-
-package org.eclipse.jgit.lib;
-
-import java.io.EOFException;
-import java.io.File;
-import java.io.IOException;
-import java.io.OutputStream;
-import java.io.RandomAccessFile;
-import java.nio.MappedByteBuffer;
-import java.nio.channels.FileChannel.MapMode;
-import java.text.MessageFormat;
-import java.util.Arrays;
-import java.util.Collections;
-import java.util.Comparator;
-import java.util.Iterator;
-import java.util.zip.CRC32;
-import java.util.zip.CheckedOutputStream;
-import java.util.zip.DataFormatException;
-
-import org.eclipse.jgit.JGitText;
-import org.eclipse.jgit.errors.CorruptObjectException;
-import org.eclipse.jgit.errors.PackInvalidException;
-import org.eclipse.jgit.errors.PackMismatchException;
-import org.eclipse.jgit.util.NB;
-import org.eclipse.jgit.util.RawParseUtils;
-
-/**
- * A Git version 2 pack file representation. A pack file contains Git objects in
- * delta packed format yielding high compression of lots of object where some
- * objects are similar.
- */
-public class PackFile implements Iterable<PackIndex.MutableEntry> {
- /** Sorts PackFiles to be most recently created to least recently created. */
- public static Comparator<PackFile> SORT = new Comparator<PackFile>() {
- public int compare(final PackFile a, final PackFile b) {
- return b.packLastModified - a.packLastModified;
- }
- };
-
- private final File idxFile;
-
- private final File packFile;
-
- final int hash;
-
- private RandomAccessFile fd;
-
- /** Serializes reads performed against {@link #fd}. */
- private final Object readLock = new Object();
-
- long length;
-
- private int activeWindows;
-
- private int activeCopyRawData;
-
- private int packLastModified;
-
- private volatile boolean invalid;
-
- private byte[] packChecksum;
-
- private PackIndex loadedIdx;
-
- private PackReverseIndex reverseIdx;
-
- /**
- * Construct a reader for an existing, pre-indexed packfile.
- *
- * @param idxFile
- * path of the <code>.idx</code> file listing the contents.
- * @param packFile
- * path of the <code>.pack</code> file holding the data.
- */
- public PackFile(final File idxFile, final File packFile) {
- this.idxFile = idxFile;
- this.packFile = packFile;
- this.packLastModified = (int) (packFile.lastModified() >> 10);
-
- // Multiply by 31 here so we can more directly combine with another
- // value in WindowCache.hash(), without doing the multiply there.
- //
- hash = System.identityHashCode(this) * 31;
- length = Long.MAX_VALUE;
- }
-
- private synchronized PackIndex idx() throws IOException {
- if (loadedIdx == null) {
- if (invalid)
- throw new PackInvalidException(packFile);
-
- try {
- final PackIndex idx = PackIndex.open(idxFile);
-
- if (packChecksum == null)
- packChecksum = idx.packChecksum;
- else if (!Arrays.equals(packChecksum, idx.packChecksum))
- throw new PackMismatchException(JGitText.get().packChecksumMismatch);
-
- loadedIdx = idx;
- } catch (IOException e) {
- invalid = true;
- throw e;
- }
- }
- return loadedIdx;
- }
-
- final PackedObjectLoader resolveBase(final WindowCursor curs, final long ofs)
- throws IOException {
- return reader(curs, ofs);
- }
-
- /** @return the File object which locates this pack on disk. */
- public File getPackFile() {
- return packFile;
- }
-
- /**
- * Determine if an object is contained within the pack file.
- * <p>
- * For performance reasons only the index file is searched; the main pack
- * content is ignored entirely.
- * </p>
- *
- * @param id
- * the object to look for. Must not be null.
- * @return true if the object is in this pack; false otherwise.
- * @throws IOException
- * the index file cannot be loaded into memory.
- */
- public boolean hasObject(final AnyObjectId id) throws IOException {
- return idx().hasObject(id);
- }
-
- /**
- * Get an object from this pack.
- *
- * @param curs
- * temporary working space associated with the calling thread.
- * @param id
- * the object to obtain from the pack. Must not be null.
- * @return the object loader for the requested object if it is contained in
- * this pack; null if the object was not found.
- * @throws IOException
- * the pack file or the index could not be read.
- */
- public PackedObjectLoader get(final WindowCursor curs, final AnyObjectId id)
- throws IOException {
- final long offset = idx().findOffset(id);
- return 0 < offset ? reader(curs, offset) : null;
- }
-
- /**
- * Close the resources utilized by this repository
- */
- public void close() {
- UnpackedObjectCache.purge(this);
- WindowCache.purge(this);
- synchronized (this) {
- loadedIdx = null;
- reverseIdx = null;
- }
- }
-
- /**
- * Provide iterator over entries in associated pack index, that should also
- * exist in this pack file. Objects returned by such iterator are mutable
- * during iteration.
- * <p>
- * Iterator returns objects in SHA-1 lexicographical order.
- * </p>
- *
- * @return iterator over entries of associated pack index
- *
- * @see PackIndex#iterator()
- */
- public Iterator<PackIndex.MutableEntry> iterator() {
- try {
- return idx().iterator();
- } catch (IOException e) {
- return Collections.<PackIndex.MutableEntry> emptyList().iterator();
- }
- }
-
- /**
- * Obtain the total number of objects available in this pack. This method
- * relies on pack index, giving number of effectively available objects.
- *
- * @return number of objects in index of this pack, likewise in this pack
- * @throws IOException
- * the index file cannot be loaded into memory.
- */
- long getObjectCount() throws IOException {
- return idx().getObjectCount();
- }
-
- /**
- * Search for object id with the specified start offset in associated pack
- * (reverse) index.
- *
- * @param offset
- * start offset of object to find
- * @return object id for this offset, or null if no object was found
- * @throws IOException
- * the index file cannot be loaded into memory.
- */
- ObjectId findObjectForOffset(final long offset) throws IOException {
- return getReverseIdx().findObject(offset);
- }
-
- final UnpackedObjectCache.Entry readCache(final long position) {
- return UnpackedObjectCache.get(this, position);
- }
-
- final void saveCache(final long position, final byte[] data, final int type) {
- UnpackedObjectCache.store(this, position, data, type);
- }
-
- final byte[] decompress(final long position, final int totalSize,
- final WindowCursor curs) throws DataFormatException, IOException {
- final byte[] dstbuf = new byte[totalSize];
- if (curs.inflate(this, position, dstbuf, 0) != totalSize)
- throw new EOFException(MessageFormat.format(JGitText.get().shortCompressedStreamAt, position));
- return dstbuf;
- }
-
- final void copyRawData(final PackedObjectLoader loader,
- final OutputStream out, final byte buf[], final WindowCursor curs)
- throws IOException {
- final long objectOffset = loader.objectOffset;
- final long dataOffset = objectOffset + loader.headerSize;
- final long sz = findEndOffset(objectOffset) - dataOffset;
- final PackIndex idx = idx();
-
- if (idx.hasCRC32Support()) {
- final CRC32 crc = new CRC32();
- int headerCnt = loader.headerSize;
- while (headerCnt > 0) {
- final int toRead = Math.min(headerCnt, buf.length);
- readFully(objectOffset, buf, 0, toRead, curs);
- crc.update(buf, 0, toRead);
- headerCnt -= toRead;
- }
- final CheckedOutputStream crcOut = new CheckedOutputStream(out, crc);
- copyToStream(dataOffset, buf, sz, crcOut, curs);
- final long computed = crc.getValue();
-
- final ObjectId id = findObjectForOffset(objectOffset);
- final long expected = idx.findCRC32(id);
- if (computed != expected)
- throw new CorruptObjectException(MessageFormat.format(
- JGitText.get().objectAtHasBadZlibStream, objectOffset, getPackFile()));
- } else {
- try {
- curs.inflateVerify(this, dataOffset);
- } catch (DataFormatException dfe) {
- final CorruptObjectException coe;
- coe = new CorruptObjectException(MessageFormat.format(
- JGitText.get().objectAtHasBadZlibStream, objectOffset, getPackFile()));
- coe.initCause(dfe);
- throw coe;
- }
- copyToStream(dataOffset, buf, sz, out, curs);
- }
- }
-
- boolean supportsFastCopyRawData() throws IOException {
- return idx().hasCRC32Support();
- }
-
- boolean invalid() {
- return invalid;
- }
-
- private void readFully(final long position, final byte[] dstbuf,
- int dstoff, final int cnt, final WindowCursor curs)
- throws IOException {
- if (curs.copy(this, position, dstbuf, dstoff, cnt) != cnt)
- throw new EOFException();
- }
-
- private void copyToStream(long position, final byte[] buf, long cnt,
- final OutputStream out, final WindowCursor curs)
- throws IOException, EOFException {
- while (cnt > 0) {
- final int toRead = (int) Math.min(cnt, buf.length);
- readFully(position, buf, 0, toRead, curs);
- position += toRead;
- cnt -= toRead;
- out.write(buf, 0, toRead);
- }
- }
-
- synchronized void beginCopyRawData() throws IOException {
- if (++activeCopyRawData == 1 && activeWindows == 0)
- doOpen();
- }
-
- synchronized void endCopyRawData() {
- if (--activeCopyRawData == 0 && activeWindows == 0)
- doClose();
- }
-
- synchronized boolean beginWindowCache() throws IOException {
- if (++activeWindows == 1) {
- if (activeCopyRawData == 0)
- doOpen();
- return true;
- }
- return false;
- }
-
- synchronized boolean endWindowCache() {
- final boolean r = --activeWindows == 0;
- if (r && activeCopyRawData == 0)
- doClose();
- return r;
- }
-
- private void doOpen() throws IOException {
- try {
- if (invalid)
- throw new PackInvalidException(packFile);
- synchronized (readLock) {
- fd = new RandomAccessFile(packFile, "r");
- length = fd.length();
- onOpenPack();
- }
- } catch (IOException ioe) {
- openFail();
- throw ioe;
- } catch (RuntimeException re) {
- openFail();
- throw re;
- } catch (Error re) {
- openFail();
- throw re;
- }
- }
-
- private void openFail() {
- activeWindows = 0;
- activeCopyRawData = 0;
- invalid = true;
- doClose();
- }
-
- private void doClose() {
- synchronized (readLock) {
- if (fd != null) {
- try {
- fd.close();
- } catch (IOException err) {
- // Ignore a close event. We had it open only for reading.
- // There should not be errors related to network buffers
- // not flushed, etc.
- }
- fd = null;
- }
- }
- }
-
- ByteArrayWindow read(final long pos, int size) throws IOException {
- synchronized (readLock) {
- if (length < pos + size)
- size = (int) (length - pos);
- final byte[] buf = new byte[size];
- fd.seek(pos);
- fd.readFully(buf, 0, size);
- return new ByteArrayWindow(this, pos, buf);
- }
- }
-
- ByteWindow mmap(final long pos, int size) throws IOException {
- synchronized (readLock) {
- if (length < pos + size)
- size = (int) (length - pos);
-
- MappedByteBuffer map;
- try {
- map = fd.getChannel().map(MapMode.READ_ONLY, pos, size);
- } catch (IOException ioe1) {
- // The most likely reason this failed is the JVM has run out
- // of virtual memory. We need to discard quickly, and try to
- // force the GC to finalize and release any existing mappings.
- //
- System.gc();
- System.runFinalization();
- map = fd.getChannel().map(MapMode.READ_ONLY, pos, size);
- }
-
- if (map.hasArray())
- return new ByteArrayWindow(this, pos, map.array());
- return new ByteBufferWindow(this, pos, map);
- }
- }
-
- private void onOpenPack() throws IOException {
- final PackIndex idx = idx();
- final byte[] buf = new byte[20];
-
- fd.seek(0);
- fd.readFully(buf, 0, 12);
- if (RawParseUtils.match(buf, 0, Constants.PACK_SIGNATURE) != 4)
- throw new IOException(JGitText.get().notAPACKFile);
- final long vers = NB.decodeUInt32(buf, 4);
- final long packCnt = NB.decodeUInt32(buf, 8);
- if (vers != 2 && vers != 3)
- throw new IOException(MessageFormat.format(JGitText.get().unsupportedPackVersion, vers));
-
- if (packCnt != idx.getObjectCount())
- throw new PackMismatchException(MessageFormat.format(
- JGitText.get().packObjectCountMismatch, packCnt, idx.getObjectCount(), getPackFile()));
-
- fd.seek(length - 20);
- fd.read(buf, 0, 20);
- if (!Arrays.equals(buf, packChecksum))
- throw new PackMismatchException(MessageFormat.format(
- JGitText.get().packObjectCountMismatch
- , ObjectId.fromRaw(buf).name()
- , ObjectId.fromRaw(idx.packChecksum).name()
- , getPackFile()));
- }
-
- private PackedObjectLoader reader(final WindowCursor curs,
- final long objOffset) throws IOException {
- int p = 0;
- final byte[] ib = curs.tempId;
- readFully(objOffset, ib, 0, 20, curs);
- int c = ib[p++] & 0xff;
- final int typeCode = (c >> 4) & 7;
- long dataSize = c & 15;
- int shift = 4;
- while ((c & 0x80) != 0) {
- c = ib[p++] & 0xff;
- dataSize += (c & 0x7f) << shift;
- shift += 7;
- }
-
- switch (typeCode) {
- case Constants.OBJ_COMMIT:
- case Constants.OBJ_TREE:
- case Constants.OBJ_BLOB:
- case Constants.OBJ_TAG:
- return new WholePackedObjectLoader(this, objOffset, p, typeCode,
- (int) dataSize);
-
- case Constants.OBJ_OFS_DELTA: {
- c = ib[p++] & 0xff;
- long ofs = c & 127;
- while ((c & 128) != 0) {
- ofs += 1;
- c = ib[p++] & 0xff;
- ofs <<= 7;
- ofs += (c & 127);
- }
- return new DeltaOfsPackedObjectLoader(this, objOffset, p,
- (int) dataSize, objOffset - ofs);
- }
- case Constants.OBJ_REF_DELTA: {
- readFully(objOffset + p, ib, 0, 20, curs);
- return new DeltaRefPackedObjectLoader(this, objOffset, p + 20,
- (int) dataSize, ObjectId.fromRaw(ib));
- }
- default:
- throw new IOException(MessageFormat.format(JGitText.get().unknownObjectType, typeCode));
- }
- }
-
- private long findEndOffset(final long startOffset)
- throws IOException, CorruptObjectException {
- final long maxOffset = length - 20;
- return getReverseIdx().findNextOffset(startOffset, maxOffset);
- }
-
- private synchronized PackReverseIndex getReverseIdx() throws IOException {
- if (reverseIdx == null)
- reverseIdx = new PackReverseIndex(idx());
- return reverseIdx;
- }
-}
diff --git a/org.eclipse.jgit/src/org/eclipse/jgit/lib/PackOutputStream.java b/org.eclipse.jgit/src/org/eclipse/jgit/lib/PackOutputStream.java
deleted file mode 100644
index a348f1e..0000000
--- a/org.eclipse.jgit/src/org/eclipse/jgit/lib/PackOutputStream.java
+++ /dev/null
@@ -1,107 +0,0 @@
-/*
- * Copyright (C) 2008-2009, Google Inc.
- * Copyright (C) 2008, Marek Zawirski <marek.zawirski@gmail.com>
- * and other copyright owners as documented in the project's IP log.
- *
- * This program and the accompanying materials are made available
- * under the terms of the Eclipse Distribution License v1.0 which
- * accompanies this distribution, is reproduced below, and is
- * available at http://www.eclipse.org/org/documents/edl-v10.php
- *
- * All rights reserved.
- *
- * Redistribution and use in source and binary forms, with or
- * without modification, are permitted provided that the following
- * conditions are met:
- *
- * - Redistributions of source code must retain the above copyright
- * notice, this list of conditions and the following disclaimer.
- *
- * - Redistributions in binary form must reproduce the above
- * copyright notice, this list of conditions and the following
- * disclaimer in the documentation and/or other materials provided
- * with the distribution.
- *
- * - Neither the name of the Eclipse Foundation, Inc. nor the
- * names of its contributors may be used to endorse or promote
- * products derived from this software without specific prior
- * written permission.
- *
- * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND
- * CONTRIBUTORS "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES,
- * INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
- * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
- * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR
- * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
- * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT
- * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
- * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER
- * CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT,
- * STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
- * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF
- * ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
- */
-
-package org.eclipse.jgit.lib;
-
-import java.io.IOException;
-import java.io.OutputStream;
-import java.security.MessageDigest;
-import java.util.zip.CRC32;
-
-/** Custom output stream to support {@link PackWriter}. */
-final class PackOutputStream extends OutputStream {
- private final OutputStream out;
-
- private final CRC32 crc = new CRC32();
-
- private final MessageDigest md = Constants.newMessageDigest();
-
- private long count;
-
- PackOutputStream(final OutputStream out) {
- this.out = out;
- }
-
- @Override
- public void write(final int b) throws IOException {
- out.write(b);
- crc.update(b);
- md.update((byte) b);
- count++;
- }
-
- @Override
- public void write(final byte[] b, final int off, final int len)
- throws IOException {
- out.write(b, off, len);
- crc.update(b, off, len);
- md.update(b, off, len);
- count += len;
- }
-
- @Override
- public void flush() throws IOException {
- out.flush();
- }
-
- /** @return total number of bytes written since stream start. */
- long length() {
- return count;
- }
-
- /** @return obtain the current CRC32 register. */
- int getCRC32() {
- return (int) crc.getValue();
- }
-
- /** Reinitialize the CRC32 register for a new region. */
- void resetCRC32() {
- crc.reset();
- }
-
- /** @return obtain the current SHA-1 digest. */
- byte[] getDigest() {
- return md.digest();
- }
-}
diff --git a/org.eclipse.jgit/src/org/eclipse/jgit/lib/PackWriter.java b/org.eclipse.jgit/src/org/eclipse/jgit/lib/PackWriter.java
deleted file mode 100644
index 48f41a5..0000000
--- a/org.eclipse.jgit/src/org/eclipse/jgit/lib/PackWriter.java
+++ /dev/null
@@ -1,1040 +0,0 @@
-/*
- * Copyright (C) 2008-2010, Google Inc.
- * Copyright (C) 2008, Marek Zawirski <marek.zawirski@gmail.com>
- * and other copyright owners as documented in the project's IP log.
- *
- * This program and the accompanying materials are made available
- * under the terms of the Eclipse Distribution License v1.0 which
- * accompanies this distribution, is reproduced below, and is
- * available at http://www.eclipse.org/org/documents/edl-v10.php
- *
- * All rights reserved.
- *
- * Redistribution and use in source and binary forms, with or
- * without modification, are permitted provided that the following
- * conditions are met:
- *
- * - Redistributions of source code must retain the above copyright
- * notice, this list of conditions and the following disclaimer.
- *
- * - Redistributions in binary form must reproduce the above
- * copyright notice, this list of conditions and the following
- * disclaimer in the documentation and/or other materials provided
- * with the distribution.
- *
- * - Neither the name of the Eclipse Foundation, Inc. nor the
- * names of its contributors may be used to endorse or promote
- * products derived from this software without specific prior
- * written permission.
- *
- * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND
- * CONTRIBUTORS "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES,
- * INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
- * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
- * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR
- * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
- * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT
- * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
- * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER
- * CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT,
- * STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
- * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF
- * ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
- */
-
-package org.eclipse.jgit.lib;
-
-import java.io.IOException;
-import java.io.OutputStream;
-import java.security.MessageDigest;
-import java.util.ArrayList;
-import java.util.Collection;
-import java.util.Collections;
-import java.util.Iterator;
-import java.util.List;
-import java.util.zip.Deflater;
-
-import org.eclipse.jgit.JGitText;
-import org.eclipse.jgit.errors.IncorrectObjectTypeException;
-import org.eclipse.jgit.errors.MissingObjectException;
-import org.eclipse.jgit.revwalk.ObjectWalk;
-import org.eclipse.jgit.revwalk.RevFlag;
-import org.eclipse.jgit.revwalk.RevObject;
-import org.eclipse.jgit.revwalk.RevSort;
-import org.eclipse.jgit.transport.PackedObjectInfo;
-import org.eclipse.jgit.util.NB;
-
-/**
- * <p>
- * PackWriter class is responsible for generating pack files from specified set
- * of objects from repository. This implementation produce pack files in format
- * version 2.
- * </p>
- * <p>
- * Source of objects may be specified in two ways:
- * <ul>
- * <li>(usually) by providing sets of interesting and uninteresting objects in
- * repository - all interesting objects and their ancestors except uninteresting
- * objects and their ancestors will be included in pack, or</li>
- * <li>by providing iterator of {@link RevObject} specifying exact list and
- * order of objects in pack</li>
- * </ul>
- * Typical usage consists of creating instance intended for some pack,
- * configuring options, preparing the list of objects by calling
- * {@link #preparePack(Iterator)} or
- * {@link #preparePack(Collection, Collection)}, and finally
- * producing the stream with {@link #writePack(OutputStream)}.
- * </p>
- * <p>
- * Class provide set of configurable options and {@link ProgressMonitor}
- * support, as operations may take a long time for big repositories. Deltas
- * searching algorithm is <b>NOT IMPLEMENTED</b> yet - this implementation
- * relies only on deltas and objects reuse.
- * </p>
- * <p>
- * This class is not thread safe, it is intended to be used in one thread, with
- * one instance per created pack. Subsequent calls to writePack result in
- * undefined behavior.
- * </p>
- */
-public class PackWriter {
- /**
- * Title of {@link ProgressMonitor} task used during counting objects to
- * pack.
- *
- * @see #preparePack(Collection, Collection)
- */
- public static final String COUNTING_OBJECTS_PROGRESS = JGitText.get().countingObjects;
-
- /**
- * Title of {@link ProgressMonitor} task used during searching for objects
- * reuse or delta reuse.
- *
- * @see #writePack(OutputStream)
- */
- public static final String SEARCHING_REUSE_PROGRESS = JGitText.get().compressingObjects;
-
- /**
- * Title of {@link ProgressMonitor} task used during writing out pack
- * (objects)
- *
- * @see #writePack(OutputStream)
- */
- public static final String WRITING_OBJECTS_PROGRESS = JGitText.get().writingObjects;
-
- /**
- * Default value of deltas reuse option.
- *
- * @see #setReuseDeltas(boolean)
- */
- public static final boolean DEFAULT_REUSE_DELTAS = true;
-
- /**
- * Default value of objects reuse option.
- *
- * @see #setReuseObjects(boolean)
- */
- public static final boolean DEFAULT_REUSE_OBJECTS = true;
-
- /**
- * Default value of delta base as offset option.
- *
- * @see #setDeltaBaseAsOffset(boolean)
- */
- public static final boolean DEFAULT_DELTA_BASE_AS_OFFSET = false;
-
- /**
- * Default value of maximum delta chain depth.
- *
- * @see #setMaxDeltaDepth(int)
- */
- public static final int DEFAULT_MAX_DELTA_DEPTH = 50;
-
- private static final int PACK_VERSION_GENERATED = 2;
-
- @SuppressWarnings("unchecked")
- private final List<ObjectToPack> objectsLists[] = new List[Constants.OBJ_TAG + 1];
- {
- objectsLists[0] = Collections.<ObjectToPack> emptyList();
- objectsLists[Constants.OBJ_COMMIT] = new ArrayList<ObjectToPack>();
- objectsLists[Constants.OBJ_TREE] = new ArrayList<ObjectToPack>();
- objectsLists[Constants.OBJ_BLOB] = new ArrayList<ObjectToPack>();
- objectsLists[Constants.OBJ_TAG] = new ArrayList<ObjectToPack>();
- }
-
- private final ObjectIdSubclassMap<ObjectToPack> objectsMap = new ObjectIdSubclassMap<ObjectToPack>();
-
- // edge objects for thin packs
- private final ObjectIdSubclassMap<ObjectId> edgeObjects = new ObjectIdSubclassMap<ObjectId>();
-
- private final Repository db;
-
- private PackOutputStream out;
-
- private final Deflater deflater;
-
- private ProgressMonitor initMonitor;
-
- private ProgressMonitor writeMonitor;
-
- private final byte[] buf = new byte[16384]; // 16 KB
-
- private final WindowCursor windowCursor = new WindowCursor();
-
- private List<ObjectToPack> sortedByName;
-
- private byte packcsum[];
-
- private boolean reuseDeltas = DEFAULT_REUSE_DELTAS;
-
- private boolean reuseObjects = DEFAULT_REUSE_OBJECTS;
-
- private boolean deltaBaseAsOffset = DEFAULT_DELTA_BASE_AS_OFFSET;
-
- private int maxDeltaDepth = DEFAULT_MAX_DELTA_DEPTH;
-
- private int outputVersion;
-
- private boolean thin;
-
- private boolean ignoreMissingUninteresting = true;
-
- /**
- * Create writer for specified repository.
- * <p>
- * Objects for packing are specified in {@link #preparePack(Iterator)} or
- * {@link #preparePack(Collection, Collection)}.
- *
- * @param repo
- * repository where objects are stored.
- * @param monitor
- * operations progress monitor, used within
- * {@link #preparePack(Iterator)},
- * {@link #preparePack(Collection, Collection)}
- * , or {@link #writePack(OutputStream)}.
- */
- public PackWriter(final Repository repo, final ProgressMonitor monitor) {
- this(repo, monitor, monitor);
- }
-
- /**
- * Create writer for specified repository.
- * <p>
- * Objects for packing are specified in {@link #preparePack(Iterator)} or
- * {@link #preparePack(Collection, Collection)}.
- *
- * @param repo
- * repository where objects are stored.
- * @param imonitor
- * operations progress monitor, used within
- * {@link #preparePack(Iterator)},
- * {@link #preparePack(Collection, Collection)}
- * @param wmonitor
- * operations progress monitor, used within
- * {@link #writePack(OutputStream)}.
- */
- public PackWriter(final Repository repo, final ProgressMonitor imonitor,
- final ProgressMonitor wmonitor) {
- this.db = repo;
- initMonitor = imonitor == null ? NullProgressMonitor.INSTANCE : imonitor;
- writeMonitor = wmonitor == null ? NullProgressMonitor.INSTANCE : wmonitor;
- this.deflater = new Deflater(db.getConfig().getCore().getCompression());
- outputVersion = repo.getConfig().getCore().getPackIndexVersion();
- }
-
- /**
- * Check whether object is configured to reuse deltas existing in
- * repository.
- * <p>
- * Default setting: {@value #DEFAULT_REUSE_DELTAS}
- * </p>
- *
- * @return true if object is configured to reuse deltas; false otherwise.
- */
- public boolean isReuseDeltas() {
- return reuseDeltas;
- }
-
- /**
- * Set reuse deltas configuration option for this writer. When enabled,
- * writer will search for delta representation of object in repository and
- * use it if possible. Normally, only deltas with base to another object
- * existing in set of objects to pack will be used. Exception is however
- * thin-pack (see
- * {@link #preparePack(Collection, Collection)} and
- * {@link #preparePack(Iterator)}) where base object must exist on other
- * side machine.
- * <p>
- * When raw delta data is directly copied from a pack file, checksum is
- * computed to verify data.
- * </p>
- * <p>
- * Default setting: {@value #DEFAULT_REUSE_DELTAS}
- * </p>
- *
- * @param reuseDeltas
- * boolean indicating whether or not try to reuse deltas.
- */
- public void setReuseDeltas(boolean reuseDeltas) {
- this.reuseDeltas = reuseDeltas;
- }
-
- /**
- * Checks whether object is configured to reuse existing objects
- * representation in repository.
- * <p>
- * Default setting: {@value #DEFAULT_REUSE_OBJECTS}
- * </p>
- *
- * @return true if writer is configured to reuse objects representation from
- * pack; false otherwise.
- */
- public boolean isReuseObjects() {
- return reuseObjects;
- }
-
- /**
- * Set reuse objects configuration option for this writer. If enabled,
- * writer searches for representation in a pack file. If possible,
- * compressed data is directly copied from such a pack file. Data checksum
- * is verified.
- * <p>
- * Default setting: {@value #DEFAULT_REUSE_OBJECTS}
- * </p>
- *
- * @param reuseObjects
- * boolean indicating whether or not writer should reuse existing
- * objects representation.
- */
- public void setReuseObjects(boolean reuseObjects) {
- this.reuseObjects = reuseObjects;
- }
-
- /**
- * Check whether writer can store delta base as an offset (new style
- * reducing pack size) or should store it as an object id (legacy style,
- * compatible with old readers).
- * <p>
- * Default setting: {@value #DEFAULT_DELTA_BASE_AS_OFFSET}
- * </p>
- *
- * @return true if delta base is stored as an offset; false if it is stored
- * as an object id.
- */
- public boolean isDeltaBaseAsOffset() {
- return deltaBaseAsOffset;
- }
-
- /**
- * Set writer delta base format. Delta base can be written as an offset in a
- * pack file (new approach reducing file size) or as an object id (legacy
- * approach, compatible with old readers).
- * <p>
- * Default setting: {@value #DEFAULT_DELTA_BASE_AS_OFFSET}
- * </p>
- *
- * @param deltaBaseAsOffset
- * boolean indicating whether delta base can be stored as an
- * offset.
- */
- public void setDeltaBaseAsOffset(boolean deltaBaseAsOffset) {
- this.deltaBaseAsOffset = deltaBaseAsOffset;
- }
-
- /**
- * Get maximum depth of delta chain set up for this writer. Generated chains
- * are not longer than this value.
- * <p>
- * Default setting: {@value #DEFAULT_MAX_DELTA_DEPTH}
- * </p>
- *
- * @return maximum delta chain depth.
- */
- public int getMaxDeltaDepth() {
- return maxDeltaDepth;
- }
-
- /**
- * Set up maximum depth of delta chain for this writer. Generated chains are
- * not longer than this value. Too low value causes low compression level,
- * while too big makes unpacking (reading) longer.
- * <p>
- * Default setting: {@value #DEFAULT_MAX_DELTA_DEPTH}
- * </p>
- *
- * @param maxDeltaDepth
- * maximum delta chain depth.
- */
- public void setMaxDeltaDepth(int maxDeltaDepth) {
- this.maxDeltaDepth = maxDeltaDepth;
- }
-
- /** @return true if this writer is producing a thin pack. */
- public boolean isThin() {
- return thin;
- }
-
- /**
- * @param packthin
- * a boolean indicating whether writer may pack objects with
- * delta base object not within set of objects to pack, but
- * belonging to party repository (uninteresting/boundary) as
- * determined by set; this kind of pack is used only for
- * transport; true - to produce thin pack, false - otherwise.
- */
- public void setThin(final boolean packthin) {
- thin = packthin;
- }
-
- /**
- * @return true to ignore objects that are uninteresting and also not found
- * on local disk; false to throw a {@link MissingObjectException}
- * out of {@link #preparePack(Collection, Collection)} if an
- * uninteresting object is not in the source repository. By default,
- * true, permitting gracefully ignoring of uninteresting objects.
- */
- public boolean isIgnoreMissingUninteresting() {
- return ignoreMissingUninteresting;
- }
-
- /**
- * @param ignore
- * true if writer should ignore non existing uninteresting
- * objects during construction set of objects to pack; false
- * otherwise - non existing uninteresting objects may cause
- * {@link MissingObjectException}
- */
- public void setIgnoreMissingUninteresting(final boolean ignore) {
- ignoreMissingUninteresting = ignore;
- }
-
- /**
- * Set the pack index file format version this instance will create.
- *
- * @param version
- * the version to write. The special version 0 designates the
- * oldest (most compatible) format available for the objects.
- * @see PackIndexWriter
- */
- public void setIndexVersion(final int version) {
- outputVersion = version;
- }
-
- /**
- * Returns objects number in a pack file that was created by this writer.
- *
- * @return number of objects in pack.
- */
- public int getObjectsNumber() {
- return objectsMap.size();
- }
-
- /**
- * Prepare the list of objects to be written to the pack stream.
- * <p>
- * Iterator <b>exactly</b> determines which objects are included in a pack
- * and order they appear in pack (except that objects order by type is not
- * needed at input). This order should conform general rules of ordering
- * objects in git - by recency and path (type and delta-base first is
- * internally secured) and responsibility for guaranteeing this order is on
- * a caller side. Iterator must return each id of object to write exactly
- * once.
- * </p>
- * <p>
- * When iterator returns object that has {@link RevFlag#UNINTERESTING} flag,
- * this object won't be included in an output pack. Instead, it is recorded
- * as edge-object (known to remote repository) for thin-pack. In such a case
- * writer may pack objects with delta base object not within set of objects
- * to pack, but belonging to party repository - those marked with
- * {@link RevFlag#UNINTERESTING} flag. This type of pack is used only for
- * transport.
- * </p>
- *
- * @param objectsSource
- * iterator of object to store in a pack; order of objects within
- * each type is important, ordering by type is not needed;
- * allowed types for objects are {@link Constants#OBJ_COMMIT},
- * {@link Constants#OBJ_TREE}, {@link Constants#OBJ_BLOB} and
- * {@link Constants#OBJ_TAG}; objects returned by iterator may
- * be later reused by caller as object id and type are internally
- * copied in each iteration; if object returned by iterator has
- * {@link RevFlag#UNINTERESTING} flag set, it won't be included
- * in a pack, but is considered as edge-object for thin-pack.
- * @throws IOException
- * when some I/O problem occur during reading objects.
- */
- public void preparePack(final Iterator<RevObject> objectsSource)
- throws IOException {
- while (objectsSource.hasNext()) {
- addObject(objectsSource.next());
- }
- }
-
- /**
- * Prepare the list of objects to be written to the pack stream.
- * <p>
- * Basing on these 2 sets, another set of objects to put in a pack file is
- * created: this set consists of all objects reachable (ancestors) from
- * interesting objects, except uninteresting objects and their ancestors.
- * This method uses class {@link ObjectWalk} extensively to find out that
- * appropriate set of output objects and their optimal order in output pack.
- * Order is consistent with general git in-pack rules: sort by object type,
- * recency, path and delta-base first.
- * </p>
- *
- * @param interestingObjects
- * collection of objects to be marked as interesting (start
- * points of graph traversal).
- * @param uninterestingObjects
- * collection of objects to be marked as uninteresting (end
- * points of graph traversal).
- * @throws IOException
- * when some I/O problem occur during reading objects.
- */
- public void preparePack(
- final Collection<? extends ObjectId> interestingObjects,
- final Collection<? extends ObjectId> uninterestingObjects)
- throws IOException {
- ObjectWalk walker = setUpWalker(interestingObjects,
- uninterestingObjects);
- findObjectsToPack(walker);
- }
-
- /**
- * Determine if the pack file will contain the requested object.
- *
- * @param id
- * the object to test the existence of.
- * @return true if the object will appear in the output pack file.
- */
- public boolean willInclude(final AnyObjectId id) {
- return objectsMap.get(id) != null;
- }
-
- /**
- * Computes SHA-1 of lexicographically sorted objects ids written in this
- * pack, as used to name a pack file in repository.
- *
- * @return ObjectId representing SHA-1 name of a pack that was created.
- */
- public ObjectId computeName() {
- final MessageDigest md = Constants.newMessageDigest();
- for (ObjectToPack otp : sortByName()) {
- otp.copyRawTo(buf, 0);
- md.update(buf, 0, Constants.OBJECT_ID_LENGTH);
- }
- return ObjectId.fromRaw(md.digest());
- }
-
- /**
- * Create an index file to match the pack file just written.
- * <p>
- * This method can only be invoked after {@link #preparePack(Iterator)} or
- * {@link #preparePack(Collection, Collection)} has been
- * invoked and completed successfully. Writing a corresponding index is an
- * optional feature that not all pack users may require.
- *
- * @param indexStream
- * output for the index data. Caller is responsible for closing
- * this stream.
- * @throws IOException
- * the index data could not be written to the supplied stream.
- */
- public void writeIndex(final OutputStream indexStream) throws IOException {
- final List<ObjectToPack> list = sortByName();
- final PackIndexWriter iw;
- if (outputVersion <= 0)
- iw = PackIndexWriter.createOldestPossible(indexStream, list);
- else
- iw = PackIndexWriter.createVersion(indexStream, outputVersion);
- iw.write(list, packcsum);
- }
-
- private List<ObjectToPack> sortByName() {
- if (sortedByName == null) {
- sortedByName = new ArrayList<ObjectToPack>(objectsMap.size());
- for (List<ObjectToPack> list : objectsLists) {
- for (ObjectToPack otp : list)
- sortedByName.add(otp);
- }
- Collections.sort(sortedByName);
- }
- return sortedByName;
- }
-
- /**
- * Write the prepared pack to the supplied stream.
- * <p>
- * At first, this method collects and sorts objects to pack, then deltas
- * search is performed if set up accordingly, finally pack stream is
- * written. {@link ProgressMonitor} tasks {@value #SEARCHING_REUSE_PROGRESS}
- * (only if reuseDeltas or reuseObjects is enabled) and
- * {@value #WRITING_OBJECTS_PROGRESS} are updated during packing.
- * </p>
- * <p>
- * All reused objects data checksum (Adler32/CRC32) is computed and
- * validated against existing checksum.
- * </p>
- *
- * @param packStream
- * output stream of pack data. The stream should be buffered by
- * the caller. The caller is responsible for closing the stream.
- * @throws IOException
- * an error occurred reading a local object's data to include in
- * the pack, or writing compressed object data to the output
- * stream.
- */
- public void writePack(OutputStream packStream) throws IOException {
- if (reuseDeltas || reuseObjects)
- searchForReuse();
-
- out = new PackOutputStream(packStream);
-
- writeMonitor.beginTask(WRITING_OBJECTS_PROGRESS, getObjectsNumber());
- writeHeader();
- writeObjects();
- writeChecksum();
-
- windowCursor.release();
- writeMonitor.endTask();
- }
-
- private void searchForReuse() throws IOException {
- initMonitor.beginTask(SEARCHING_REUSE_PROGRESS, getObjectsNumber());
- final Collection<PackedObjectLoader> reuseLoaders = new ArrayList<PackedObjectLoader>();
- for (List<ObjectToPack> list : objectsLists) {
- for (ObjectToPack otp : list) {
- if (initMonitor.isCancelled())
- throw new IOException(
- JGitText.get().packingCancelledDuringObjectsWriting);
- reuseLoaders.clear();
- searchForReuse(reuseLoaders, otp);
- initMonitor.update(1);
- }
- }
-
- initMonitor.endTask();
- }
-
- private void searchForReuse(
- final Collection<PackedObjectLoader> reuseLoaders,
- final ObjectToPack otp) throws IOException {
- db.openObjectInAllPacks(otp, reuseLoaders, windowCursor);
- if (reuseDeltas) {
- selectDeltaReuseForObject(otp, reuseLoaders);
- }
- // delta reuse is preferred over object reuse
- if (reuseObjects && !otp.isCopyable()) {
- selectObjectReuseForObject(otp, reuseLoaders);
- }
- }
-
- private void selectDeltaReuseForObject(final ObjectToPack otp,
- final Collection<PackedObjectLoader> loaders) throws IOException {
- PackedObjectLoader bestLoader = null;
- ObjectId bestBase = null;
-
- for (PackedObjectLoader loader : loaders) {
- ObjectId idBase = loader.getDeltaBase();
- if (idBase == null)
- continue;
- ObjectToPack otpBase = objectsMap.get(idBase);
-
- // only if base is in set of objects to write or thin-pack's edge
- if ((otpBase != null || (thin && edgeObjects.get(idBase) != null))
- // select smallest possible delta if > 1 available
- && isBetterDeltaReuseLoader(bestLoader, loader)) {
- bestLoader = loader;
- bestBase = (otpBase != null ? otpBase : idBase);
- }
- }
-
- if (bestLoader != null) {
- otp.setCopyFromPack(bestLoader);
- otp.setDeltaBase(bestBase);
- }
- }
-
- private static boolean isBetterDeltaReuseLoader(
- PackedObjectLoader currentLoader, PackedObjectLoader loader)
- throws IOException {
- if (currentLoader == null)
- return true;
- if (loader.getRawSize() < currentLoader.getRawSize())
- return true;
- return (loader.getRawSize() == currentLoader.getRawSize()
- && loader.supportsFastCopyRawData() && !currentLoader
- .supportsFastCopyRawData());
- }
-
- private void selectObjectReuseForObject(final ObjectToPack otp,
- final Collection<PackedObjectLoader> loaders) {
- for (final PackedObjectLoader loader : loaders) {
- if (loader instanceof WholePackedObjectLoader) {
- otp.setCopyFromPack(loader);
- return;
- }
- }
- }
-
- private void writeHeader() throws IOException {
- System.arraycopy(Constants.PACK_SIGNATURE, 0, buf, 0, 4);
- NB.encodeInt32(buf, 4, PACK_VERSION_GENERATED);
- NB.encodeInt32(buf, 8, getObjectsNumber());
- out.write(buf, 0, 12);
- }
-
- private void writeObjects() throws IOException {
- for (List<ObjectToPack> list : objectsLists) {
- for (ObjectToPack otp : list) {
- if (writeMonitor.isCancelled())
- throw new IOException(
- JGitText.get().packingCancelledDuringObjectsWriting);
- if (!otp.isWritten())
- writeObject(otp);
- }
- }
- }
-
- private void writeObject(final ObjectToPack otp) throws IOException {
- otp.markWantWrite();
- if (otp.isDeltaRepresentation()) {
- ObjectToPack deltaBase = otp.getDeltaBase();
- assert deltaBase != null || thin;
- if (deltaBase != null && !deltaBase.isWritten()) {
- if (deltaBase.wantWrite()) {
- otp.clearDeltaBase(); // cycle detected
- otp.clearSourcePack();
- } else {
- writeObject(deltaBase);
- }
- }
- }
-
- assert !otp.isWritten();
-
- out.resetCRC32();
- otp.setOffset(out.length());
-
- final PackedObjectLoader reuse = open(otp);
- if (reuse != null) {
- try {
- if (otp.isDeltaRepresentation())
- writeDeltaObjectHeader(otp, reuse);
- else
- writeObjectHeader(otp.getType(), reuse.getSize());
- reuse.copyRawData(out, buf, windowCursor);
- } finally {
- reuse.endCopyRawData();
- }
- } else if (otp.isDeltaRepresentation()) {
- throw new IOException(JGitText.get().creatingDeltasIsNotImplemented);
- } else {
- writeWholeObjectDeflate(otp);
- }
- otp.setCRC(out.getCRC32());
-
- writeMonitor.update(1);
- }
-
- private PackedObjectLoader open(final ObjectToPack otp) throws IOException {
- while (otp.isCopyable()) {
- try {
- PackedObjectLoader reuse = otp.getCopyLoader(windowCursor);
- reuse.beginCopyRawData();
- return reuse;
- } catch (IOException err) {
- // The pack we found the object in originally is gone, or
- // it has been overwritten with a different layout.
- //
- otp.clearDeltaBase();
- otp.clearSourcePack();
- searchForReuse(new ArrayList<PackedObjectLoader>(), otp);
- continue;
- }
- }
- return null;
- }
-
- private void writeWholeObjectDeflate(final ObjectToPack otp)
- throws IOException {
- final ObjectLoader loader = db.openObject(windowCursor, otp);
- final byte[] data = loader.getCachedBytes();
- writeObjectHeader(otp.getType(), data.length);
- deflater.reset();
- deflater.setInput(data, 0, data.length);
- deflater.finish();
- do {
- final int n = deflater.deflate(buf, 0, buf.length);
- if (n > 0)
- out.write(buf, 0, n);
- } while (!deflater.finished());
- }
-
- private void writeDeltaObjectHeader(final ObjectToPack otp,
- final PackedObjectLoader reuse) throws IOException {
- if (deltaBaseAsOffset && otp.getDeltaBase() != null) {
- writeObjectHeader(Constants.OBJ_OFS_DELTA, reuse.getRawSize());
-
- final ObjectToPack deltaBase = otp.getDeltaBase();
- long offsetDiff = otp.getOffset() - deltaBase.getOffset();
- int pos = buf.length - 1;
- buf[pos] = (byte) (offsetDiff & 0x7F);
- while ((offsetDiff >>= 7) > 0) {
- buf[--pos] = (byte) (0x80 | (--offsetDiff & 0x7F));
- }
-
- out.write(buf, pos, buf.length - pos);
- } else {
- writeObjectHeader(Constants.OBJ_REF_DELTA, reuse.getRawSize());
- otp.getDeltaBaseId().copyRawTo(buf, 0);
- out.write(buf, 0, Constants.OBJECT_ID_LENGTH);
- }
- }
-
- private void writeObjectHeader(final int objectType, long dataLength)
- throws IOException {
- long nextLength = dataLength >>> 4;
- int size = 0;
- buf[size++] = (byte) ((nextLength > 0 ? 0x80 : 0x00)
- | (objectType << 4) | (dataLength & 0x0F));
- dataLength = nextLength;
- while (dataLength > 0) {
- nextLength >>>= 7;
- buf[size++] = (byte) ((nextLength > 0 ? 0x80 : 0x00) | (dataLength & 0x7F));
- dataLength = nextLength;
- }
- out.write(buf, 0, size);
- }
-
- private void writeChecksum() throws IOException {
- packcsum = out.getDigest();
- out.write(packcsum);
- }
-
- private ObjectWalk setUpWalker(
- final Collection<? extends ObjectId> interestingObjects,
- final Collection<? extends ObjectId> uninterestingObjects)
- throws MissingObjectException, IOException,
- IncorrectObjectTypeException {
- final ObjectWalk walker = new ObjectWalk(db);
- walker.setRetainBody(false);
- walker.sort(RevSort.TOPO);
- walker.sort(RevSort.COMMIT_TIME_DESC, true);
- if (thin)
- walker.sort(RevSort.BOUNDARY, true);
-
- for (ObjectId id : interestingObjects) {
- RevObject o = walker.parseAny(id);
- walker.markStart(o);
- }
- if (uninterestingObjects != null) {
- for (ObjectId id : uninterestingObjects) {
- final RevObject o;
- try {
- o = walker.parseAny(id);
- } catch (MissingObjectException x) {
- if (ignoreMissingUninteresting)
- continue;
- throw x;
- }
- walker.markUninteresting(o);
- }
- }
- return walker;
- }
-
- private void findObjectsToPack(final ObjectWalk walker)
- throws MissingObjectException, IncorrectObjectTypeException,
- IOException {
- initMonitor.beginTask(COUNTING_OBJECTS_PROGRESS,
- ProgressMonitor.UNKNOWN);
- RevObject o;
-
- while ((o = walker.next()) != null) {
- addObject(o);
- initMonitor.update(1);
- }
- while ((o = walker.nextObject()) != null) {
- addObject(o);
- initMonitor.update(1);
- }
- initMonitor.endTask();
- }
-
- /**
- * Include one object to the output file.
- * <p>
- * Objects are written in the order they are added. If the same object is
- * added twice, it may be written twice, creating a larger than necessary
- * file.
- *
- * @param object
- * the object to add.
- * @throws IncorrectObjectTypeException
- * the object is an unsupported type.
- */
- public void addObject(final RevObject object)
- throws IncorrectObjectTypeException {
- if (object.has(RevFlag.UNINTERESTING)) {
- edgeObjects.add(object);
- thin = true;
- return;
- }
-
- final ObjectToPack otp = new ObjectToPack(object, object.getType());
- try {
- objectsLists[object.getType()].add(otp);
- } catch (ArrayIndexOutOfBoundsException x) {
- throw new IncorrectObjectTypeException(object,
- JGitText.get().incorrectObjectType_COMMITnorTREEnorBLOBnorTAG);
- } catch (UnsupportedOperationException x) {
- // index pointing to "dummy" empty list
- throw new IncorrectObjectTypeException(object,
- JGitText.get().incorrectObjectType_COMMITnorTREEnorBLOBnorTAG);
- }
- objectsMap.add(otp);
- }
-
- /**
- * Class holding information about object that is going to be packed by
- * {@link PackWriter}. Information include object representation in a
- * pack-file and object status.
- *
- */
- static class ObjectToPack extends PackedObjectInfo {
- /** Other object being packed that this will delta against. */
- private ObjectId deltaBase;
-
- /** Pack to reuse compressed data from, otherwise null. */
- private PackFile copyFromPack;
-
- /** Offset of the object's header in {@link #copyFromPack}. */
- private long copyOffset;
-
- /**
- * Bit field, from bit 0 to bit 31:
- * <ul>
- * <li>1 bit: wantWrite</li>
- * <li>3 bits: type</li>
- * <li>28 bits: deltaDepth</li>
- * </ul>
- */
- private int flags;
-
- /**
- * Construct object for specified object id. <br/> By default object is
- * marked as not written and non-delta packed (as a whole object).
- *
- * @param src
- * object id of object for packing
- * @param type
- * real type code of the object, not its in-pack type.
- */
- ObjectToPack(AnyObjectId src, final int type) {
- super(src);
- flags |= type << 1;
- }
-
- /**
- * @return delta base object id if object is going to be packed in delta
- * representation; null otherwise - if going to be packed as a
- * whole object.
- */
- ObjectId getDeltaBaseId() {
- return deltaBase;
- }
-
- /**
- * @return delta base object to pack if object is going to be packed in
- * delta representation and delta is specified as object to
- * pack; null otherwise - if going to be packed as a whole
- * object or delta base is specified only as id.
- */
- ObjectToPack getDeltaBase() {
- if (deltaBase instanceof ObjectToPack)
- return (ObjectToPack) deltaBase;
- return null;
- }
-
- /**
- * Set delta base for the object. Delta base set by this method is used
- * by {@link PackWriter} to write object - determines its representation
- * in a created pack.
- *
- * @param deltaBase
- * delta base object or null if object should be packed as a
- * whole object.
- *
- */
- void setDeltaBase(ObjectId deltaBase) {
- this.deltaBase = deltaBase;
- }
-
- void clearDeltaBase() {
- this.deltaBase = null;
- }
-
- /**
- * @return true if object is going to be written as delta; false
- * otherwise.
- */
- boolean isDeltaRepresentation() {
- return deltaBase != null;
- }
-
- /**
- * Check if object is already written in a pack. This information is
- * used to achieve delta-base precedence in a pack file.
- *
- * @return true if object is already written; false otherwise.
- */
- boolean isWritten() {
- return getOffset() != 0;
- }
-
- boolean isCopyable() {
- return copyFromPack != null;
- }
-
- PackedObjectLoader getCopyLoader(WindowCursor curs) throws IOException {
- return copyFromPack.resolveBase(curs, copyOffset);
- }
-
- void setCopyFromPack(PackedObjectLoader loader) {
- this.copyFromPack = loader.pack;
- this.copyOffset = loader.objectOffset;
- }
-
- void clearSourcePack() {
- copyFromPack = null;
- }
-
- int getType() {
- return (flags>>1) & 0x7;
- }
-
- int getDeltaDepth() {
- return flags >>> 4;
- }
-
- void updateDeltaDepth() {
- final int d;
- if (deltaBase instanceof ObjectToPack)
- d = ((ObjectToPack) deltaBase).getDeltaDepth() + 1;
- else if (deltaBase != null)
- d = 1;
- else
- d = 0;
- flags = (d << 4) | flags & 0x15;
- }
-
- boolean wantWrite() {
- return (flags & 1) == 1;
- }
-
- void markWantWrite() {
- flags |= 1;
- }
- }
-}
diff --git a/org.eclipse.jgit/src/org/eclipse/jgit/lib/PackedObjectLoader.java b/org.eclipse.jgit/src/org/eclipse/jgit/lib/PackedObjectLoader.java
deleted file mode 100644
index 026b008..0000000
--- a/org.eclipse.jgit/src/org/eclipse/jgit/lib/PackedObjectLoader.java
+++ /dev/null
@@ -1,188 +0,0 @@
-/*
- * Copyright (C) 2009, Google Inc.
- * Copyright (C) 2008, Marek Zawirski <marek.zawirski@gmail.com>
- * Copyright (C) 2007, Robin Rosenberg <robin.rosenberg@dewire.com>
- * Copyright (C) 2006-2008, Shawn O. Pearce <spearce@spearce.org>
- * and other copyright owners as documented in the project's IP log.
- *
- * This program and the accompanying materials are made available
- * under the terms of the Eclipse Distribution License v1.0 which
- * accompanies this distribution, is reproduced below, and is
- * available at http://www.eclipse.org/org/documents/edl-v10.php
- *
- * All rights reserved.
- *
- * Redistribution and use in source and binary forms, with or
- * without modification, are permitted provided that the following
- * conditions are met:
- *
- * - Redistributions of source code must retain the above copyright
- * notice, this list of conditions and the following disclaimer.
- *
- * - Redistributions in binary form must reproduce the above
- * copyright notice, this list of conditions and the following
- * disclaimer in the documentation and/or other materials provided
- * with the distribution.
- *
- * - Neither the name of the Eclipse Foundation, Inc. nor the
- * names of its contributors may be used to endorse or promote
- * products derived from this software without specific prior
- * written permission.
- *
- * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND
- * CONTRIBUTORS "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES,
- * INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
- * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
- * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR
- * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
- * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT
- * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
- * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER
- * CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT,
- * STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
- * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF
- * ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
- */
-
-package org.eclipse.jgit.lib;
-
-import java.io.IOException;
-import java.io.OutputStream;
-
-/**
- * Base class for a set of object loader classes for packed objects.
- */
-abstract class PackedObjectLoader extends ObjectLoader {
- protected final PackFile pack;
-
- /** Position of the first byte of the object's header. */
- protected final long objectOffset;
-
- /** Bytes used to express the object header, including delta reference. */
- protected final int headerSize;
-
- protected int objectType;
-
- protected int objectSize;
-
- protected byte[] cachedBytes;
-
- PackedObjectLoader(final PackFile pr, final long objectOffset,
- final int headerSize) {
- pack = pr;
- this.objectOffset = objectOffset;
- this.headerSize = headerSize;
- }
-
- /**
- * Force this object to be loaded into memory and pinned in this loader.
- * <p>
- * Once materialized, subsequent get operations for the following methods
- * will always succeed without raising an exception, as all information is
- * pinned in memory by this loader instance.
- * <ul>
- * <li>{@link #getType()}</li>
- * <li>{@link #getSize()}</li>
- * <li>{@link #getBytes()}, {@link #getCachedBytes}</li>
- * <li>{@link #getRawSize()}</li>
- * <li>{@link #getRawType()}</li>
- * </ul>
- *
- * @param curs
- * temporary thread storage during data access.
- * @throws IOException
- * the object cannot be read.
- */
- public abstract void materialize(WindowCursor curs) throws IOException;
-
- public final int getType() {
- return objectType;
- }
-
- public final long getSize() {
- return objectSize;
- }
-
- @Override
- public final byte[] getCachedBytes() {
- return cachedBytes;
- }
-
- /**
- * @return offset of object header within pack file
- */
- public final long getObjectOffset() {
- return objectOffset;
- }
-
- /**
- * Peg the pack file open to support data copying.
- * <p>
- * Applications trying to copy raw pack data should ensure the pack stays
- * open and available throughout the entire copy. To do that use:
- *
- * <pre>
- * loader.beginCopyRawData();
- * try {
- * loader.copyRawData(out, tmpbuf, curs);
- * } finally {
- * loader.endCopyRawData();
- * }
- * </pre>
- *
- * @throws IOException
- * this loader contains stale information and cannot be used.
- * The most likely cause is the underlying pack file has been
- * deleted, and the object has moved to another pack file.
- */
- public void beginCopyRawData() throws IOException {
- pack.beginCopyRawData();
- }
-
- /**
- * Copy raw object representation from storage to provided output stream.
- * <p>
- * Copied data doesn't include object header. User must provide temporary
- * buffer used during copying by underlying I/O layer.
- * </p>
- *
- * @param out
- * output stream when data is copied. No buffering is guaranteed.
- * @param buf
- * temporary buffer used during copying. Recommended size is at
- * least few kB.
- * @param curs
- * temporary thread storage during data access.
- * @throws IOException
- * when the object cannot be read.
- * @see #beginCopyRawData()
- */
- public void copyRawData(OutputStream out, byte buf[], WindowCursor curs)
- throws IOException {
- pack.copyRawData(this, out, buf, curs);
- }
-
- /** Release resources after {@link #beginCopyRawData()}. */
- public void endCopyRawData() {
- pack.endCopyRawData();
- }
-
- /**
- * @return true if this loader is capable of fast raw-data copying basing on
- * compressed data checksum; false if raw-data copying needs
- * uncompressing and compressing data
- * @throws IOException
- * the index file format cannot be determined.
- */
- public boolean supportsFastCopyRawData() throws IOException {
- return pack.supportsFastCopyRawData();
- }
-
- /**
- * @return id of delta base object for this object representation. null if
- * object is not stored as delta.
- * @throws IOException
- * when delta base cannot read.
- */
- public abstract ObjectId getDeltaBase() throws IOException;
-}
diff --git a/org.eclipse.jgit/src/org/eclipse/jgit/lib/PersonIdent.java b/org.eclipse.jgit/src/org/eclipse/jgit/lib/PersonIdent.java
index 522f847..0406684 100644
--- a/org.eclipse.jgit/src/org/eclipse/jgit/lib/PersonIdent.java
+++ b/org.eclipse.jgit/src/org/eclipse/jgit/lib/PersonIdent.java
@@ -77,7 +77,7 @@ public class PersonIdent {
* @param repo
*/
public PersonIdent(final Repository repo) {
- final RepositoryConfig config = repo.getConfig();
+ final UserConfig config = repo.getConfig().get(UserConfig.KEY);
name = config.getCommitterName();
emailAddress = config.getCommitterEmail();
when = SystemReader.getInstance().getCurrentTime();
diff --git a/org.eclipse.jgit/src/org/eclipse/jgit/lib/RefUpdate.java b/org.eclipse.jgit/src/org/eclipse/jgit/lib/RefUpdate.java
index e04a587..e6f8933 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 Result forceUpdate() throws IOException {
* 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 @@ Result execute(Result status) throws IOException {
* @throws IOException
*/
public Result delete() throws IOException {
- return delete(new RevWalk(getRepository()));
+ RevWalk rw = new RevWalk(getRepository());
+ try {
+ return delete(rw);
+ } finally {
+ rw.release();
+ }
}
/**
diff --git a/org.eclipse.jgit/src/org/eclipse/jgit/lib/RefWriter.java b/org.eclipse.jgit/src/org/eclipse/jgit/lib/RefWriter.java
index f273824..4acb3ec 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.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/RefsChangedEvent.java b/org.eclipse.jgit/src/org/eclipse/jgit/lib/RefsChangedEvent.java
deleted file mode 100644
index 705c613..0000000
--- a/org.eclipse.jgit/src/org/eclipse/jgit/lib/RefsChangedEvent.java
+++ /dev/null
@@ -1,67 +0,0 @@
-/*
- * Copyright (C) 2008, Robin Rosenberg <robin.rosenberg@dewire.com>
- * and other copyright owners as documented in the project's IP log.
- *
- * This program and the accompanying materials are made available
- * under the terms of the Eclipse Distribution License v1.0 which
- * accompanies this distribution, is reproduced below, and is
- * available at http://www.eclipse.org/org/documents/edl-v10.php
- *
- * All rights reserved.
- *
- * Redistribution and use in source and binary forms, with or
- * without modification, are permitted provided that the following
- * conditions are met:
- *
- * - Redistributions of source code must retain the above copyright
- * notice, this list of conditions and the following disclaimer.
- *
- * - Redistributions in binary form must reproduce the above
- * copyright notice, this list of conditions and the following
- * disclaimer in the documentation and/or other materials provided
- * with the distribution.
- *
- * - Neither the name of the Eclipse Foundation, Inc. nor the
- * names of its contributors may be used to endorse or promote
- * products derived from this software without specific prior
- * written permission.
- *
- * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND
- * CONTRIBUTORS "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES,
- * INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
- * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
- * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR
- * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
- * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT
- * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
- * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER
- * CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT,
- * STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
- * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF
- * ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
- */
-
-package org.eclipse.jgit.lib;
-
-/**
- * This class passes information about a changed Git index to a
- * {@link RepositoryListener}
- *
- * Currently only a reference to the repository is passed.
- */
-public class RefsChangedEvent extends RepositoryChangedEvent {
- /**
- * Create an event describing reference changes in a repository.
- *
- * @param repository
- * the repository whose references recently changed.
- */
- public RefsChangedEvent(final Repository repository) {
- super(repository);
- }
-
- @Override
- public String toString() {
- return "RefsChangedEvent[" + getRepository() + "]";
- }
-}
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 62e1578..27fdf68 100644
--- a/org.eclipse.jgit/src/org/eclipse/jgit/lib/Repository.java
+++ b/org.eclipse.jgit/src/org/eclipse/jgit/lib/Repository.java
@@ -46,12 +46,11 @@
package org.eclipse.jgit.lib;
+import java.io.BufferedOutputStream;
import java.io.File;
import java.io.FileNotFoundException;
+import java.io.FileOutputStream;
import java.io.IOException;
-import java.text.MessageFormat;
-import java.util.ArrayList;
-import java.util.Collection;
import java.util.Collections;
import java.util.HashMap;
import java.util.HashSet;
@@ -59,279 +58,103 @@
import java.util.List;
import java.util.Map;
import java.util.Set;
-import java.util.Vector;
import java.util.concurrent.atomic.AtomicInteger;
import org.eclipse.jgit.JGitText;
import org.eclipse.jgit.dircache.DirCache;
-import org.eclipse.jgit.errors.ConfigInvalidException;
+import org.eclipse.jgit.errors.CorruptObjectException;
import org.eclipse.jgit.errors.IncorrectObjectTypeException;
+import org.eclipse.jgit.errors.MissingObjectException;
+import org.eclipse.jgit.errors.NoWorkTreeException;
import org.eclipse.jgit.errors.RevisionSyntaxException;
+import org.eclipse.jgit.events.ListenerList;
+import org.eclipse.jgit.events.RepositoryEvent;
+import org.eclipse.jgit.revwalk.RevBlob;
+import org.eclipse.jgit.revwalk.RevCommit;
+import org.eclipse.jgit.revwalk.RevObject;
+import org.eclipse.jgit.revwalk.RevWalk;
+import org.eclipse.jgit.storage.file.ReflogReader;
import org.eclipse.jgit.util.FS;
import org.eclipse.jgit.util.IO;
import org.eclipse.jgit.util.RawParseUtils;
-import org.eclipse.jgit.util.SystemReader;
/**
- * Represents a Git repository. A repository holds all objects and refs used for
- * managing source code (could by any type of file, but source code is what
- * SCM's are typically used for).
- *
- * In Git terms all data is stored in GIT_DIR, typically a directory called
- * .git. A work tree is maintained unless the repository is a bare repository.
- * Typically the .git directory is located at the root of the work dir.
- *
- * <ul>
- * <li>GIT_DIR
- * <ul>
- * <li>objects/ - objects</li>
- * <li>refs/ - tags and heads</li>
- * <li>config - configuration</li>
- * <li>info/ - more configurations</li>
- * </ul>
- * </li>
- * </ul>
+ * Represents a Git repository.
+ * <p>
+ * A repository holds all objects and refs used for managing source code (could
+ * be any type of file, but source code is what SCM's are typically used for).
* <p>
* This class is thread-safe.
- * <p>
- * This implementation only handles a subtly undocumented subset of git features.
- *
*/
-public class Repository {
+public abstract class Repository {
+ private static final ListenerList globalListeners = new ListenerList();
+
+ /** @return the global listener list observing all events in this JVM. */
+ public static ListenerList getGlobalListenerList() {
+ return globalListeners;
+ }
+
private final AtomicInteger useCnt = new AtomicInteger(1);
+ /** Metadata directory holding the repository's critical files. */
private final File gitDir;
+ /** File abstraction used to resolve paths. */
private final FS fs;
- private final FileBasedConfig userConfig;
-
- private final RepositoryConfig config;
-
- private final RefDatabase refs;
-
- private final ObjectDirectory objectDatabase;
-
private GitIndex index;
- private final List<RepositoryListener> listeners = new Vector<RepositoryListener>(); // thread safe
- static private final List<RepositoryListener> allListeners = new Vector<RepositoryListener>(); // thread safe
+ private final ListenerList myListeners = new ListenerList();
- private File workDir;
+ /** If not bare, the top level directory of the working files. */
+ private final File workTree;
- private File indexFile;
+ /** If not bare, the index file caching the working file states. */
+ private final File indexFile;
/**
- * Construct a representation of a Git repository.
+ * Initialize a new repository instance.
*
- * 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.
+ * @param options
+ * options to configure the repository.
*/
- public Repository(final File d) throws IOException {
- this(d, null, null, null, null); // go figure it out
+ protected Repository(final BaseRepositoryBuilder options) {
+ gitDir = options.getGitDir();
+ fs = options.getFS();
+ workTree = options.getWorkTree();
+ indexFile = options.getIndexFile();
+ }
+
+ /** @return listeners observing only events on this repository. */
+ public ListenerList getListenerList() {
+ return myListeners;
}
/**
- * Construct a representation of a Git repository.
+ * Fire an event to all registered listeners.
+ * <p>
+ * The source repository of the event is automatically set to this
+ * repository, before the event is delivered to any listeners.
*
- * 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.
+ * @param event
+ * the event to deliver.
*/
- public Repository(final File d, final File workTree) throws IOException {
- this(d, workTree, null, null, null); // go figure it out
+ public void fireEvent(RepositoryEvent<?> event) {
+ event.setRepository(this);
+ myListeners.dispatch(event);
+ globalListeners.dispatch(event);
}
/**
- * 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);
- config = new RepositoryConfig(userConfig, fs.resolve(gitDir, "config"));
-
- loadUserConfig();
- loadConfig();
-
- if (workDir == null) {
- // if the working directory was not provided explicitly,
- // we need to decide if this is a "bare" repository or not
- // first, we check the working tree configuration
- String workTreeConfig = getConfig().getString(
- ConfigConstants.CONFIG_CORE_SECTION, null,
- ConfigConstants.CONFIG_KEY_WORKTREE);
- if (workTreeConfig != null) {
- // the working tree configuration wins
- workDir = fs.resolve(d, workTreeConfig);
- } else if (getConfig().getString(
- ConfigConstants.CONFIG_CORE_SECTION, null,
- ConfigConstants.CONFIG_KEY_BARE) != null) {
- // we have asserted that a value for the "bare" flag was set
- if (!getConfig().getBoolean(ConfigConstants.CONFIG_CORE_SECTION,
- ConfigConstants.CONFIG_KEY_BARE, true))
- // the "bare" flag is false -> use the parent of the
- // meta data directory
- workDir = gitDir.getParentFile();
- else
- // the "bare" flag is true
- workDir = null;
- } else if (Constants.DOT_GIT.equals(gitDir.getName())) {
- // no value for the "bare" flag, but the meta data directory
- // is named ".git" -> use the parent of the meta data directory
- workDir = gitDir.getParentFile();
- } else {
- workDir = null;
- }
- }
-
- refs = new RefDirectory(this);
- if (objectDir != null)
- objectDatabase = new ObjectDirectory(fs.resolve(objectDir, ""),
- alternateObjectDir, fs);
- else
- objectDatabase = new ObjectDirectory(fs.resolve(gitDir, "objects"),
- alternateObjectDir, fs);
-
- if (indexFile != null)
- this.indexFile = indexFile;
- else
- this.indexFile = new File(gitDir, "index");
-
- if (objectDatabase.exists()) {
- final String repositoryFormatVersion = getConfig().getString(
- ConfigConstants.CONFIG_CORE_SECTION, null,
- ConfigConstants.CONFIG_KEY_REPO_FORMAT_VERSION);
- if (!"0".equals(repositoryFormatVersion)) {
- throw new IOException(MessageFormat.format(
- JGitText.get().unknownRepositoryFormat2,
- repositoryFormatVersion));
- }
- }
- }
-
- private void loadUserConfig() throws IOException {
- try {
- userConfig.load();
- } catch (ConfigInvalidException e1) {
- IOException e2 = new IOException(MessageFormat.format(JGitText
- .get().userConfigFileInvalid, userConfig.getFile()
- .getAbsolutePath(), e1));
- e2.initCause(e1);
- throw e2;
- }
- }
-
- private void loadConfig() throws IOException {
- try {
- config.load();
- } catch (ConfigInvalidException e1) {
- IOException e2 = new IOException(JGitText.get().unknownRepositoryFormat);
- e2.initCause(e1);
- throw e2;
- }
- }
-
-
- /**
- * Create a new Git repository initializing the necessary files and
- * directories. Repository with working tree is created using this method.
+ * Create a new Git repository.
+ * <p>
+ * Repository with working tree is created using this method. This method is
+ * the same as {@code create(false)}.
*
* @throws IOException
* @see #create(boolean)
*/
- public synchronized void create() throws IOException {
+ public void create() throws IOException {
create(false);
}
@@ -340,44 +163,14 @@ public synchronized void create() throws IOException {
* directories.
*
* @param bare
- * if true, a bare repository is created.
- *
+ * if true, a bare repository (a repository without a working
+ * directory) is created.
* @throws IOException
* in case of IO problem
*/
- public void create(boolean bare) throws IOException {
- final RepositoryConfig cfg = getConfig();
- if (cfg.getFile().exists()) {
- throw new IllegalStateException(MessageFormat.format(
- JGitText.get().repositoryAlreadyExists, gitDir));
- }
- gitDir.mkdirs();
- refs.create();
- objectDatabase.create();
+ public abstract void create(boolean bare) throws IOException;
- 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 GIT_DIR
- */
+ /** @return local metadata directory; null if repository isn't local. */
public File getDirectory() {
return gitDir;
}
@@ -385,42 +178,30 @@ public File getDirectory() {
/**
* @return the directory containing the objects owned by this repository.
*/
- public File getObjectsDirectory() {
- return objectDatabase.getDirectory();
- }
+ public abstract File getObjectsDirectory();
/**
* @return the object database which stores this repository's data.
*/
- public ObjectDatabase getObjectDatabase() {
- return objectDatabase;
+ public abstract ObjectDatabase getObjectDatabase();
+
+ /** @return a new inserter to create objects in {@link #getObjectDatabase()} */
+ public ObjectInserter newObjectInserter() {
+ return getObjectDatabase().newInserter();
+ }
+
+ /** @return 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 RefDatabase getRefDatabase() {
- return refs;
- }
+ public abstract RefDatabase getRefDatabase();
/**
* @return the configuration of this repository
*/
- public RepositoryConfig getConfig() {
- if (userConfig.isOutdated()) {
- try {
- loadUserConfig();
- } catch (IOException e) {
- throw new RuntimeException(e);
- }
- }
- if (config.isOutdated()) {
- try {
- loadConfig();
- } catch (IOException e) {
- throw new RuntimeException(e);
- }
- }
- return config;
- }
+ public abstract StoredConfig getConfig();
/**
* @return the used file system abstraction
@@ -430,116 +211,63 @@ public FS getFS() {
}
/**
- * Construct a filename where the loose object having a specified SHA-1
- * should be stored. If the object is stored in a shared repository the path
- * to the alternative repo will be returned. If the object is not yet store
- * a usable path in this repo will be returned. It is assumed that callers
- * will look for objects in a pack first.
- *
- * @param objectId
- * @return suggested file name
- */
- public File toFile(final AnyObjectId objectId) {
- return objectDatabase.fileFor(objectId);
- }
-
- /**
* @param objectId
* @return true if the specified object is stored in this repo or any of the
* known shared repositories.
*/
- public boolean hasObject(final AnyObjectId objectId) {
- return objectDatabase.hasObject(objectId);
- }
-
- /**
- * @param id
- * SHA-1 of an object.
- *
- * @return a {@link ObjectLoader} for accessing the data of the named
- * object, or null if the object does not exist.
- * @throws IOException
- */
- public ObjectLoader openObject(final AnyObjectId id)
- throws IOException {
- final WindowCursor wc = new WindowCursor();
+ public boolean hasObject(AnyObjectId objectId) {
try {
- return openObject(wc, id);
- } finally {
- wc.release();
+ return getObjectDatabase().has(objectId);
+ } catch (IOException e) {
+ // Legacy API, assume error means "no"
+ return false;
}
}
/**
- * @param curs
- * temporary working space associated with the calling thread.
- * @param id
- * SHA-1 of an object.
- *
- * @return a {@link ObjectLoader} for accessing the data of the named
- * object, or null if the object does not exist.
- * @throws IOException
- */
- public ObjectLoader openObject(final WindowCursor curs, final AnyObjectId id)
- throws IOException {
- return objectDatabase.openObject(curs, id);
- }
-
- /**
- * Open object in all packs containing specified object.
+ * Open an object from this repository.
+ * <p>
+ * This is a one-shot call interface which may be faster than allocating a
+ * {@link #newObjectReader()} to perform the lookup.
*
* @param objectId
- * id of object to search for
- * @param curs
- * temporary working space associated with the calling thread.
- * @return collection of loaders for this object, from all packs containing
- * this object
+ * identity of the object to open.
+ * @return a {@link ObjectLoader} for accessing the object.
+ * @throws MissingObjectException
+ * the object does not exist.
* @throws IOException
+ * the object store cannot be accessed.
*/
- public Collection<PackedObjectLoader> openObjectInAllPacks(
- final AnyObjectId objectId, final WindowCursor curs)
- throws IOException {
- Collection<PackedObjectLoader> result = new LinkedList<PackedObjectLoader>();
- openObjectInAllPacks(objectId, result, curs);
- return result;
+ public ObjectLoader open(final AnyObjectId objectId)
+ throws MissingObjectException, IOException {
+ return getObjectDatabase().open(objectId);
}
/**
- * Open object in all packs containing specified object.
+ * Open an object from this repository.
+ * <p>
+ * This is a one-shot call interface which may be faster than allocating a
+ * {@link #newObjectReader()} to perform the lookup.
*
* @param objectId
- * id of object to search for
- * @param resultLoaders
- * result collection of loaders for this object, filled with
- * loaders from all packs containing specified object
- * @param curs
- * temporary working space associated with the calling thread.
+ * 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.
*/
- void openObjectInAllPacks(final AnyObjectId objectId,
- final Collection<PackedObjectLoader> resultLoaders,
- final WindowCursor curs) throws IOException {
- objectDatabase.openObjectInAllPacks(resultLoaders, curs, objectId);
- }
-
- /**
- * @param id
- * SHA'1 of a blob
- * @return an {@link ObjectLoader} for accessing the data of a named blob
- * @throws IOException
- */
- public ObjectLoader openBlob(final ObjectId id) throws IOException {
- return openObject(id);
- }
-
- /**
- * @param id
- * SHA'1 of a tree
- * @return an {@link ObjectLoader} for accessing the data of a named tree
- * @throws IOException
- */
- public ObjectLoader openTree(final ObjectId id) throws IOException {
- return openObject(id);
+ public ObjectLoader open(AnyObjectId objectId, int typeHint)
+ throws MissingObjectException, IncorrectObjectTypeException,
+ IOException {
+ return getObjectDatabase().open(objectId, typeHint);
}
/**
@@ -572,23 +300,26 @@ public Commit mapCommit(final String revstr) throws IOException {
* @deprecated Use {@link org.eclipse.jgit.revwalk.RevWalk#parseCommit(AnyObjectId)},
* or {@link org.eclipse.jgit.revwalk.RevWalk#parseTag(AnyObjectId)}.
* To read a tree, use {@link org.eclipse.jgit.treewalk.TreeWalk#addTree(AnyObjectId)}.
- * To read a blob, open it with {@link #openObject(AnyObjectId)}.
+ * To read a blob, open it with {@link #open(AnyObjectId)}.
*/
@Deprecated
public Object mapObject(final ObjectId id, final String refName) throws IOException {
- final ObjectLoader or = openObject(id);
- if (or == null)
+ final ObjectLoader or;
+ try {
+ or = open(id);
+ } catch (MissingObjectException notFound) {
return null;
- final byte[] raw = or.getBytes();
+ }
+ final byte[] raw = or.getCachedBytes();
switch (or.getType()) {
case Constants.OBJ_TREE:
- return makeTree(id, raw);
+ return new Tree(this, id, raw);
case Constants.OBJ_COMMIT:
- return makeCommit(id, raw);
+ return new Commit(this, id, raw);
case Constants.OBJ_TAG:
- return makeTag(id, refName, raw);
+ return new Tag(this, id, refName, raw);
case Constants.OBJ_BLOB:
return raw;
@@ -608,18 +339,13 @@ public Object mapObject(final ObjectId id, final String refName) throws IOExcept
*/
@Deprecated
public Commit mapCommit(final ObjectId id) throws IOException {
- final ObjectLoader or = openObject(id);
- if (or == null)
+ final ObjectLoader or;
+ try {
+ or = open(id, Constants.OBJ_COMMIT);
+ } catch (MissingObjectException notFound) {
return null;
- final byte[] raw = or.getBytes();
- if (Constants.OBJ_COMMIT == or.getType())
- return new Commit(this, id, raw);
- throw new IncorrectObjectTypeException(id, Constants.TYPE_COMMIT);
- }
-
- private Commit makeCommit(final ObjectId id, final byte[] raw) {
- Commit ret = new Commit(this, id, raw);
- return ret;
+ }
+ return new Commit(this, id, or.getCachedBytes());
}
/**
@@ -650,10 +376,13 @@ public Tree mapTree(final String revstr) throws IOException {
*/
@Deprecated
public Tree mapTree(final ObjectId id) throws IOException {
- final ObjectLoader or = openObject(id);
- if (or == null)
+ final ObjectLoader or;
+ try {
+ or = open(id);
+ } catch (MissingObjectException notFound) {
return null;
- final byte[] raw = or.getBytes();
+ }
+ final byte[] raw = or.getCachedBytes();
switch (or.getType()) {
case Constants.OBJ_TREE:
return new Tree(this, id, raw);
@@ -666,16 +395,6 @@ public Tree mapTree(final ObjectId id) throws IOException {
}
}
- private Tree makeTree(final ObjectId id, final byte[] raw) throws IOException {
- Tree ret = new Tree(this, id, raw);
- return ret;
- }
-
- private Tag makeTag(final ObjectId id, final String refName, final byte[] raw) {
- Tag ret = new Tag(this, id, refName, raw);
- return ret;
- }
-
/**
* Access a tag by symbolic name.
*
@@ -701,12 +420,14 @@ public Tag mapTag(String revstr) throws IOException {
*/
@Deprecated
public Tag mapTag(final String refName, final ObjectId id) throws IOException {
- final ObjectLoader or = openObject(id);
- if (or == null)
+ final ObjectLoader or;
+ try {
+ or = open(id);
+ } catch (MissingObjectException notFound) {
return null;
- final byte[] raw = or.getBytes();
- if (Constants.OBJ_TAG == or.getType())
- return new Tag(this, id, refName, raw);
+ }
+ if (or.getType() == Constants.OBJ_TAG)
+ return new Tag(this, id, refName, or.getCachedBytes());
return new Tag(this, id, refName, null);
}
@@ -741,7 +462,7 @@ public RefUpdate updateRef(final String ref) throws IOException {
* to the base ref, as the symbolic ref could not be read.
*/
public RefUpdate updateRef(final String ref, final boolean detach) throws IOException {
- return refs.newUpdate(ref, detach);
+ return getRefDatabase().newUpdate(ref, detach);
}
/**
@@ -757,7 +478,7 @@ public RefUpdate updateRef(final String ref, final boolean detach) throws IOExce
*
*/
public RefRename renameRef(final String fromRef, final String toRef) throws IOException {
- return refs.newRename(fromRef, toRef);
+ return getRefDatabase().newRename(fromRef, toRef);
}
/**
@@ -765,36 +486,45 @@ public RefRename renameRef(final String fromRef, final String toRef) throws IOEx
*
* Currently supported is combinations of these.
* <ul>
- * <li>SHA-1 - a SHA-1</li>
- * <li>refs/... - a ref name</li>
- * <li>ref^n - nth parent reference</li>
- * <li>ref~n - distance via parent reference</li>
- * <li>ref@{n} - nth version of ref</li>
- * <li>ref^{tree} - tree references by ref</li>
- * <li>ref^{commit} - commit references by ref</li>
+ * <li>SHA-1 - a SHA-1</li>
+ * <li>refs/... - a ref name</li>
+ * <li>ref^n - nth parent reference</li>
+ * <li>ref~n - distance via parent reference</li>
+ * <li>ref@{n} - nth version of ref</li>
+ * <li>ref^{tree} - tree references by ref</li>
+ * <li>ref^{commit} - commit references by ref</li>
* </ul>
*
- * Not supported is
+ * Not supported is:
* <ul>
* <li>timestamps in reflogs, ref@{full or relative timestamp}</li>
* <li>abbreviated SHA-1's</li>
* </ul>
*
- * @param revstr A git object references expression
+ * @param revstr
+ * A git object references expression
* @return an ObjectId or null if revstr can't be resolved to any ObjectId
- * @throws IOException on serious errors
+ * @throws IOException
+ * on serious errors
*/
public ObjectId resolve(final String revstr) throws IOException {
+ RevWalk rw = new RevWalk(this);
+ try {
+ return resolve(rw, revstr);
+ } finally {
+ rw.release();
+ }
+ }
+
+ private ObjectId resolve(final RevWalk rw, final String revstr) throws IOException {
char[] rev = revstr.toCharArray();
- Object ref = null;
- ObjectId refId = null;
+ RevObject ref = null;
for (int i = 0; i < rev.length; ++i) {
switch (rev[i]) {
case '^':
- if (refId == null) {
- String refstr = new String(rev,0,i);
- refId = resolveSimple(refstr);
- if (refId == null)
+ if (ref == null) {
+ ref = parseSimple(rw, new String(rev, 0, i));
+ if (ref == null)
return null;
}
if (i + 1 < rev.length) {
@@ -810,19 +540,12 @@ public ObjectId resolve(final String revstr) throws IOException {
case '8':
case '9':
int j;
- ref = mapObject(refId, null);
- while (ref instanceof Tag) {
- Tag tag = (Tag)ref;
- refId = tag.getObjId();
- ref = mapObject(refId, null);
- }
- if (!(ref instanceof Commit))
- throw new IncorrectObjectTypeException(refId, Constants.TYPE_COMMIT);
- for (j=i+1; j<rev.length; ++j) {
+ ref = rw.parseCommit(ref);
+ for (j = i + 1; j < rev.length; ++j) {
if (!Character.isDigit(rev[j]))
break;
}
- String parentnum = new String(rev, i+1, j-i-1);
+ String parentnum = new String(rev, i + 1, j - i - 1);
int pnum;
try {
pnum = Integer.parseInt(parentnum);
@@ -832,123 +555,83 @@ public ObjectId resolve(final String revstr) throws IOException {
revstr);
}
if (pnum != 0) {
- final ObjectId parents[] = ((Commit) ref)
- .getParentIds();
- if (pnum > parents.length)
- refId = null;
+ RevCommit commit = (RevCommit) ref;
+ if (pnum > commit.getParentCount())
+ ref = null;
else
- refId = parents[pnum - 1];
+ ref = commit.getParent(pnum - 1);
}
i = j - 1;
break;
case '{':
int k;
String item = null;
- for (k=i+2; k<rev.length; ++k) {
+ for (k = i + 2; k < rev.length; ++k) {
if (rev[k] == '}') {
- item = new String(rev, i+2, k-i-2);
+ item = new String(rev, i + 2, k - i - 2);
break;
}
}
i = k;
if (item != null)
if (item.equals("tree")) {
- ref = mapObject(refId, null);
- while (ref instanceof Tag) {
- Tag t = (Tag)ref;
- refId = t.getObjId();
- ref = mapObject(refId, null);
- }
- if (ref instanceof Treeish)
- refId = ((Treeish)ref).getTreeId();
- else
- throw new IncorrectObjectTypeException(refId, Constants.TYPE_TREE);
- }
- else if (item.equals("commit")) {
- ref = mapObject(refId, null);
- while (ref instanceof Tag) {
- Tag t = (Tag)ref;
- refId = t.getObjId();
- ref = mapObject(refId, null);
- }
- if (!(ref instanceof Commit))
- throw new IncorrectObjectTypeException(refId, Constants.TYPE_COMMIT);
- }
- else if (item.equals("blob")) {
- ref = mapObject(refId, null);
- while (ref instanceof Tag) {
- Tag t = (Tag)ref;
- refId = t.getObjId();
- ref = mapObject(refId, null);
- }
- if (!(ref instanceof byte[]))
- throw new IncorrectObjectTypeException(refId, Constants.TYPE_BLOB);
- }
- else if (item.equals("")) {
- ref = mapObject(refId, null);
- while (ref instanceof Tag) {
- Tag t = (Tag)ref;
- refId = t.getObjId();
- ref = mapObject(refId, null);
- }
- }
- else
+ ref = rw.parseTree(ref);
+ } else if (item.equals("commit")) {
+ ref = rw.parseCommit(ref);
+ } else if (item.equals("blob")) {
+ ref = rw.peel(ref);
+ if (!(ref instanceof RevBlob))
+ throw new IncorrectObjectTypeException(ref,
+ Constants.TYPE_BLOB);
+ } else if (item.equals("")) {
+ ref = rw.peel(ref);
+ } else
throw new RevisionSyntaxException(revstr);
else
throw new RevisionSyntaxException(revstr);
break;
default:
- ref = mapObject(refId, null);
- if (ref instanceof Commit) {
- final ObjectId parents[] = ((Commit) ref)
- .getParentIds();
- if (parents.length == 0)
- refId = null;
+ ref = rw.parseAny(ref);
+ if (ref instanceof RevCommit) {
+ RevCommit commit = ((RevCommit) ref);
+ if (commit.getParentCount() == 0)
+ ref = null;
else
- refId = parents[0];
+ ref = commit.getParent(0);
} else
- throw new IncorrectObjectTypeException(refId, Constants.TYPE_COMMIT);
+ throw new IncorrectObjectTypeException(ref,
+ Constants.TYPE_COMMIT);
}
} else {
- ref = mapObject(refId, null);
- while (ref instanceof Tag) {
- Tag tag = (Tag)ref;
- refId = tag.getObjId();
- ref = mapObject(refId, null);
- }
- if (ref instanceof Commit) {
- final ObjectId parents[] = ((Commit) ref)
- .getParentIds();
- if (parents.length == 0)
- refId = null;
+ ref = rw.peel(ref);
+ if (ref instanceof RevCommit) {
+ RevCommit commit = ((RevCommit) ref);
+ if (commit.getParentCount() == 0)
+ ref = null;
else
- refId = parents[0];
+ ref = commit.getParent(0);
} else
- throw new IncorrectObjectTypeException(refId, Constants.TYPE_COMMIT);
+ throw new IncorrectObjectTypeException(ref,
+ Constants.TYPE_COMMIT);
}
break;
case '~':
if (ref == null) {
- String refstr = new String(rev,0,i);
- refId = resolveSimple(refstr);
- if (refId == null)
+ ref = parseSimple(rw, new String(rev, 0, i));
+ if (ref == null)
return null;
- ref = mapObject(refId, null);
}
- while (ref instanceof Tag) {
- Tag tag = (Tag)ref;
- refId = tag.getObjId();
- ref = mapObject(refId, null);
- }
- if (!(ref instanceof Commit))
- throw new IncorrectObjectTypeException(refId, Constants.TYPE_COMMIT);
+ ref = rw.peel(ref);
+ if (!(ref instanceof RevCommit))
+ throw new IncorrectObjectTypeException(ref,
+ Constants.TYPE_COMMIT);
int l;
for (l = i + 1; l < rev.length; ++l) {
if (!Character.isDigit(rev[l]))
break;
}
- String distnum = new String(rev, i+1, l-i-1);
+ String distnum = new String(rev, i + 1, l - i - 1);
int dist;
try {
dist = Integer.parseInt(distnum);
@@ -957,13 +640,14 @@ else if (item.equals("")) {
JGitText.get().invalidAncestryLength, revstr);
}
while (dist > 0) {
- final ObjectId[] parents = ((Commit) ref).getParentIds();
- if (parents.length == 0) {
- refId = null;
+ RevCommit commit = (RevCommit) ref;
+ if (commit.getParentCount() == 0) {
+ ref = null;
break;
}
- refId = parents[0];
- ref = mapCommit(refId);
+ commit = commit.getParent(0);
+ rw.parseHeaders(commit);
+ ref = commit;
--dist;
}
i = l - 1;
@@ -971,30 +655,35 @@ else if (item.equals("")) {
case '@':
int m;
String time = null;
- for (m=i+2; m<rev.length; ++m) {
+ for (m = i + 2; m < rev.length; ++m) {
if (rev[m] == '}') {
- time = new String(rev, i+2, m-i-2);
+ time = new String(rev, i + 2, m - i - 2);
break;
}
}
if (time != null)
- throw new RevisionSyntaxException(JGitText.get().reflogsNotYetSupportedByRevisionParser, revstr);
+ throw new RevisionSyntaxException(
+ JGitText.get().reflogsNotYetSupportedByRevisionParser,
+ revstr);
i = m - 1;
break;
default:
- if (refId != null)
+ if (ref != null)
throw new RevisionSyntaxException(revstr);
}
}
- if (refId == null)
- refId = resolveSimple(revstr);
- return refId;
+ return ref != null ? ref.copy() : resolveSimple(revstr);
+ }
+
+ private RevObject parseSimple(RevWalk rw, String revstr) throws IOException {
+ ObjectId id = resolveSimple(revstr);
+ return id != null ? rw.parseAny(id) : null;
}
private ObjectId resolveSimple(final String revstr) throws IOException {
if (ObjectId.isId(revstr))
return ObjectId.fromString(revstr);
- final Ref r = refs.getRef(revstr);
+ final Ref r = getRefDatabase().getRef(revstr);
return r != null ? r.getObjectId() : null;
}
@@ -1003,17 +692,24 @@ public void incrementOpen() {
useCnt.incrementAndGet();
}
- /**
- * Close all resources used by this repository
- */
+ /** Decrement the use count, and maybe close resources. */
public void close() {
if (useCnt.decrementAndGet() == 0) {
- objectDatabase.close();
- refs.close();
+ doClose();
}
}
/**
+ * Invoked when the use count drops to zero during {@link #close()}.
+ * <p>
+ * The default implementation closes the object and ref databases.
+ */
+ protected void doClose() {
+ getObjectDatabase().close();
+ getRefDatabase().close();
+ }
+
+ /**
* Add a single existing pack to the list of available pack files.
*
* @param pack
@@ -1024,12 +720,16 @@ public void close() {
* index file could not be opened, read, or is not recognized as
* a Git pack file index.
*/
- public void openPack(final File pack, final File idx) throws IOException {
- objectDatabase.openPack(pack, idx);
- }
+ public abstract void openPack(File pack, File idx) throws IOException;
public String toString() {
- return "Repository[" + getDirectory() + "]";
+ String desc;
+ if (getDirectory() != null)
+ desc = getDirectory().getPath();
+ else
+ desc = getClass().getSimpleName() + "-"
+ + System.identityHashCode(this);
+ return "Repository[" + desc + "]";
}
/**
@@ -1078,6 +778,20 @@ public String getBranch() throws IOException {
}
/**
+ * Objects known to exist but not expressed by {@link #getAllRefs()}.
+ * <p>
+ * When a repository borrows objects from another repository, it can
+ * advertise that it safely has that other repository's references, without
+ * exposing any other details about the other repository. This may help
+ * a client trying to push changes avoid pushing more than it needs to.
+ *
+ * @return unmodifiable collection of other known objects.
+ */
+ public Set<ObjectId> getAdditionalHaves() {
+ return Collections.emptySet();
+ }
+
+ /**
* Get a ref by name.
*
* @param name
@@ -1088,7 +802,7 @@ public String getBranch() throws IOException {
* @throws IOException
*/
public Ref getRef(final String name) throws IOException {
- return refs.getRef(name);
+ return getRefDatabase().getRef(name);
}
/**
@@ -1096,7 +810,7 @@ public Ref getRef(final String name) throws IOException {
*/
public Map<String, Ref> getAllRefs() {
try {
- return refs.getRefs(RefDatabase.ALL);
+ return getRefDatabase().getRefs(RefDatabase.ALL);
} catch (IOException e) {
return new HashMap<String, Ref>();
}
@@ -1109,7 +823,7 @@ public Map<String, Ref> getAllRefs() {
*/
public Map<String, Ref> getTags() {
try {
- return refs.getRefs(Constants.R_TAGS);
+ return getRefDatabase().getRefs(Constants.R_TAGS);
} catch (IOException e) {
return new HashMap<String, Ref>();
}
@@ -1130,7 +844,7 @@ public Map<String, Ref> getTags() {
*/
public Ref peel(final Ref ref) {
try {
- return refs.peel(ref);
+ return getRefDatabase().peel(ref);
} catch (IOException e) {
// Historical accident; if the reference cannot be peeled due
// to some sort of repository access problem we claim that the
@@ -1170,13 +884,13 @@ public Map<AnyObjectId, Set<Ref>> getAllRefsByPeeledObjectId() {
* {@link Repository}
* @throws IOException
* if the index can not be read
- * @throws IllegalStateException
- * if this is bare (see {@link #isBare()})
+ * @throws NoWorkTreeException
+ * if this is bare, which implies it has no working directory.
+ * See {@link #isBare()}.
*/
- public GitIndex getIndex() throws IOException, IllegalStateException {
+ public GitIndex getIndex() throws IOException, NoWorkTreeException {
if (isBare())
- throw new IllegalStateException(
- JGitText.get().bareRepositoryNoWorkdirAndIndex);
+ throw new NoWorkTreeException();
if (index == null) {
index = new GitIndex(this);
index.read();
@@ -1188,16 +902,63 @@ public GitIndex getIndex() throws IOException, IllegalStateException {
/**
* @return the index file location
- * @throws IllegalStateException
- * if this is bare (see {@link #isBare()})
+ * @throws NoWorkTreeException
+ * if this is bare, which implies it has no working directory.
+ * See {@link #isBare()}.
*/
- public File getIndexFile() throws IllegalStateException {
+ public File getIndexFile() throws NoWorkTreeException {
if (isBare())
- throw new IllegalStateException(
- JGitText.get().bareRepositoryNoWorkdirAndIndex);
+ throw new NoWorkTreeException();
return indexFile;
}
+ /**
+ * Create a new in-core index representation and read an index from disk.
+ * <p>
+ * The new index will be read before it is returned to the caller. Read
+ * failures are reported as exceptions and therefore prevent the method from
+ * returning a partially populated index.
+ *
+ * @return a cache representing the contents of the specified index file (if
+ * it exists) or an empty cache if the file does not exist.
+ * @throws NoWorkTreeException
+ * if this is bare, which implies it has no working directory.
+ * See {@link #isBare()}.
+ * @throws IOException
+ * the index file is present but could not be read.
+ * @throws CorruptObjectException
+ * the index file is using a format or extension that this
+ * library does not support.
+ */
+ public DirCache readDirCache() throws NoWorkTreeException,
+ CorruptObjectException, IOException {
+ return DirCache.read(getIndexFile(), getFS());
+ }
+
+ /**
+ * Create a new in-core index representation, lock it, and read from disk.
+ * <p>
+ * The new index will be locked and then read before it is returned to the
+ * caller. Read failures are reported as exceptions and therefore prevent
+ * the method from returning a partially populated index.
+ *
+ * @return a cache representing the contents of the specified index file (if
+ * it exists) or an empty cache if the file does not exist.
+ * @throws NoWorkTreeException
+ * if this is bare, which implies it has no working directory.
+ * See {@link #isBare()}.
+ * @throws IOException
+ * the index file is present but could not be read, or the lock
+ * could not be obtained.
+ * @throws CorruptObjectException
+ * the index file is using a format or extension that this
+ * library does not support.
+ */
+ public DirCache lockDirCache() throws NoWorkTreeException,
+ CorruptObjectException, IOException {
+ return DirCache.lock(getIndexFile(), getFS());
+ }
+
static byte[] gitInternalSlash(byte[] bytes) {
if (File.separatorChar == '/')
return bytes;
@@ -1211,10 +972,13 @@ public File getIndexFile() throws IllegalStateException {
* @return an important state
*/
public RepositoryState getRepositoryState() {
+ if (isBare() || getDirectory() == null)
+ return RepositoryState.BARE;
+
// Pre Git-1.6 logic
- if (new File(getWorkDir(), ".dotest").exists())
+ if (new File(getWorkTree(), ".dotest").exists())
return RepositoryState.REBASING;
- if (new File(gitDir,".dotest-merge").exists())
+ if (new File(getDirectory(), ".dotest-merge").exists())
return RepositoryState.REBASING_INTERACTIVE;
// From 1.6 onwards
@@ -1231,10 +995,10 @@ public RepositoryState getRepositoryState() {
return RepositoryState.REBASING_MERGE;
// Both versions
- if (new File(gitDir, "MERGE_HEAD").exists()) {
+ if (new File(getDirectory(), "MERGE_HEAD").exists()) {
// we are merging - now check whether we have unmerged paths
try {
- if (!DirCache.read(this).hasUnmergedPaths()) {
+ if (!readDirCache().hasUnmergedPaths()) {
// no unmerged paths -> return the MERGING_RESOLVED state
return RepositoryState.MERGING_RESOLVED;
}
@@ -1247,7 +1011,7 @@ public RepositoryState getRepositoryState() {
return RepositoryState.MERGING;
}
- if (new File(gitDir,"BISECT_LOG").exists())
+ if (new File(getDirectory(), "BISECT_LOG").exists())
return RepositoryState.BISECTING;
return RepositoryState.SAFE;
@@ -1334,96 +1098,23 @@ public static String stripWorkDir(File workDir, File file) {
}
/**
- * @return the "bare"-ness of this Repository
+ * @return true if this is bare, which implies it has no working directory.
*/
public boolean isBare() {
- return workDir == null;
+ return workTree == null;
}
/**
- * @return the workdir file, i.e. where the files are checked out
- * @throws IllegalStateException
- * if the repository is "bare"
+ * @return the root directory of the working tree, where files are checked
+ * out for viewing and editing.
+ * @throws NoWorkTreeException
+ * if this is bare, which implies it has no working directory.
+ * See {@link #isBare()}.
*/
- public File getWorkDir() throws IllegalStateException {
+ public File getWorkTree() throws NoWorkTreeException {
if (isBare())
- throw new IllegalStateException(
- JGitText.get().bareRepositoryNoWorkdirAndIndex);
- return workDir;
- }
-
- /**
- * Override default workdir
- *
- * @param workTree
- * the work tree directory
- */
- public void setWorkDir(File workTree) {
- this.workDir = workTree;
- }
-
- /**
- * Register a {@link RepositoryListener} which will be notified
- * when ref changes are detected.
- *
- * @param l
- */
- public void addRepositoryChangedListener(final RepositoryListener l) {
- listeners.add(l);
- }
-
- /**
- * Remove a registered {@link RepositoryListener}
- * @param l
- */
- public void removeRepositoryChangedListener(final RepositoryListener l) {
- listeners.remove(l);
- }
-
- /**
- * Register a global {@link RepositoryListener} which will be notified
- * when a ref changes in any repository are detected.
- *
- * @param l
- */
- public static void addAnyRepositoryChangedListener(final RepositoryListener l) {
- allListeners.add(l);
- }
-
- /**
- * Remove a globally registered {@link RepositoryListener}
- * @param l
- */
- public static void removeAnyRepositoryChangedListener(final RepositoryListener l) {
- allListeners.remove(l);
- }
-
- void fireRefsChanged() {
- final RefsChangedEvent event = new RefsChangedEvent(this);
- List<RepositoryListener> all;
- synchronized (listeners) {
- all = new ArrayList<RepositoryListener>(listeners);
- }
- synchronized (allListeners) {
- all.addAll(allListeners);
- }
- for (final RepositoryListener l : all) {
- l.refsChanged(event);
- }
- }
-
- void fireIndexChanged() {
- final IndexChangedEvent event = new IndexChangedEvent(this);
- List<RepositoryListener> all;
- synchronized (listeners) {
- all = new ArrayList<RepositoryListener>(listeners);
- }
- synchronized (allListeners) {
- all.addAll(allListeners);
- }
- for (final RepositoryListener l : all) {
- l.indexChanged(event);
- }
+ throw new NoWorkTreeException();
+ return workTree;
}
/**
@@ -1431,11 +1122,7 @@ void fireIndexChanged() {
*
* @throws IOException
*/
- public void scanForRepoChanges() throws IOException {
- getAllRefs(); // This will look for changes to refs
- if (!isBare())
- getIndex(); // This will detect changes in the index
- }
+ public abstract void scanForRepoChanges() throws IOException;
/**
* @param refName
@@ -1458,12 +1145,8 @@ public String shortenRefName(String refName) {
* named ref does not exist.
* @throws IOException the ref could not be accessed.
*/
- public ReflogReader getReflogReader(String refName) throws IOException {
- Ref ref = getRef(refName);
- if (ref != null)
- return new ReflogReader(this, ref.getName());
- return null;
- }
+ public abstract ReflogReader getReflogReader(String refName)
+ throws IOException;
/**
* Return the information stored in the file $GIT_DIR/MERGE_MSG. In this
@@ -1473,11 +1156,17 @@ public ReflogReader getReflogReader(String refName) throws IOException {
* @return a String containing the content of the MERGE_MSG file or
* {@code null} if this file doesn't exist
* @throws IOException
+ * @throws NoWorkTreeException
+ * if this is bare, which implies it has no working directory.
+ * See {@link #isBare()}.
*/
- public String readMergeCommitMsg() throws IOException {
- File mergeMsgFile = new File(gitDir, Constants.MERGE_MSG);
+ public String readMergeCommitMsg() throws IOException, NoWorkTreeException {
+ if (isBare() || getDirectory() == null)
+ throw new NoWorkTreeException();
+
+ File mergeMsgFile = new File(getDirectory(), Constants.MERGE_MSG);
try {
- return new String(IO.readFully(mergeMsgFile));
+ return RawParseUtils.decode(IO.readFully(mergeMsgFile));
} catch (FileNotFoundException e) {
// MERGE_MSG file has disappeared in the meantime
// ignore it
@@ -1486,6 +1175,32 @@ public String readMergeCommitMsg() throws IOException {
}
/**
+ * Write new content to the file $GIT_DIR/MERGE_MSG. In this file operations
+ * triggering a merge will store a template for the commit message of the
+ * merge commit. If <code>null</code> is specified as message the file will
+ * be deleted
+ *
+ * @param msg
+ * the message which should be written or <code>null</code> to
+ * delete the file
+ *
+ * @throws IOException
+ */
+ public void writeMergeCommitMsg(String msg) throws IOException {
+ File mergeMsgFile = new File(gitDir, Constants.MERGE_MSG);
+ if (msg != null) {
+ FileOutputStream fos = new FileOutputStream(mergeMsgFile);
+ try {
+ fos.write(msg.getBytes(Constants.CHARACTER_ENCODING));
+ } finally {
+ fos.close();
+ }
+ } else {
+ mergeMsgFile.delete();
+ }
+ }
+
+ /**
* Return the information stored in the file $GIT_DIR/MERGE_HEAD. In this
* file operations triggering a merge will store the IDs of all heads which
* should be merged together with HEAD.
@@ -1494,18 +1209,24 @@ public String readMergeCommitMsg() throws IOException {
* file or {@code null} if this file doesn't exist. Also if the file
* exists but is empty {@code null} will be returned
* @throws IOException
+ * @throws NoWorkTreeException
+ * if this is bare, which implies it has no working directory.
+ * See {@link #isBare()}.
*/
- public List<ObjectId> readMergeHeads() throws IOException {
- File mergeHeadFile = new File(gitDir, Constants.MERGE_HEAD);
+ public List<ObjectId> readMergeHeads() throws IOException, NoWorkTreeException {
+ if (isBare() || getDirectory() == null)
+ throw new NoWorkTreeException();
+
+ File mergeHeadFile = new File(getDirectory(), Constants.MERGE_HEAD);
byte[] raw;
try {
raw = IO.readFully(mergeHeadFile);
} catch (FileNotFoundException notFound) {
- return new LinkedList<ObjectId>();
+ return null;
}
if (raw.length == 0)
- throw new IOException("MERGE_HEAD file empty: " + mergeHeadFile);
+ return null;
LinkedList<ObjectId> heads = new LinkedList<ObjectId>();
for (int p = 0; p < raw.length;) {
@@ -1515,4 +1236,33 @@ public List<ObjectId> readMergeHeads() throws IOException {
}
return heads;
}
+
+ /**
+ * Write new merge-heads into $GIT_DIR/MERGE_HEAD. In this file operations
+ * triggering a merge will store the IDs of all heads which should be merged
+ * together with HEAD. If <code>null</code> is specified as list of commits
+ * the file will be deleted
+ *
+ * @param heads
+ * a list of {@link Commit}s which IDs should be written to
+ * $GIT_DIR/MERGE_HEAD or <code>null</code> to delete the file
+ * @throws IOException
+ */
+ public void writeMergeHeads(List<ObjectId> heads) throws IOException {
+ File mergeHeadFile = new File(gitDir, Constants.MERGE_HEAD);
+ if (heads != null) {
+ BufferedOutputStream bos = new BufferedOutputStream(
+ new FileOutputStream(mergeHeadFile));
+ try {
+ for (ObjectId id : heads) {
+ id.copyTo(bos);
+ bos.write('\n');
+ }
+ } finally {
+ bos.close();
+ }
+ } else {
+ mergeHeadFile.delete();
+ }
+ }
}
diff --git a/org.eclipse.jgit/src/org/eclipse/jgit/lib/RepositoryAdapter.java b/org.eclipse.jgit/src/org/eclipse/jgit/lib/RepositoryBuilder.java
similarity index 66%
copy from org.eclipse.jgit/src/org/eclipse/jgit/lib/RepositoryAdapter.java
copy to org.eclipse.jgit/src/org/eclipse/jgit/lib/RepositoryBuilder.java
index e43c33a..f9185e8 100644
--- a/org.eclipse.jgit/src/org/eclipse/jgit/lib/RepositoryAdapter.java
+++ b/org.eclipse.jgit/src/org/eclipse/jgit/lib/RepositoryBuilder.java
@@ -1,5 +1,5 @@
/*
- * Copyright (C) 2008, Robin Rosenberg <robin.rosenberg@dewire.com>
+ * Copyright (C) 2010, Google Inc.
* and other copyright owners as documented in the project's IP log.
*
* This program and the accompanying materials are made available
@@ -43,18 +43,32 @@
package org.eclipse.jgit.lib;
+import java.io.File;
+
+import org.eclipse.jgit.storage.file.FileRepositoryBuilder;
+
/**
- * A default {@link RepositoryListener} that does nothing except invoke an
- * optional general method for any repository change.
+ * Base class to support constructing a {@link Repository}.
+ * <p>
+ * Applications must set one of {@link #setGitDir(File)} or
+ * {@link #setWorkTree(File)}, or use {@link #readEnvironment()} or
+ * {@link #findGitDir()} in order to configure the minimum property set
+ * necessary to open a repository.
+ * <p>
+ * Single repository applications trying to be compatible with other Git
+ * implementations are encouraged to use a model such as:
+ *
+ * <pre>
+ * new RepositoryBuilder() //
+ * .setGitDir(gitDirArgument) // --git-dir if supplied, no-op if null
+ * .readEnviroment() // scan environment GIT_* variables
+ * .findGitDir() // scan up the file system tree
+ * .build()
+ * </pre>
+ *
+ * @see FileRepositoryBuilder
*/
-public class RepositoryAdapter implements RepositoryListener {
-
- public void indexChanged(final IndexChangedEvent e) {
- // Empty
- }
-
- public void refsChanged(final RefsChangedEvent e) {
- // Empty
- }
-
+public class RepositoryBuilder extends
+ BaseRepositoryBuilder<RepositoryBuilder, Repository> {
+ // Empty implementation, everything is inherited.
}
diff --git a/org.eclipse.jgit/src/org/eclipse/jgit/lib/RepositoryCache.java b/org.eclipse.jgit/src/org/eclipse/jgit/lib/RepositoryCache.java
index 0b0260a..dc5eae5 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.concurrent.ConcurrentHashMap;
import org.eclipse.jgit.errors.RepositoryNotFoundException;
+import org.eclipse.jgit.storage.file.FileRepository;
import org.eclipse.jgit.util.FS;
import org.eclipse.jgit.util.IO;
import org.eclipse.jgit.util.RawParseUtils;
@@ -120,7 +121,10 @@ public static Repository open(final Key location, final boolean mustExist)
* repository to register.
*/
public static void register(final Repository db) {
- cache.registerRepository(FileKey.exact(db.getDirectory(), db.getFS()), db);
+ if (db.getDirectory() != null) {
+ FileKey key = FileKey.exact(db.getDirectory(), db.getFS());
+ cache.registerRepository(key, db);
+ }
}
/**
@@ -133,7 +137,10 @@ public static void register(final Repository db) {
* repository to unregister.
*/
public static void close(final Repository db) {
- cache.unregisterRepository(FileKey.exact(db.getDirectory(), db.getFS()));
+ if (db.getDirectory() != null) {
+ FileKey key = FileKey.exact(db.getDirectory(), db.getFS());
+ cache.unregisterRepository(key);
+ }
}
/** Unregister all repositories from the cache. */
@@ -313,7 +320,7 @@ public final File getFile() {
public Repository open(final boolean mustExist) throws IOException {
if (mustExist && !isGitRepository(path, fs))
throw new RepositoryNotFoundException(path);
- return new Repository(path);
+ return new FileRepository(path);
}
@Override
diff --git a/org.eclipse.jgit/src/org/eclipse/jgit/lib/RepositoryConfig.java b/org.eclipse.jgit/src/org/eclipse/jgit/lib/RepositoryConfig.java
deleted file mode 100644
index 805975a..0000000
--- a/org.eclipse.jgit/src/org/eclipse/jgit/lib/RepositoryConfig.java
+++ /dev/null
@@ -1,135 +0,0 @@
-/*
- * Copyright (C) 2009, Constantine Plotnikov <constantine.plotnikov@gmail.com>
- * Copyright (C) 2007, Dave Watson <dwatson@mimvista.com>
- * Copyright (C) 2008-2009, Google Inc.
- * Copyright (C) 2009, JetBrains s.r.o.
- * Copyright (C) 2007-2008, Robin Rosenberg <robin.rosenberg@dewire.com>
- * Copyright (C) 2006-2008, Shawn O. Pearce <spearce@spearce.org>
- * Copyright (C) 2008, Thad Hughes <thadh@thad.corp.google.com>
- * Copyright (C) 2009, Yann Simon <yann.simon.fr@gmail.com>
- * and other copyright owners as documented in the project's IP log.
- *
- * This program and the accompanying materials are made available
- * under the terms of the Eclipse Distribution License v1.0 which
- * accompanies this distribution, is reproduced below, and is
- * available at http://www.eclipse.org/org/documents/edl-v10.php
- *
- * All rights reserved.
- *
- * Redistribution and use in source and binary forms, with or
- * without modification, are permitted provided that the following
- * conditions are met:
- *
- * - Redistributions of source code must retain the above copyright
- * notice, this list of conditions and the following disclaimer.
- *
- * - Redistributions in binary form must reproduce the above
- * copyright notice, this list of conditions and the following
- * disclaimer in the documentation and/or other materials provided
- * with the distribution.
- *
- * - Neither the name of the Eclipse Foundation, Inc. nor the
- * names of its contributors may be used to endorse or promote
- * products derived from this software without specific prior
- * written permission.
- *
- * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND
- * CONTRIBUTORS "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES,
- * INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
- * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
- * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR
- * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
- * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT
- * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
- * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER
- * CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT,
- * STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
- * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF
- * ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
- */
-
-package org.eclipse.jgit.lib;
-
-import java.io.File;
-
-/**
- * An object representing the Git config file.
- *
- * This can be either the repository specific file or the user global
- * file depending on how it is instantiated.
- */
-public class RepositoryConfig extends FileBasedConfig {
- /** Section name for a branch configuration. */
- public static final String BRANCH_SECTION = "branch";
-
- /**
- * Create a Git configuration file reader/writer/cache for a specific file.
- *
- * @param base
- * configuration that provides default values if this file does
- * not set/override a particular key. Often this is the user's
- * global configuration file, or the system level configuration.
- * @param cfgLocation
- * path of the file to load (or save).
- */
- public RepositoryConfig(final Config base, final File cfgLocation) {
- super(base, cfgLocation);
- }
-
- /**
- * @return Core configuration values
- */
- public CoreConfig getCore() {
- return get(CoreConfig.KEY);
- }
-
- /**
- * @return transfer, fetch and receive configuration values
- */
- public TransferConfig getTransfer() {
- return get(TransferConfig.KEY);
- }
-
- /** @return standard user configuration data */
- public UserConfig getUserConfig() {
- return get(UserConfig.KEY);
- }
-
- /**
- * @return the author name as defined in the git variables
- * and configurations. If no name could be found, try
- * to use the system user name instead.
- */
- public String getAuthorName() {
- return getUserConfig().getAuthorName();
- }
-
- /**
- * @return the committer name as defined in the git variables
- * and configurations. If no name could be found, try
- * to use the system user name instead.
- */
- public String getCommitterName() {
- return getUserConfig().getCommitterName();
- }
-
- /**
- * @return the author email as defined in git variables and
- * configurations. If no email could be found, try to
- * propose one default with the user name and the
- * host name.
- */
- public String getAuthorEmail() {
- return getUserConfig().getAuthorEmail();
- }
-
- /**
- * @return the committer email as defined in git variables and
- * configurations. If no email could be found, try to
- * propose one default with the user name and the
- * host name.
- */
- public String getCommitterEmail() {
- return getUserConfig().getCommitterEmail();
- }
-}
diff --git a/org.eclipse.jgit/src/org/eclipse/jgit/lib/RepositoryListener.java b/org.eclipse.jgit/src/org/eclipse/jgit/lib/RepositoryListener.java
deleted file mode 100644
index 0473093..0000000
--- a/org.eclipse.jgit/src/org/eclipse/jgit/lib/RepositoryListener.java
+++ /dev/null
@@ -1,69 +0,0 @@
-/*
- * Copyright (C) 2008, Robin Rosenberg <robin.rosenberg@dewire.com>
- * and other copyright owners as documented in the project's IP log.
- *
- * This program and the accompanying materials are made available
- * under the terms of the Eclipse Distribution License v1.0 which
- * accompanies this distribution, is reproduced below, and is
- * available at http://www.eclipse.org/org/documents/edl-v10.php
- *
- * All rights reserved.
- *
- * Redistribution and use in source and binary forms, with or
- * without modification, are permitted provided that the following
- * conditions are met:
- *
- * - Redistributions of source code must retain the above copyright
- * notice, this list of conditions and the following disclaimer.
- *
- * - Redistributions in binary form must reproduce the above
- * copyright notice, this list of conditions and the following
- * disclaimer in the documentation and/or other materials provided
- * with the distribution.
- *
- * - Neither the name of the Eclipse Foundation, Inc. nor the
- * names of its contributors may be used to endorse or promote
- * products derived from this software without specific prior
- * written permission.
- *
- * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND
- * CONTRIBUTORS "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES,
- * INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
- * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
- * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR
- * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
- * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT
- * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
- * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER
- * CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT,
- * STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
- * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF
- * ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
- */
-
-package org.eclipse.jgit.lib;
-
-/**
- * A RepositoryListener gets notification about changes in refs or repository.
- * <p>
- * It currently does <em>not</em> get notification about which items are
- * changed.
- */
-public interface RepositoryListener {
- /**
- * Invoked when a ref changes
- *
- * @param e
- * information about the changes.
- */
- void refsChanged(RefsChangedEvent e);
-
- /**
- * Invoked when the index changes
- *
- * @param e
- * information about the changes.
- */
- void indexChanged(IndexChangedEvent e);
-
-}
diff --git a/org.eclipse.jgit/src/org/eclipse/jgit/lib/RepositoryState.java b/org.eclipse.jgit/src/org/eclipse/jgit/lib/RepositoryState.java
index 2cf5225..0a59906 100644
--- a/org.eclipse.jgit/src/org/eclipse/jgit/lib/RepositoryState.java
+++ b/org.eclipse.jgit/src/org/eclipse/jgit/lib/RepositoryState.java
@@ -55,6 +55,14 @@
* on the state are the only supported means of deciding what to do.
*/
public enum RepositoryState {
+ /** Has no work tree and cannot be used for normal editing. */
+ BARE {
+ public boolean canCheckout() { return false; }
+ public boolean canResetHead() { return false; }
+ public boolean canCommit() { return false; }
+ public String getDescription() { return "Bare"; }
+ },
+
/**
* A safe state for working normally
* */
diff --git a/org.eclipse.jgit/src/org/eclipse/jgit/lib/PackLock.java b/org.eclipse.jgit/src/org/eclipse/jgit/lib/StoredConfig.java
similarity index 61%
copy from org.eclipse.jgit/src/org/eclipse/jgit/lib/PackLock.java
copy to org.eclipse.jgit/src/org/eclipse/jgit/lib/StoredConfig.java
index de8e3fa..f904120 100644
--- a/org.eclipse.jgit/src/org/eclipse/jgit/lib/PackLock.java
+++ b/org.eclipse.jgit/src/org/eclipse/jgit/lib/StoredConfig.java
@@ -1,5 +1,5 @@
/*
- * Copyright (C) 2009, Google Inc.
+ * 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
@@ -43,48 +43,48 @@
package org.eclipse.jgit.lib;
-import java.io.File;
import java.io.IOException;
-/** Keeps track of a {@link PackFile}'s associated <code>.keep</code> file. */
-public class PackLock {
- private final File keepFile;
+import org.eclipse.jgit.errors.ConfigInvalidException;
- /**
- * Create a new lock for a pack file.
- *
- * @param packFile
- * location of the <code>pack-*.pack</code> 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");
+/**
+ * Persistent configuration that can be stored and loaded from a location.
+ */
+public abstract class StoredConfig extends Config {
+ /** Create a configuration with no default fallback. */
+ public StoredConfig() {
+ super();
}
/**
- * Create the <code>pack-*.keep</code> file, with the given message.
+ * Create an empty configuration with a fallback for missing keys.
*
- * @param msg
- * message to store in the file.
- * @return true if the keep file was successfully written; false otherwise.
+ * @param defaultConfig
+ * the base configuration to be consulted when a key is missing
+ * from this configuration instance.
+ */
+ public StoredConfig(Config defaultConfig) {
+ super(defaultConfig);
+ }
+
+ /**
+ * Load the configuration from the persistent store.
+ * <p>
+ * If the configuration does not exist, this configuration is cleared, and
+ * thus behaves the same as though the backing store exists, but is empty.
+ *
* @throws IOException
- * the keep file could not be written.
+ * the configuration could not be read (but does exist).
+ * @throws ConfigInvalidException
+ * the configuration is not properly formatted.
*/
- 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();
- }
+ public abstract void load() throws IOException, ConfigInvalidException;
- /** Remove the <code>.keep</code> file that holds this pack in place. */
- public void unlock() {
- keepFile.delete();
- }
+ /**
+ * Save the configuration to the persistent store.
+ *
+ * @throws IOException
+ * the configuration could not be written.
+ */
+ public abstract void save() throws IOException;
}
diff --git a/org.eclipse.jgit/src/org/eclipse/jgit/lib/Tag.java b/org.eclipse.jgit/src/org/eclipse/jgit/lib/Tag.java
index 5b3531e..25a06c9 100644
--- a/org.eclipse.jgit/src/org/eclipse/jgit/lib/Tag.java
+++ b/org.eclipse.jgit/src/org/eclipse/jgit/lib/Tag.java
@@ -203,9 +203,14 @@ public void tag() throws IOException {
final RefUpdate ru;
if (tagger!=null || message!=null || type!=null) {
- ObjectId tagid = new ObjectWriter(objdb).writeTag(this);
- setTagId(tagid);
- id = tagid;
+ ObjectInserter odi = objdb.newObjectInserter();
+ try {
+ id = odi.insert(Constants.OBJ_TAG, odi.format(this));
+ odi.flush();
+ setTagId(id);
+ } finally {
+ odi.release();
+ }
} else {
id = objId;
}
diff --git a/org.eclipse.jgit/src/org/eclipse/jgit/lib/RepositoryAdapter.java b/org.eclipse.jgit/src/org/eclipse/jgit/lib/ThreadSafeProgressMonitor.java
similarity index 62%
copy from org.eclipse.jgit/src/org/eclipse/jgit/lib/RepositoryAdapter.java
copy to org.eclipse.jgit/src/org/eclipse/jgit/lib/ThreadSafeProgressMonitor.java
index e43c33a..9708bb2 100644
--- a/org.eclipse.jgit/src/org/eclipse/jgit/lib/RepositoryAdapter.java
+++ b/org.eclipse.jgit/src/org/eclipse/jgit/lib/ThreadSafeProgressMonitor.java
@@ -1,5 +1,5 @@
/*
- * Copyright (C) 2008, Robin Rosenberg <robin.rosenberg@dewire.com>
+ * Copyright (C) 2010, Google Inc.
* and other copyright owners as documented in the project's IP log.
*
* This program and the accompanying materials are made available
@@ -43,18 +43,69 @@
package org.eclipse.jgit.lib;
+import java.util.concurrent.locks.ReentrantLock;
+
/**
- * A default {@link RepositoryListener} that does nothing except invoke an
- * optional general method for any repository change.
+ * Wrapper around the general {@link ProgressMonitor} to make it thread safe.
*/
-public class RepositoryAdapter implements RepositoryListener {
+public class ThreadSafeProgressMonitor implements ProgressMonitor {
+ private final ProgressMonitor pm;
- public void indexChanged(final IndexChangedEvent e) {
- // Empty
+ private final ReentrantLock lock;
+
+ /**
+ * Wrap a ProgressMonitor to be thread safe.
+ *
+ * @param pm
+ * the underlying monitor to receive events.
+ */
+ public ThreadSafeProgressMonitor(ProgressMonitor pm) {
+ this.pm = pm;
+ this.lock = new ReentrantLock();
}
- public void refsChanged(final RefsChangedEvent e) {
- // Empty
+ public void start(int totalTasks) {
+ lock.lock();
+ try {
+ pm.start(totalTasks);
+ } finally {
+ lock.unlock();
+ }
}
+ public void beginTask(String title, int totalWork) {
+ lock.lock();
+ try {
+ pm.beginTask(title, totalWork);
+ } finally {
+ lock.unlock();
+ }
+ }
+
+ public void update(int completed) {
+ lock.lock();
+ try {
+ pm.update(completed);
+ } finally {
+ lock.unlock();
+ }
+ }
+
+ public boolean isCancelled() {
+ lock.lock();
+ try {
+ return pm.isCancelled();
+ } finally {
+ lock.unlock();
+ }
+ }
+
+ public void endTask() {
+ lock.lock();
+ try {
+ pm.endTask();
+ } finally {
+ lock.unlock();
+ }
+ }
}
diff --git a/org.eclipse.jgit/src/org/eclipse/jgit/lib/Tree.java b/org.eclipse.jgit/src/org/eclipse/jgit/lib/Tree.java
index 0872c96..d68b9f6 100644
--- a/org.eclipse.jgit/src/org/eclipse/jgit/lib/Tree.java
+++ b/org.eclipse.jgit/src/org/eclipse/jgit/lib/Tree.java
@@ -537,10 +537,8 @@ public void accept(final TreeVisitor tv, final int flags)
private void ensureLoaded() throws IOException, MissingObjectException {
if (!isLoaded()) {
- final ObjectLoader or = db.openTree(getId());
- if (or == null)
- throw new MissingObjectException(getId(), Constants.TYPE_TREE);
- readTree(or.getBytes());
+ ObjectLoader ldr = db.open(getId(), Constants.OBJ_TREE);
+ readTree(ldr.getCachedBytes());
}
}
diff --git a/org.eclipse.jgit/src/org/eclipse/jgit/lib/UnpackedObjectLoader.java b/org.eclipse.jgit/src/org/eclipse/jgit/lib/UnpackedObjectLoader.java
deleted file mode 100644
index cd2eb38..0000000
--- a/org.eclipse.jgit/src/org/eclipse/jgit/lib/UnpackedObjectLoader.java
+++ /dev/null
@@ -1,223 +0,0 @@
-/*
- * Copyright (C) 2007, Robin Rosenberg <robin.rosenberg@dewire.com>
- * Copyright (C) 2006-2008, Shawn O. Pearce <spearce@spearce.org>
- * and other copyright owners as documented in the project's IP log.
- *
- * This program and the accompanying materials are made available
- * under the terms of the Eclipse Distribution License v1.0 which
- * accompanies this distribution, is reproduced below, and is
- * available at http://www.eclipse.org/org/documents/edl-v10.php
- *
- * All rights reserved.
- *
- * Redistribution and use in source and binary forms, with or
- * without modification, are permitted provided that the following
- * conditions are met:
- *
- * - Redistributions of source code must retain the above copyright
- * notice, this list of conditions and the following disclaimer.
- *
- * - Redistributions in binary form must reproduce the above
- * copyright notice, this list of conditions and the following
- * disclaimer in the documentation and/or other materials provided
- * with the distribution.
- *
- * - Neither the name of the Eclipse Foundation, Inc. nor the
- * names of its contributors may be used to endorse or promote
- * products derived from this software without specific prior
- * written permission.
- *
- * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND
- * CONTRIBUTORS "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES,
- * INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
- * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
- * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR
- * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
- * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT
- * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
- * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER
- * CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT,
- * STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
- * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF
- * ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
- */
-
-package org.eclipse.jgit.lib;
-
-import java.io.File;
-import java.io.FileNotFoundException;
-import java.io.IOException;
-import java.util.zip.DataFormatException;
-import java.util.zip.Inflater;
-
-import org.eclipse.jgit.JGitText;
-import org.eclipse.jgit.errors.CorruptObjectException;
-import org.eclipse.jgit.util.IO;
-import org.eclipse.jgit.util.MutableInteger;
-import org.eclipse.jgit.util.RawParseUtils;
-
-/**
- * Loose object loader. This class loads an object not stored in a pack.
- */
-public class UnpackedObjectLoader extends ObjectLoader {
- private final int objectType;
-
- private final int objectSize;
-
- private final byte[] bytes;
-
- /**
- * Construct an ObjectLoader to read from the file.
- *
- * @param path
- * location of the loose object to read.
- * @param id
- * expected identity of the object being loaded, if known.
- * @throws FileNotFoundException
- * the loose object file does not exist.
- * @throws IOException
- * the loose object file exists, but is corrupt.
- */
- public UnpackedObjectLoader(final File path, final AnyObjectId id)
- throws IOException {
- this(IO.readFully(path), id);
- }
-
- /**
- * Construct an ObjectLoader from a loose object's compressed form.
- *
- * @param compressed
- * entire content of the loose object file.
- * @throws CorruptObjectException
- * The compressed data supplied does not match the format for a
- * valid loose object.
- */
- public UnpackedObjectLoader(final byte[] compressed)
- throws CorruptObjectException {
- this(compressed, null);
- }
-
- private UnpackedObjectLoader(final byte[] compressed, final AnyObjectId id)
- throws CorruptObjectException {
- // Try to determine if this is a legacy format loose object or
- // a new style loose object. The legacy format was completely
- // compressed with zlib so the first byte must be 0x78 (15-bit
- // window size, deflated) and the first 16 bit word must be
- // evenly divisible by 31. Otherwise its a new style loose
- // object.
- //
- final Inflater inflater = InflaterCache.get();
- try {
- final int fb = compressed[0] & 0xff;
- if (fb == 0x78 && (((fb << 8) | compressed[1] & 0xff) % 31) == 0) {
- inflater.setInput(compressed);
- final byte[] hdr = new byte[64];
- int avail = 0;
- while (!inflater.finished() && avail < hdr.length)
- try {
- int uncompressed = inflater.inflate(hdr, avail,
- hdr.length - avail);
- if (uncompressed == 0) {
- throw new CorruptObjectException(id,
- JGitText.get().corruptObjectBadStreamCorruptHeader);
- }
- avail += uncompressed;
- } catch (DataFormatException dfe) {
- final CorruptObjectException coe;
- coe = new CorruptObjectException(id, JGitText.get().corruptObjectBadStream);
- coe.initCause(dfe);
- throw coe;
- }
- if (avail < 5)
- throw new CorruptObjectException(id, JGitText.get().corruptObjectNoHeader);
-
- final MutableInteger p = new MutableInteger();
- objectType = Constants.decodeTypeString(id, hdr, (byte) ' ', p);
- objectSize = RawParseUtils.parseBase10(hdr, p.value, p);
- if (objectSize < 0)
- throw new CorruptObjectException(id, JGitText.get().corruptObjectNegativeSize);
- if (hdr[p.value++] != 0)
- throw new CorruptObjectException(id, JGitText.get().corruptObjectGarbageAfterSize);
- bytes = new byte[objectSize];
- if (p.value < avail)
- System.arraycopy(hdr, p.value, bytes, 0, avail - p.value);
- decompress(id, inflater, avail - p.value);
- } else {
- int p = 0;
- int c = compressed[p++] & 0xff;
- final int typeCode = (c >> 4) & 7;
- int size = c & 15;
- int shift = 4;
- while ((c & 0x80) != 0) {
- c = compressed[p++] & 0xff;
- size += (c & 0x7f) << shift;
- shift += 7;
- }
-
- switch (typeCode) {
- case Constants.OBJ_COMMIT:
- case Constants.OBJ_TREE:
- case Constants.OBJ_BLOB:
- case Constants.OBJ_TAG:
- objectType = typeCode;
- break;
- default:
- throw new CorruptObjectException(id, JGitText.get().corruptObjectInvalidType);
- }
-
- objectSize = size;
- bytes = new byte[objectSize];
- inflater.setInput(compressed, p, compressed.length - p);
- decompress(id, inflater, 0);
- }
- } finally {
- InflaterCache.release(inflater);
- }
- }
-
- private void decompress(final AnyObjectId id, final Inflater inf, int p)
- throws CorruptObjectException {
- try {
- while (!inf.finished()) {
- int uncompressed = inf.inflate(bytes, p, objectSize - p);
- p += uncompressed;
- if (uncompressed == 0 && !inf.finished()) {
- throw new CorruptObjectException(id,
- JGitText.get().corruptObjectBadStreamCorruptHeader);
- }
- }
- } catch (DataFormatException dfe) {
- final CorruptObjectException coe;
- coe = new CorruptObjectException(id, JGitText.get().corruptObjectBadStream);
- coe.initCause(dfe);
- throw coe;
- }
- if (p != objectSize)
- throw new CorruptObjectException(id, JGitText.get().corruptObjectIncorrectLength);
- }
-
- @Override
- public int getType() {
- return objectType;
- }
-
- @Override
- public long getSize() {
- return objectSize;
- }
-
- @Override
- public byte[] getCachedBytes() {
- return bytes;
- }
-
- @Override
- public int getRawType() {
- return objectType;
- }
-
- @Override
- public long getRawSize() {
- return objectSize;
- }
-}
diff --git a/org.eclipse.jgit/src/org/eclipse/jgit/lib/WholePackedObjectLoader.java b/org.eclipse.jgit/src/org/eclipse/jgit/lib/WholePackedObjectLoader.java
deleted file mode 100644
index fcfa573..0000000
--- a/org.eclipse.jgit/src/org/eclipse/jgit/lib/WholePackedObjectLoader.java
+++ /dev/null
@@ -1,110 +0,0 @@
-/*
- * Copyright (C) 2008-2009, Google Inc.
- * Copyright (C) 2008, Marek Zawirski <marek.zawirski@gmail.com>
- * Copyright (C) 2006-2008, Shawn O. Pearce <spearce@spearce.org>
- * and other copyright owners as documented in the project's IP log.
- *
- * This program and the accompanying materials are made available
- * under the terms of the Eclipse Distribution License v1.0 which
- * accompanies this distribution, is reproduced below, and is
- * available at http://www.eclipse.org/org/documents/edl-v10.php
- *
- * All rights reserved.
- *
- * Redistribution and use in source and binary forms, with or
- * without modification, are permitted provided that the following
- * conditions are met:
- *
- * - Redistributions of source code must retain the above copyright
- * notice, this list of conditions and the following disclaimer.
- *
- * - Redistributions in binary form must reproduce the above
- * copyright notice, this list of conditions and the following
- * disclaimer in the documentation and/or other materials provided
- * with the distribution.
- *
- * - Neither the name of the Eclipse Foundation, Inc. nor the
- * names of its contributors may be used to endorse or promote
- * products derived from this software without specific prior
- * written permission.
- *
- * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND
- * CONTRIBUTORS "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES,
- * INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
- * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
- * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR
- * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
- * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT
- * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
- * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER
- * CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT,
- * STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
- * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF
- * ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
- */
-
-package org.eclipse.jgit.lib;
-
-import java.io.IOException;
-import java.text.MessageFormat;
-import java.util.zip.DataFormatException;
-
-import org.eclipse.jgit.JGitText;
-import org.eclipse.jgit.errors.CorruptObjectException;
-
-/** Reader for a non-delta (just deflated) object in a pack file. */
-class WholePackedObjectLoader extends PackedObjectLoader {
- private static final int OBJ_COMMIT = Constants.OBJ_COMMIT;
-
- WholePackedObjectLoader(final PackFile pr, final long objectOffset,
- final int headerSize, final int type, final int size) {
- super(pr, objectOffset, headerSize);
- objectType = type;
- objectSize = size;
- }
-
- @Override
- public void materialize(final WindowCursor curs) throws IOException {
- if (cachedBytes != null) {
- return;
- }
-
- if (objectType != OBJ_COMMIT) {
- UnpackedObjectCache.Entry cache = pack.readCache(objectOffset);
- if (cache != null) {
- curs.release();
- cachedBytes = cache.data;
- return;
- }
- }
-
- try {
- cachedBytes = pack.decompress(objectOffset + headerSize,
- objectSize, curs);
- curs.release();
- if (objectType != OBJ_COMMIT)
- pack.saveCache(objectOffset, cachedBytes, objectType);
- } catch (DataFormatException dfe) {
- final CorruptObjectException coe;
- coe = new CorruptObjectException(MessageFormat.format(JGitText.get().objectAtHasBadZlibStream,
- objectOffset, pack.getPackFile()));
- coe.initCause(dfe);
- throw coe;
- }
- }
-
- @Override
- public int getRawType() {
- return objectType;
- }
-
- @Override
- public long getRawSize() {
- return objectSize;
- }
-
- @Override
- public ObjectId getDeltaBase() {
- return null;
- }
-}
diff --git a/org.eclipse.jgit/src/org/eclipse/jgit/lib/WorkDirCheckout.java b/org.eclipse.jgit/src/org/eclipse/jgit/lib/WorkDirCheckout.java
index ef3d784..beab61a 100644
--- a/org.eclipse.jgit/src/org/eclipse/jgit/lib/WorkDirCheckout.java
+++ b/org.eclipse.jgit/src/org/eclipse/jgit/lib/WorkDirCheckout.java
@@ -165,6 +165,10 @@ private void checkoutTwoTrees() throws FileNotFoundException, IOException {
private void checkoutOutIndexNoHead() throws IOException {
new IndexTreeWalker(index, merge, root, new AbstractIndexTreeVisitor() {
public void visitEntry(TreeEntry m, Entry i, File f) throws IOException {
+ // TODO remove this once we support submodules
+ if (f.getName().equals(".gitmodules"))
+ throw new UnsupportedOperationException(
+ JGitText.get().submodulesNotSupported);
if (m == null) {
index.remove(root, f);
return;
diff --git a/org.eclipse.jgit/src/org/eclipse/jgit/merge/MergeChunk.java b/org.eclipse.jgit/src/org/eclipse/jgit/merge/MergeChunk.java
index 1127b89..72857ff 100644
--- a/org.eclipse.jgit/src/org/eclipse/jgit/merge/MergeChunk.java
+++ b/org.eclipse.jgit/src/org/eclipse/jgit/merge/MergeChunk.java
@@ -87,7 +87,7 @@ public enum ConflictState {
*
* @param sequenceIndex
* determines to which sequence this chunks belongs to. Same as
- * in {@link MergeResult#add(int, int, int, ConflictState)}
+ * in {@link org.eclipse.jgit.merge.MergeResult#add}
* @param begin
* the first element from the specified sequence which should be
* included in the merge result. Indexes start with 0.
@@ -109,8 +109,7 @@ protected MergeChunk(int sequenceIndex, int begin, int end,
/**
* @return the index of the sequence to which sequence this chunks belongs
- * to. Same as in
- * {@link MergeResult#add(int, int, int, ConflictState)}
+ * to. Same as in {@link org.eclipse.jgit.merge.MergeResult#add}
*/
public int getSequenceIndex() {
return sequenceIndex;
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 38af20f..68d60c0 100644
--- a/org.eclipse.jgit/src/org/eclipse/jgit/merge/Merger.java
+++ b/org.eclipse.jgit/src/org/eclipse/jgit/merge/Merger.java
@@ -51,9 +51,9 @@
import org.eclipse.jgit.lib.AnyObjectId;
import org.eclipse.jgit.lib.Constants;
import org.eclipse.jgit.lib.ObjectId;
-import org.eclipse.jgit.lib.ObjectWriter;
+import org.eclipse.jgit.lib.ObjectInserter;
import org.eclipse.jgit.lib.Repository;
-import org.eclipse.jgit.lib.WindowCursor;
+import org.eclipse.jgit.lib.ObjectReader;
import org.eclipse.jgit.revwalk.RevCommit;
import org.eclipse.jgit.revwalk.RevObject;
import org.eclipse.jgit.revwalk.RevTree;
@@ -70,10 +70,13 @@ public abstract class Merger {
/** The repository this merger operates on. */
protected final Repository db;
+ /** Reader to support {@link #walk} and other object loading. */
+ protected final ObjectReader reader;
+
/** A RevWalk for computing merge bases, or listing incoming commits. */
protected final RevWalk walk;
- private ObjectWriter writer;
+ private ObjectInserter inserter;
/** The original objects supplied in the merge; this can be any tree-ish. */
protected RevObject[] sourceObjects;
@@ -92,7 +95,8 @@ public abstract class Merger {
*/
protected Merger(final Repository local) {
db = local;
- walk = new RevWalk(db);
+ reader = db.newObjectReader();
+ walk = new RevWalk(reader);
}
/**
@@ -105,10 +109,10 @@ public Repository getRepository() {
/**
* @return an object writer to create objects in {@link #getRepository()}.
*/
- public ObjectWriter getObjectWriter() {
- if (writer == null)
- writer = new ObjectWriter(getRepository());
- return writer;
+ public ObjectInserter getObjectInserter() {
+ if (inserter == null)
+ inserter = getRepository().newObjectInserter();
+ return inserter;
}
/**
@@ -148,7 +152,13 @@ public boolean merge(final AnyObjectId[] tips) throws IOException {
for (int i = 0; i < sourceObjects.length; i++)
sourceTrees[i] = walk.parseTree(sourceObjects[i]);
- return mergeImpl();
+ try {
+ return mergeImpl();
+ } finally {
+ if (inserter != null)
+ inserter.release();
+ reader.release();
+ }
}
/**
@@ -202,12 +212,7 @@ protected AbstractTreeIterator mergeBase(final int aIdx, final int bIdx)
*/
protected AbstractTreeIterator openTree(final AnyObjectId treeId)
throws IncorrectObjectTypeException, IOException {
- final WindowCursor curs = new WindowCursor();
- try {
- return new CanonicalTreeParser(null, db, treeId, curs);
- } finally {
- curs.release();
- }
+ return new CanonicalTreeParser(null, reader, treeId);
}
/**
diff --git a/org.eclipse.jgit/src/org/eclipse/jgit/merge/StrategySimpleTwoWayInCore.java b/org.eclipse.jgit/src/org/eclipse/jgit/merge/StrategySimpleTwoWayInCore.java
index 6cd2445..29342a7 100644
--- a/org.eclipse.jgit/src/org/eclipse/jgit/merge/StrategySimpleTwoWayInCore.java
+++ b/org.eclipse.jgit/src/org/eclipse/jgit/merge/StrategySimpleTwoWayInCore.java
@@ -51,6 +51,7 @@
import org.eclipse.jgit.errors.UnmergedPathException;
import org.eclipse.jgit.lib.FileMode;
import org.eclipse.jgit.lib.ObjectId;
+import org.eclipse.jgit.lib.ObjectInserter;
import org.eclipse.jgit.lib.Repository;
import org.eclipse.jgit.treewalk.AbstractTreeIterator;
import org.eclipse.jgit.treewalk.NameConflictTreeWalk;
@@ -99,7 +100,7 @@ private static class InCoreMerger extends ThreeWayMerger {
InCoreMerger(final Repository local) {
super(local);
- tw = new NameConflictTreeWalk(db);
+ tw = new NameConflictTreeWalk(reader);
cache = DirCache.newInCore();
}
@@ -152,7 +153,9 @@ else if (tw.isSubtree()) {
if (hasConflict)
return false;
try {
- resultTree = cache.writeTree(getObjectWriter());
+ ObjectInserter odi = getObjectInserter();
+ resultTree = cache.writeTree(odi);
+ odi.flush();
return true;
} catch (UnmergedPathException upe) {
resultTree = null;
@@ -168,7 +171,7 @@ private void add(final int tree, final int stage) throws IOException {
final AbstractTreeIterator i = getTree(tree);
if (i != null) {
if (FileMode.TREE.equals(tw.getRawMode(tree))) {
- builder.addTree(tw.getRawPath(), stage, db, tw
+ builder.addTree(tw.getRawPath(), stage, reader, tw
.getObjectId(tree));
} else {
final DirCacheEntry e;
diff --git a/org.eclipse.jgit/src/org/eclipse/jgit/patch/FileHeader.java b/org.eclipse.jgit/src/org/eclipse/jgit/patch/FileHeader.java
index 0c24fc6..eae1040 100644
--- a/org.eclipse.jgit/src/org/eclipse/jgit/patch/FileHeader.java
+++ b/org.eclipse.jgit/src/org/eclipse/jgit/patch/FileHeader.java
@@ -385,12 +385,12 @@ int parseGitFileName(int ptr, final int end) {
if (buf[sp - 2] != '"') {
return eol;
}
- oldName = QuotedString.GIT_PATH.dequote(buf, bol, sp - 1);
- oldName = p1(oldName);
+ oldPath = QuotedString.GIT_PATH.dequote(buf, bol, sp - 1);
+ oldPath = p1(oldPath);
} else {
- oldName = decode(Constants.CHARSET, buf, aStart, sp - 1);
+ oldPath = decode(Constants.CHARSET, buf, aStart, sp - 1);
}
- newName = oldName;
+ newPath = oldPath;
return eol;
}
@@ -431,27 +431,27 @@ int parseGitHeaders(int ptr, final int end) {
parseNewFileMode(ptr, eol);
} else if (match(buf, ptr, COPY_FROM) >= 0) {
- oldName = parseName(oldName, ptr + COPY_FROM.length, eol);
+ oldPath = parseName(oldPath, ptr + COPY_FROM.length, eol);
changeType = ChangeType.COPY;
} else if (match(buf, ptr, COPY_TO) >= 0) {
- newName = parseName(newName, ptr + COPY_TO.length, eol);
+ newPath = parseName(newPath, ptr + COPY_TO.length, eol);
changeType = ChangeType.COPY;
} else if (match(buf, ptr, RENAME_OLD) >= 0) {
- oldName = parseName(oldName, ptr + RENAME_OLD.length, eol);
+ oldPath = parseName(oldPath, ptr + RENAME_OLD.length, eol);
changeType = ChangeType.RENAME;
} else if (match(buf, ptr, RENAME_NEW) >= 0) {
- newName = parseName(newName, ptr + RENAME_NEW.length, eol);
+ newPath = parseName(newPath, ptr + RENAME_NEW.length, eol);
changeType = ChangeType.RENAME;
} else if (match(buf, ptr, RENAME_FROM) >= 0) {
- oldName = parseName(oldName, ptr + RENAME_FROM.length, eol);
+ oldPath = parseName(oldPath, ptr + RENAME_FROM.length, eol);
changeType = ChangeType.RENAME;
} else if (match(buf, ptr, RENAME_TO) >= 0) {
- newName = parseName(newName, ptr + RENAME_TO.length, eol);
+ newPath = parseName(newPath, ptr + RENAME_TO.length, eol);
changeType = ChangeType.RENAME;
} else if (match(buf, ptr, SIMILARITY_INDEX) >= 0) {
@@ -474,14 +474,14 @@ int parseGitHeaders(int ptr, final int end) {
}
void parseOldName(int ptr, final int eol) {
- oldName = p1(parseName(oldName, ptr + OLD_NAME.length, eol));
- if (oldName == DEV_NULL)
+ oldPath = p1(parseName(oldPath, ptr + OLD_NAME.length, eol));
+ if (oldPath == DEV_NULL)
changeType = ChangeType.ADD;
}
void parseNewName(int ptr, final int eol) {
- newName = p1(parseName(newName, ptr + NEW_NAME.length, eol));
- if (newName == DEV_NULL)
+ newPath = p1(parseName(newPath, ptr + NEW_NAME.length, eol));
+ if (newPath == DEV_NULL)
changeType = ChangeType.DELETE;
}
diff --git a/org.eclipse.jgit/src/org/eclipse/jgit/revplot/PlotCommit.java b/org.eclipse.jgit/src/org/eclipse/jgit/revplot/PlotCommit.java
index a068943..dc9e032 100644
--- a/org.eclipse.jgit/src/org/eclipse/jgit/revplot/PlotCommit.java
+++ b/org.eclipse.jgit/src/org/eclipse/jgit/revplot/PlotCommit.java
@@ -65,19 +65,16 @@ public class PlotCommit<L extends PlotLane> extends RevCommit {
PlotCommit[] children;
- final Ref[] refs;
+ Ref[] refs;
/**
* Create a new commit.
*
* @param id
* the identity of this commit.
- * @param tags
- * the tags associated with this commit, null for no tags
*/
- protected PlotCommit(final AnyObjectId id, final Ref[] tags) {
+ protected PlotCommit(final AnyObjectId id) {
super(id);
- this.refs = tags;
passingLanes = NO_LANES;
children = NO_CHILDREN;
}
diff --git a/org.eclipse.jgit/src/org/eclipse/jgit/revplot/PlotWalk.java b/org.eclipse.jgit/src/org/eclipse/jgit/revplot/PlotWalk.java
index 6b4ed80..c69e66c 100644
--- a/org.eclipse.jgit/src/org/eclipse/jgit/revplot/PlotWalk.java
+++ b/org.eclipse.jgit/src/org/eclipse/jgit/revplot/PlotWalk.java
@@ -52,13 +52,16 @@
import java.util.Set;
import org.eclipse.jgit.JGitText;
+import org.eclipse.jgit.errors.IncorrectObjectTypeException;
+import org.eclipse.jgit.errors.MissingObjectException;
import org.eclipse.jgit.lib.AnyObjectId;
-import org.eclipse.jgit.lib.Commit;
+import org.eclipse.jgit.lib.PersonIdent;
import org.eclipse.jgit.lib.Ref;
import org.eclipse.jgit.lib.Repository;
-import org.eclipse.jgit.lib.Tag;
import org.eclipse.jgit.revwalk.RevCommit;
+import org.eclipse.jgit.revwalk.RevObject;
import org.eclipse.jgit.revwalk.RevSort;
+import org.eclipse.jgit.revwalk.RevTag;
import org.eclipse.jgit.revwalk.RevWalk;
/** Specialized RevWalk for visualization of a commit graph. */
@@ -93,14 +96,19 @@ public void sort(final RevSort s, final boolean use) {
@Override
protected RevCommit createCommit(final AnyObjectId id) {
- return new PlotCommit(id, getTags(id));
+ return new PlotCommit(id);
}
- /**
- * @param commitId
- * @return return the list of knows tags referring to this commit
- */
- protected Ref[] getTags(final AnyObjectId commitId) {
+ @Override
+ public RevCommit next() throws MissingObjectException,
+ IncorrectObjectTypeException, IOException {
+ PlotCommit<?> pc = (PlotCommit) super.next();
+ if (pc != null)
+ pc.refs = getTags(pc);
+ return pc;
+ }
+
+ private Ref[] getTags(final AnyObjectId commitId) {
Collection<Ref> list = reverseRefMap.get(commitId);
Ref[] tags;
if (list == null)
@@ -115,8 +123,8 @@ protected RevCommit createCommit(final AnyObjectId id) {
class PlotRefComparator implements Comparator<Ref> {
public int compare(Ref o1, Ref o2) {
try {
- Object obj1 = getRepository().mapObject(o1.getObjectId(), o1.getName());
- Object obj2 = getRepository().mapObject(o2.getObjectId(), o2.getName());
+ RevObject obj1 = parseAny(o1.getObjectId());
+ RevObject obj2 = parseAny(o2.getObjectId());
long t1 = timeof(obj1);
long t2 = timeof(obj2);
if (t1 > t2)
@@ -129,11 +137,15 @@ public int compare(Ref o1, Ref o2) {
return 0;
}
}
- long timeof(Object o) {
- if (o instanceof Commit)
- return ((Commit)o).getCommitter().getWhen().getTime();
- if (o instanceof Tag)
- return ((Tag)o).getTagger().getWhen().getTime();
+
+ long timeof(RevObject o) {
+ if (o instanceof RevCommit)
+ return ((RevCommit) o).getCommitTime();
+ if (o instanceof RevTag) {
+ RevTag tag = (RevTag) o;
+ PersonIdent who = tag.getTaggerIdent();
+ return who != null ? who.getWhen().getTime() : 0;
+ }
return 0;
}
}
diff --git a/org.eclipse.jgit/src/org/eclipse/jgit/revwalk/MergeBaseGenerator.java b/org.eclipse.jgit/src/org/eclipse/jgit/revwalk/MergeBaseGenerator.java
index edb8837..76510ce 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 @@ RevCommit next() throws MissingObjectException,
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 11d4001..a6ecfe2 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.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 RevObject nextObject() throws MissingObjectException,
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 void checkConnectivity() throws MissingObjectException,
final RevObject o = nextObject();
if (o == null)
break;
- if (o instanceof RevBlob && !db.hasObject(o))
+ if (o instanceof RevBlob && !reader.has(o))
throw new MissingObjectException(o, Constants.TYPE_BLOB);
}
}
@@ -371,6 +384,18 @@ public String getPathString() {
return last != null ? treeWalk.getEntryPathString() : null;
}
+ /**
+ * Get the current object's path hash code.
+ * <p>
+ * This method computes a hash code on the fly for this path, the hash is
+ * suitable to cluster objects that may have similar paths together.
+ *
+ * @return path hash code; any integer may be returned.
+ */
+ public int getPathHashCode() {
+ return last != null ? treeWalk.getEntryPathHashCode() : 0;
+ }
+
@Override
public void dispose() {
super.dispose();
@@ -403,7 +428,7 @@ private void markTreeUninteresting(final RevTree tree)
return;
tree.flags |= UNINTERESTING;
- treeWalk = treeWalk.resetRoot(db, tree, curs);
+ treeWalk = treeWalk.resetRoot(reader, tree);
while (!treeWalk.eof()) {
final FileMode mode = treeWalk.getEntryFileMode();
final int sType = mode.getObjectType();
@@ -419,7 +444,7 @@ private void markTreeUninteresting(final RevTree tree)
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 e723bce..0e2bb98 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 @@ RevCommit next() throws MissingObjectException,
for (;;) {
final RevCommit c = pending.next();
if (c == null) {
- walker.curs.release();
+ walker.reader.release();
return null;
}
@@ -174,7 +174,7 @@ else if (canDispose)
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 2d96bbf..84cc704 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 final int getCommitTime() {
* @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 5dde43b..a19f4d8 100644
--- a/org.eclipse.jgit/src/org/eclipse/jgit/revwalk/RevObject.java
+++ b/org.eclipse.jgit/src/org/eclipse/jgit/revwalk/RevObject.java
@@ -51,7 +51,6 @@
import org.eclipse.jgit.lib.AnyObjectId;
import org.eclipse.jgit.lib.Constants;
import org.eclipse.jgit.lib.ObjectId;
-import org.eclipse.jgit.lib.ObjectLoader;
/** Base object type accessed during revision walking. */
public abstract class RevObject extends ObjectId {
@@ -78,13 +77,7 @@ void parseBody(final RevWalk walk) throws MissingObjectException,
final byte[] loadCanonical(final RevWalk walk) throws IOException,
MissingObjectException, IncorrectObjectTypeException,
CorruptObjectException {
- final ObjectLoader ldr = walk.db.openObject(walk.curs, this);
- if (ldr == null)
- throw new MissingObjectException(this, getType());
- final byte[] data = ldr.getCachedBytes();
- if (getType() != ldr.getType())
- throw new IncorrectObjectTypeException(this, getType());
- return data;
+ return walk.reader.open(this, getType()).getCachedBytes();
}
/**
diff --git a/org.eclipse.jgit/src/org/eclipse/jgit/revwalk/RevTag.java b/org.eclipse.jgit/src/org/eclipse/jgit/revwalk/RevTag.java
index d2a665e..a04ea71 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 final String getShortMessage() {
* @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 a8c67c6..7406bb6 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.ObjectIdSubclassMap;
import org.eclipse.jgit.lib.ObjectLoader;
import org.eclipse.jgit.lib.Repository;
-import org.eclipse.jgit.lib.WindowCursor;
+import org.eclipse.jgit.lib.ObjectReader;
import org.eclipse.jgit.revwalk.filter.RevFilter;
import org.eclipse.jgit.treewalk.filter.TreeFilter;
@@ -157,9 +157,10 @@ public class RevWalk implements Iterable<RevCommit> {
private static final int APP_FLAGS = -1 & ~((1 << RESERVED_FLAGS) - 1);
- final Repository db;
+ /** Exists <b>ONLY</b> to support legacy Tag and Commit objects. */
+ final Repository repository;
- final WindowCursor curs;
+ final ObjectReader reader;
final MutableObjectId idBuffer;
@@ -189,11 +190,29 @@ public class RevWalk implements Iterable<RevCommit> {
* Create a new revision walker for a given repository.
*
* @param repo
- * the repository the walker will obtain data from.
+ * the repository the walker will obtain data from. An
+ * ObjectReader will be created by the walker, and must be
+ * released by the caller.
*/
public RevWalk(final Repository repo) {
- db = repo;
- curs = new WindowCursor();
+ this(repo, repo.newObjectReader());
+ }
+
+ /**
+ * Create a new revision walker for a given repository.
+ *
+ * @param or
+ * the reader the walker will obtain data from. The reader should
+ * be released by the caller when the walker is no longer
+ * required.
+ */
+ public RevWalk(ObjectReader or) {
+ this(null, or);
+ }
+
+ private RevWalk(final Repository repo, final ObjectReader or) {
+ repository = repo;
+ reader = or;
idBuffer = new MutableObjectId();
objects = new ObjectIdSubclassMap<RevObject>();
roots = new ArrayList<RevCommit>();
@@ -205,13 +224,19 @@ public RevWalk(final Repository repo) {
retainBody = true;
}
+ /** @return the reader this walker is using to load objects. */
+ public ObjectReader getObjectReader() {
+ return reader;
+ }
+
/**
- * Get the repository this walker loads objects from.
- *
- * @return the repository this walker was created to read.
+ * Release any resources used by this walker's reader.
+ * <p>
+ * A walker that has been released can be used again, but may need to be
+ * released after the subsequent usage.
*/
- public Repository getRepository() {
- return db;
+ public void release() {
+ reader.release();
}
/**
@@ -678,11 +703,7 @@ public RevObject lookupAny(final AnyObjectId id, final int type) {
public RevCommit parseCommit(final AnyObjectId id)
throws MissingObjectException, IncorrectObjectTypeException,
IOException {
- RevObject c = parseAny(id);
- while (c instanceof RevTag) {
- c = ((RevTag) c).getObject();
- parseHeaders(c);
- }
+ RevObject c = peel(parseAny(id));
if (!(c instanceof RevCommit))
throw new IncorrectObjectTypeException(id.toObjectId(),
Constants.TYPE_COMMIT);
@@ -709,11 +730,7 @@ public RevCommit parseCommit(final AnyObjectId id)
public RevTree parseTree(final AnyObjectId id)
throws MissingObjectException, IncorrectObjectTypeException,
IOException {
- RevObject c = parseAny(id);
- while (c instanceof RevTag) {
- c = ((RevTag) c).getObject();
- parseHeaders(c);
- }
+ RevObject c = peel(parseAny(id));
final RevTree t;
if (c instanceof RevCommit)
@@ -773,15 +790,12 @@ public RevObject parseAny(final AnyObjectId id)
throws MissingObjectException, IOException {
RevObject r = objects.get(id);
if (r == null) {
- final ObjectLoader ldr = db.openObject(curs, id);
- if (ldr == null)
- throw new MissingObjectException(id.toObjectId(), "unknown");
- final byte[] data = ldr.getCachedBytes();
+ final ObjectLoader ldr = reader.open(id);
final int type = ldr.getType();
switch (type) {
case Constants.OBJ_COMMIT: {
final RevCommit c = createCommit(id);
- c.parseCanonical(this, data);
+ c.parseCanonical(this, ldr.getCachedBytes());
r = c;
break;
}
@@ -797,7 +811,7 @@ public RevObject parseAny(final AnyObjectId id)
}
case Constants.OBJ_TAG: {
final RevTag t = new RevTag(id);
- t.parseCanonical(this, data);
+ t.parseCanonical(this, ldr.getCachedBytes());
r = t;
break;
}
@@ -848,6 +862,29 @@ public void parseBody(final RevObject obj)
}
/**
+ * Peel back annotated tags until a non-tag object is found.
+ *
+ * @param obj
+ * the starting object.
+ * @return If {@code obj} is not an annotated tag, {@code obj}. Otherwise
+ * the first non-tag object that {@code obj} references. The
+ * returned object's headers have been parsed.
+ * @throws MissingObjectException
+ * a referenced object cannot be found.
+ * @throws IOException
+ * a pack file or loose object could not be read.
+ */
+ public RevObject peel(RevObject obj) throws MissingObjectException,
+ IOException {
+ while (obj instanceof RevTag) {
+ parseHeaders(obj);
+ obj = ((RevTag) obj).getObject();
+ }
+ parseHeaders(obj);
+ return obj;
+ }
+
+ /**
* Create a new flag for application use during walking.
* <p>
* Applications are only assured to be able to create 24 unique flags on any
@@ -1023,7 +1060,7 @@ protected void reset(int retainFlags) {
}
}
- curs.release();
+ reader.release();
roots.clear();
queue = new DateRevQueue();
pending = new StartGenerator(this);
@@ -1038,11 +1075,12 @@ protected void reset(int retainFlags) {
* 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 4c5a2a7..41cfcf8 100644
--- a/org.eclipse.jgit/src/org/eclipse/jgit/revwalk/RewriteTreeFilter.java
+++ b/org.eclipse.jgit/src/org/eclipse/jgit/revwalk/RewriteTreeFilter.java
@@ -82,11 +82,11 @@ class RewriteTreeFilter extends RevFilter {
private final TreeWalk pathFilter;
- private final Repository repo;
+ private final Repository repository;
RewriteTreeFilter(final RevWalk walker, final TreeFilter t) {
- repo = walker.db;
- pathFilter = new TreeWalk(repo);
+ repository = walker.repository;
+ pathFilter = new TreeWalk(walker.reader);
pathFilter.setFilter(t);
pathFilter.setRecursive(t.shouldBeRecursive());
}
@@ -239,14 +239,14 @@ private void updateFollowFilter(ObjectId[] trees)
tw.reset(trees);
List<DiffEntry> files = DiffEntry.scan(tw);
- RenameDetector rd = new RenameDetector(repo);
+ RenameDetector rd = new RenameDetector(repository);
rd.addAll(files);
files = rd.compute();
TreeFilter newFilter = oldFilter;
for (DiffEntry ent : files) {
- if (isRename(ent) && ent.getNewName().equals(oldFilter.getPath())) {
- newFilter = FollowFilter.create(ent.getOldName());
+ if (isRename(ent) && ent.getNewPath().equals(oldFilter.getPath())) {
+ newFilter = FollowFilter.create(ent.getOldPath());
break;
}
}
diff --git a/org.eclipse.jgit/src/org/eclipse/jgit/lib/ByteArrayWindow.java b/org.eclipse.jgit/src/org/eclipse/jgit/storage/file/ByteArrayWindow.java
similarity index 76%
rename from org.eclipse.jgit/src/org/eclipse/jgit/lib/ByteArrayWindow.java
rename to org.eclipse.jgit/src/org/eclipse/jgit/storage/file/ByteArrayWindow.java
index 8042610..457c8dc 100644
--- a/org.eclipse.jgit/src/org/eclipse/jgit/lib/ByteArrayWindow.java
+++ b/org.eclipse.jgit/src/org/eclipse/jgit/storage/file/ByteArrayWindow.java
@@ -43,8 +43,11 @@
* ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
*/
-package org.eclipse.jgit.lib;
+package org.eclipse.jgit.storage.file;
+import java.io.IOException;
+import java.io.OutputStream;
+import java.util.zip.CRC32;
import java.util.zip.DataFormatException;
import java.util.zip.Inflater;
@@ -67,31 +70,25 @@ protected int copy(final int p, final byte[] b, final int o, int 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;
+ protected int setInput(final int pos, final Inflater inf)
+ throws DataFormatException {
+ int n = array.length - pos;
+ inf.setInput(array, pos, n);
+ return n;
}
- @Override
- protected void inflateVerify(final int pos, final Inflater inf)
+ void crc32(CRC32 out, long pos, int cnt) {
+ out.update(array, (int) (pos - start), cnt);
+ }
+
+ void write(OutputStream out, long pos, int cnt) throws IOException {
+ out.write(array, (int) (pos - start), cnt);
+ }
+
+ void check(Inflater inf, byte[] tmp, long pos, int cnt)
throws DataFormatException {
- while (!inf.finished()) {
- if (inf.needsInput()) {
- inf.setInput(array, pos, array.length - pos);
- break;
- }
- inf.inflate(verifyGarbageBuffer, 0, verifyGarbageBuffer.length);
- }
- while (!inf.finished() && !inf.needsInput())
- inf.inflate(verifyGarbageBuffer, 0, verifyGarbageBuffer.length);
+ inf.setInput(array, (int) (pos - start), cnt);
+ while (inf.inflate(tmp, 0, tmp.length) > 0)
+ continue;
}
}
diff --git a/org.eclipse.jgit/src/org/eclipse/jgit/lib/ByteBufferWindow.java b/org.eclipse.jgit/src/org/eclipse/jgit/storage/file/ByteBufferWindow.java
similarity index 72%
rename from org.eclipse.jgit/src/org/eclipse/jgit/lib/ByteBufferWindow.java
rename to org.eclipse.jgit/src/org/eclipse/jgit/storage/file/ByteBufferWindow.java
index 1b29934..29a0159 100644
--- a/org.eclipse.jgit/src/org/eclipse/jgit/lib/ByteBufferWindow.java
+++ b/org.eclipse.jgit/src/org/eclipse/jgit/storage/file/ByteBufferWindow.java
@@ -43,7 +43,7 @@
* ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
*/
-package org.eclipse.jgit.lib;
+package org.eclipse.jgit.storage.file;
import java.nio.ByteBuffer;
import java.util.zip.DataFormatException;
@@ -72,39 +72,13 @@ protected int copy(final int p, final byte[] b, final int o, int 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;
- }
-
- @Override
- protected void inflateVerify(final int pos, final Inflater inf)
+ protected int setInput(final int pos, final Inflater inf)
throws DataFormatException {
- final byte[] tmp = new byte[512];
final ByteBuffer s = buffer.slice();
s.position(pos);
- while (s.remaining() > 0 && !inf.finished()) {
- if (inf.needsInput()) {
- final int n = Math.min(s.remaining(), tmp.length);
- s.get(tmp, 0, n);
- inf.setInput(tmp, 0, n);
- }
- inf.inflate(verifyGarbageBuffer, 0, verifyGarbageBuffer.length);
- }
- while (!inf.finished() && !inf.needsInput())
- inf.inflate(verifyGarbageBuffer, 0, verifyGarbageBuffer.length);
+ final byte[] tmp = new byte[Math.min(s.remaining(), 512)];
+ s.get(tmp, 0, tmp.length);
+ inf.setInput(tmp, 0, tmp.length);
+ return tmp.length;
}
}
diff --git a/org.eclipse.jgit/src/org/eclipse/jgit/lib/ByteWindow.java b/org.eclipse.jgit/src/org/eclipse/jgit/storage/file/ByteWindow.java
similarity index 62%
rename from org.eclipse.jgit/src/org/eclipse/jgit/lib/ByteWindow.java
rename to org.eclipse.jgit/src/org/eclipse/jgit/storage/file/ByteWindow.java
index cbef421..f92efb4 100644
--- a/org.eclipse.jgit/src/org/eclipse/jgit/lib/ByteWindow.java
+++ b/org.eclipse.jgit/src/org/eclipse/jgit/storage/file/ByteWindow.java
@@ -42,7 +42,7 @@
* ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
*/
-package org.eclipse.jgit.lib;
+package org.eclipse.jgit.storage.file;
import java.util.zip.DataFormatException;
import java.util.zip.Inflater;
@@ -117,69 +117,10 @@ final int copy(long pos, byte[] dstbuf, int dstoff, int cnt) {
*/
protected abstract int copy(int pos, byte[] dstbuf, int dstoff, int cnt);
- /**
- * Pump bytes into the supplied inflater as input.
- *
- * @param pos
- * offset within the file to start supplying input from.
- * @param dstbuf
- * destination buffer the inflater should output decompressed
- * data to.
- * @param dstoff
- * current offset within <code>dstbuf</code> to inflate into.
- * @param inf
- * the inflater to feed input to. The caller is responsible for
- * initializing the inflater as multiple windows may need to
- * supply data to the same inflater to completely decompress
- * something.
- * @return updated <code>dstoff</code> based on the number of bytes
- * successfully copied into <code>dstbuf</code> by
- * <code>inf</code>. If the inflater is not yet finished then
- * another window's data must still be supplied as input to finish
- * decompression.
- * @throws DataFormatException
- * the inflater encountered an invalid chunk of data. Data
- * stream corruption is likely.
- */
- final int inflate(long pos, byte[] dstbuf, int dstoff, Inflater inf)
- throws DataFormatException {
- return inflate((int) (pos - start), dstbuf, dstoff, inf);
+ final int setInput(long pos, Inflater inf) throws DataFormatException {
+ return setInput((int) (pos - start), inf);
}
- /**
- * Pump bytes into the supplied inflater as input.
- *
- * @param pos
- * offset within the window to start supplying input from.
- * @param dstbuf
- * destination buffer the inflater should output decompressed
- * data to.
- * @param dstoff
- * current offset within <code>dstbuf</code> to inflate into.
- * @param inf
- * the inflater to feed input to. The caller is responsible for
- * initializing the inflater as multiple windows may need to
- * supply data to the same inflater to completely decompress
- * something.
- * @return updated <code>dstoff</code> based on the number of bytes
- * successfully copied into <code>dstbuf</code> by
- * <code>inf</code>. If the inflater is not yet finished then
- * another window's data must still be supplied as input to finish
- * decompression.
- * @throws DataFormatException
- * the inflater encountered an invalid chunk of data. Data
- * stream corruption is likely.
- */
- protected abstract int inflate(int pos, byte[] dstbuf, int dstoff,
- Inflater inf) throws DataFormatException;
-
- protected static final byte[] verifyGarbageBuffer = new byte[2048];
-
- final void inflateVerify(final long pos, final Inflater inf)
- throws DataFormatException {
- inflateVerify((int) (pos - start), inf);
- }
-
- protected abstract void inflateVerify(int pos, Inflater inf)
+ protected abstract int setInput(int pos, Inflater inf)
throws DataFormatException;
}
diff --git a/org.eclipse.jgit/src/org/eclipse/jgit/storage/file/CachedObjectDirectory.java b/org.eclipse.jgit/src/org/eclipse/jgit/storage/file/CachedObjectDirectory.java
new file mode 100644
index 0000000..8ea0b85
--- /dev/null
+++ b/org.eclipse.jgit/src/org/eclipse/jgit/storage/file/CachedObjectDirectory.java
@@ -0,0 +1,213 @@
+/*
+ * Copyright (C) 2010, Constantine Plotnikov <constantine.plotnikov@gmail.com>
+ * Copyright (C) 2010, JetBrains s.r.o.
+ * and other copyright owners as documented in the project's IP log.
+ *
+ * This program and the accompanying materials are made available
+ * under the terms of the Eclipse Distribution License v1.0 which
+ * accompanies this distribution, is reproduced below, and is
+ * available at http://www.eclipse.org/org/documents/edl-v10.php
+ *
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or
+ * without modification, are permitted provided that the following
+ * conditions are met:
+ *
+ * - Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ *
+ * - Redistributions in binary form must reproduce the above
+ * copyright notice, this list of conditions and the following
+ * disclaimer in the documentation and/or other materials provided
+ * with the distribution.
+ *
+ * - Neither the name of the Eclipse Foundation, Inc. nor the
+ * names of its contributors may be used to endorse or promote
+ * products derived from this software without specific prior
+ * written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND
+ * CONTRIBUTORS "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES,
+ * INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
+ * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR
+ * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
+ * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT
+ * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
+ * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER
+ * CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT,
+ * STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
+ * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF
+ * ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ */
+
+package org.eclipse.jgit.storage.file;
+
+import java.io.File;
+import java.io.IOException;
+
+import org.eclipse.jgit.lib.AnyObjectId;
+import org.eclipse.jgit.lib.Constants;
+import org.eclipse.jgit.lib.ObjectDatabase;
+import org.eclipse.jgit.lib.ObjectId;
+import org.eclipse.jgit.lib.ObjectIdSubclassMap;
+import org.eclipse.jgit.lib.ObjectInserter;
+import org.eclipse.jgit.lib.ObjectLoader;
+import org.eclipse.jgit.storage.pack.ObjectToPack;
+import org.eclipse.jgit.storage.pack.PackWriter;
+
+/**
+ * The cached instance of an {@link ObjectDirectory}.
+ * <p>
+ * This class caches the list of loose objects in memory, so the file system is
+ * not queried with stat calls.
+ */
+class CachedObjectDirectory extends FileObjectDatabase {
+ /**
+ * The set that contains unpacked objects identifiers, it is created when
+ * the cached instance is created.
+ */
+ private final ObjectIdSubclassMap<ObjectId> unpackedObjects = new ObjectIdSubclassMap<ObjectId>();
+
+ private final ObjectDirectory wrapped;
+
+ private AlternateHandle[] alts;
+
+ /**
+ * The constructor
+ *
+ * @param wrapped
+ * the wrapped database
+ */
+ 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 has(final AnyObjectId objectId) {
+ return hasObjectImpl1(objectId);
+ }
+
+ @Override
+ boolean hasObject1(AnyObjectId objectId) {
+ return unpackedObjects.contains(objectId)
+ || wrapped.hasObject1(objectId);
+ }
+
+ @Override
+ ObjectLoader openObject(final WindowCursor curs,
+ final AnyObjectId objectId) throws IOException {
+ return openObjectImpl1(curs, objectId);
+ }
+
+ @Override
+ ObjectLoader openObject1(WindowCursor curs, AnyObjectId objectId)
+ throws IOException {
+ if (unpackedObjects.contains(objectId))
+ return wrapped.openObject2(curs, objectId.name(), objectId);
+ return wrapped.openObject1(curs, objectId);
+ }
+
+ @Override
+ boolean hasObject2(String objectId) {
+ // This method should never be invoked.
+ throw new UnsupportedOperationException();
+ }
+
+ @Override
+ ObjectLoader openObject2(WindowCursor curs, String objectName,
+ AnyObjectId objectId) throws IOException {
+ // This method should never be invoked.
+ throw new UnsupportedOperationException();
+ }
+
+ @Override
+ long getObjectSize1(WindowCursor curs, AnyObjectId objectId) throws IOException {
+ if (unpackedObjects.contains(objectId))
+ return wrapped.getObjectSize2(curs, objectId.name(), objectId);
+ return wrapped.getObjectSize1(curs, objectId);
+ }
+
+ @Override
+ long getObjectSize2(WindowCursor curs, String objectName, AnyObjectId objectId)
+ throws IOException {
+ // This method should never be invoked.
+ throw new UnsupportedOperationException();
+ }
+
+ @Override
+ void selectObjectRepresentation(PackWriter packer, ObjectToPack otp,
+ WindowCursor curs) throws IOException {
+ wrapped.selectObjectRepresentation(packer, otp, curs);
+ }
+
+ @Override
+ int getStreamFileThreshold() {
+ return wrapped.getStreamFileThreshold();
+ }
+}
diff --git a/org.eclipse.jgit/src/org/eclipse/jgit/lib/FileBasedConfig.java b/org.eclipse.jgit/src/org/eclipse/jgit/storage/file/FileBasedConfig.java
similarity index 87%
rename from org.eclipse.jgit/src/org/eclipse/jgit/lib/FileBasedConfig.java
rename to org.eclipse.jgit/src/org/eclipse/jgit/storage/file/FileBasedConfig.java
index eb00917..8ffbe80 100644
--- a/org.eclipse.jgit/src/org/eclipse/jgit/lib/FileBasedConfig.java
+++ b/org.eclipse.jgit/src/org/eclipse/jgit/storage/file/FileBasedConfig.java
@@ -47,7 +47,7 @@
* ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
*/
-package org.eclipse.jgit.lib;
+package org.eclipse.jgit.storage.file;
import java.io.File;
import java.io.FileNotFoundException;
@@ -56,24 +56,32 @@
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.lib.StoredConfig;
+import org.eclipse.jgit.util.FS;
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 {
+public class FileBasedConfig extends StoredConfig {
private final File configFile;
private volatile long lastModified;
+ private final FS fs;
/**
* Create a configuration with no default fallback.
*
* @param cfgLocation
* the location of the configuration file on the file system
+ * @param fs
+ * the file system abstraction which will be necessary to perform
+ * certain file system operations.
*/
- public FileBasedConfig(File cfgLocation) {
- this(null, cfgLocation);
+ public FileBasedConfig(File cfgLocation, FS fs) {
+ this(null, cfgLocation, fs);
}
/**
@@ -83,10 +91,14 @@ public FileBasedConfig(File cfgLocation) {
* the base configuration file
* @param cfgLocation
* the location of the configuration file on the file system
+ * @param fs
+ * the file system abstraction which will be necessary to perform
+ * certain file system operations.
*/
- public FileBasedConfig(Config base, File cfgLocation) {
+ public FileBasedConfig(Config base, File cfgLocation, FS fs) {
super(base);
configFile = cfgLocation;
+ this.fs = fs;
}
/** @return location of the configuration file on disk */
@@ -105,6 +117,7 @@ public final File getFile() {
* @throws ConfigInvalidException
* the file is not a properly formatted configuration file.
*/
+ @Override
public void load() throws IOException, ConfigInvalidException {
lastModified = getFile().lastModified();
try {
@@ -134,7 +147,7 @@ public void load() throws IOException, ConfigInvalidException {
*/
public void save() throws IOException {
final byte[] out = Constants.encode(toText());
- final LockFile lf = new LockFile(getFile());
+ final LockFile lf = new LockFile(getFile(), fs);
if (!lf.lock())
throw new IOException(MessageFormat.format(JGitText.get().cannotLockFile, getFile()));
try {
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 0000000..250c7ca
--- /dev/null
+++ b/org.eclipse.jgit/src/org/eclipse/jgit/storage/file/FileObjectDatabase.java
@@ -0,0 +1,271 @@
+/*
+ * Copyright (C) 2010, Google Inc.
+ * and other copyright owners as documented in the project's IP log.
+ *
+ * This program and the accompanying materials are made available
+ * under the terms of the Eclipse Distribution License v1.0 which
+ * accompanies this distribution, is reproduced below, and is
+ * available at http://www.eclipse.org/org/documents/edl-v10.php
+ *
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or
+ * without modification, are permitted provided that the following
+ * conditions are met:
+ *
+ * - Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ *
+ * - Redistributions in binary form must reproduce the above
+ * copyright notice, this list of conditions and the following
+ * disclaimer in the documentation and/or other materials provided
+ * with the distribution.
+ *
+ * - Neither the name of the Eclipse Foundation, Inc. nor the
+ * names of its contributors may be used to endorse or promote
+ * products derived from this software without specific prior
+ * written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND
+ * CONTRIBUTORS "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES,
+ * INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
+ * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR
+ * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
+ * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT
+ * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
+ * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER
+ * CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT,
+ * STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
+ * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF
+ * ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ */
+
+package org.eclipse.jgit.storage.file;
+
+import java.io.File;
+import java.io.IOException;
+
+import org.eclipse.jgit.lib.AnyObjectId;
+import org.eclipse.jgit.lib.ObjectDatabase;
+import org.eclipse.jgit.lib.ObjectLoader;
+import org.eclipse.jgit.lib.ObjectReader;
+import org.eclipse.jgit.storage.pack.ObjectToPack;
+import org.eclipse.jgit.storage.pack.PackWriter;
+
+abstract class FileObjectDatabase extends ObjectDatabase {
+ @Override
+ public ObjectReader newReader() {
+ return new WindowCursor(this);
+ }
+
+ /**
+ * Does the requested object exist in this database?
+ * <p>
+ * Alternates (if present) are searched automatically.
+ *
+ * @param objectId
+ * identity of the object to test for existence of.
+ * @return true if the specified object is stored in this database, or any
+ * of the alternate databases.
+ */
+ public boolean has(final AnyObjectId objectId) {
+ return hasObjectImpl1(objectId) || hasObjectImpl2(objectId.name());
+ }
+
+ final boolean hasObjectImpl1(final AnyObjectId objectId) {
+ if (hasObject1(objectId))
+ return true;
+
+ for (final AlternateHandle alt : myAlternates()) {
+ if (alt.db.hasObjectImpl1(objectId))
+ return true;
+ }
+
+ return tryAgain1() && hasObject1(objectId);
+ }
+
+ final boolean hasObjectImpl2(final String objectId) {
+ if (hasObject2(objectId))
+ return true;
+
+ for (final AlternateHandle alt : myAlternates()) {
+ if (alt.db.hasObjectImpl2(objectId))
+ return true;
+ }
+
+ return false;
+ }
+
+ /**
+ * Open an object from this database.
+ * <p>
+ * Alternates (if present) are searched automatically.
+ *
+ * @param curs
+ * temporary working space associated with the calling thread.
+ * @param objectId
+ * identity of the object to open.
+ * @return a {@link ObjectLoader} for accessing the data of the named
+ * object, or null if the object does not exist.
+ * @throws IOException
+ */
+ ObjectLoader openObject(final WindowCursor curs, final AnyObjectId objectId)
+ throws IOException {
+ ObjectLoader ldr;
+
+ ldr = openObjectImpl1(curs, objectId);
+ if (ldr != null)
+ return ldr;
+
+ ldr = openObjectImpl2(curs, objectId.name(), objectId);
+ if (ldr != null)
+ return ldr;
+
+ return null;
+ }
+
+ final ObjectLoader openObjectImpl1(final WindowCursor curs,
+ final AnyObjectId objectId) throws IOException {
+ ObjectLoader ldr;
+
+ ldr = openObject1(curs, objectId);
+ if (ldr != null)
+ return ldr;
+
+ for (final AlternateHandle alt : myAlternates()) {
+ ldr = alt.db.openObjectImpl1(curs, objectId);
+ if (ldr != null)
+ return ldr;
+ }
+
+ if (tryAgain1()) {
+ ldr = openObject1(curs, objectId);
+ if (ldr != null)
+ return ldr;
+ }
+
+ return null;
+ }
+
+ final ObjectLoader openObjectImpl2(final WindowCursor curs,
+ final String objectName, final AnyObjectId objectId)
+ throws IOException {
+ ObjectLoader ldr;
+
+ ldr = openObject2(curs, objectName, objectId);
+ if (ldr != null)
+ return ldr;
+
+ for (final AlternateHandle alt : myAlternates()) {
+ ldr = alt.db.openObjectImpl2(curs, objectName, objectId);
+ if (ldr != null)
+ return ldr;
+ }
+
+ return null;
+ }
+
+ long getObjectSize(WindowCursor curs, AnyObjectId objectId)
+ throws IOException {
+ long sz = getObjectSizeImpl1(curs, objectId);
+ if (0 <= sz)
+ return sz;
+ return getObjectSizeImpl2(curs, objectId.name(), objectId);
+ }
+
+ final long getObjectSizeImpl1(final WindowCursor curs,
+ final AnyObjectId objectId) throws IOException {
+ long sz;
+
+ sz = getObjectSize1(curs, objectId);
+ if (0 <= sz)
+ return sz;
+
+ for (final AlternateHandle alt : myAlternates()) {
+ sz = alt.db.getObjectSizeImpl1(curs, objectId);
+ if (0 <= sz)
+ return sz;
+ }
+
+ if (tryAgain1()) {
+ sz = getObjectSize1(curs, objectId);
+ if (0 <= sz)
+ return sz;
+ }
+
+ return -1;
+ }
+
+ final long getObjectSizeImpl2(final WindowCursor curs,
+ final String objectName, final AnyObjectId objectId)
+ throws IOException {
+ long sz;
+
+ sz = getObjectSize2(curs, objectName, objectId);
+ if (0 <= sz)
+ return sz;
+
+ for (final AlternateHandle alt : myAlternates()) {
+ sz = alt.db.getObjectSizeImpl2(curs, objectName, objectId);
+ if (0 <= sz)
+ return sz;
+ }
+
+ return -1;
+ }
+
+ abstract void selectObjectRepresentation(PackWriter packer,
+ ObjectToPack otp, WindowCursor curs) throws IOException;
+
+ abstract File getDirectory();
+
+ abstract AlternateHandle[] myAlternates();
+
+ abstract boolean tryAgain1();
+
+ abstract boolean hasObject1(AnyObjectId objectId);
+
+ abstract boolean hasObject2(String objectId);
+
+ abstract ObjectLoader openObject1(WindowCursor curs, AnyObjectId objectId)
+ throws IOException;
+
+ abstract ObjectLoader openObject2(WindowCursor curs, String objectName,
+ AnyObjectId objectId) throws IOException;
+
+ abstract long getObjectSize1(WindowCursor curs, AnyObjectId objectId)
+ throws IOException;
+
+ abstract long getObjectSize2(WindowCursor curs, String objectName,
+ AnyObjectId objectId) throws IOException;
+
+ abstract FileObjectDatabase newCachedFileObjectDatabase();
+
+ abstract int getStreamFileThreshold();
+
+ static class AlternateHandle {
+ final FileObjectDatabase db;
+
+ AlternateHandle(FileObjectDatabase db) {
+ this.db = db;
+ }
+
+ void close() {
+ db.close();
+ }
+ }
+
+ static class AlternateRepository extends AlternateHandle {
+ final FileRepository repository;
+
+ AlternateRepository(FileRepository r) {
+ super(r.getObjectDatabase());
+ repository = r;
+ }
+
+ void close() {
+ repository.close();
+ }
+ }
+}
diff --git a/org.eclipse.jgit/src/org/eclipse/jgit/storage/file/FileRepository.java b/org.eclipse.jgit/src/org/eclipse/jgit/storage/file/FileRepository.java
new file mode 100644
index 0000000..69cce71
--- /dev/null
+++ b/org.eclipse.jgit/src/org/eclipse/jgit/storage/file/FileRepository.java
@@ -0,0 +1,330 @@
+/*
+ * Copyright (C) 2007, Dave Watson <dwatson@mimvista.com>
+ * Copyright (C) 2008-2010, Google Inc.
+ * Copyright (C) 2006-2010, Robin Rosenberg <robin.rosenberg@dewire.com>
+ * Copyright (C) 2006-2008, Shawn O. Pearce <spearce@spearce.org>
+ * and other copyright owners as documented in the project's IP log.
+ *
+ * This program and the accompanying materials are made available
+ * under the terms of the Eclipse Distribution License v1.0 which
+ * accompanies this distribution, is reproduced below, and is
+ * available at http://www.eclipse.org/org/documents/edl-v10.php
+ *
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or
+ * without modification, are permitted provided that the following
+ * conditions are met:
+ *
+ * - Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ *
+ * - Redistributions in binary form must reproduce the above
+ * copyright notice, this list of conditions and the following
+ * disclaimer in the documentation and/or other materials provided
+ * with the distribution.
+ *
+ * - Neither the name of the Eclipse Foundation, Inc. nor the
+ * names of its contributors may be used to endorse or promote
+ * products derived from this software without specific prior
+ * written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND
+ * CONTRIBUTORS "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES,
+ * INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
+ * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR
+ * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
+ * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT
+ * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
+ * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER
+ * CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT,
+ * STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
+ * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF
+ * ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ */
+
+package org.eclipse.jgit.storage.file;
+
+import java.io.File;
+import java.io.IOException;
+import java.text.MessageFormat;
+import java.util.HashSet;
+import java.util.Set;
+
+import org.eclipse.jgit.JGitText;
+import org.eclipse.jgit.errors.ConfigInvalidException;
+import org.eclipse.jgit.lib.BaseRepositoryBuilder;
+import org.eclipse.jgit.lib.ConfigConstants;
+import org.eclipse.jgit.lib.Constants;
+import org.eclipse.jgit.lib.ObjectId;
+import org.eclipse.jgit.lib.Ref;
+import org.eclipse.jgit.lib.RefDatabase;
+import org.eclipse.jgit.lib.RefUpdate;
+import org.eclipse.jgit.lib.Repository;
+import org.eclipse.jgit.storage.file.FileObjectDatabase.AlternateHandle;
+import org.eclipse.jgit.storage.file.FileObjectDatabase.AlternateRepository;
+import org.eclipse.jgit.util.SystemReader;
+
+/**
+ * Represents a Git repository. A repository holds all objects and refs used for
+ * managing source code (could by any type of file, but source code is what
+ * SCM's are typically used for).
+ *
+ * In Git terms all data is stored in GIT_DIR, typically a directory called
+ * .git. A work tree is maintained unless the repository is a bare repository.
+ * Typically the .git directory is located at the root of the work dir.
+ *
+ * <ul>
+ * <li>GIT_DIR
+ * <ul>
+ * <li>objects/ - objects</li>
+ * <li>refs/ - tags and heads</li>
+ * <li>config - configuration</li>
+ * <li>info/ - more configurations</li>
+ * </ul>
+ * </li>
+ * </ul>
+ * <p>
+ * This class is thread-safe.
+ * <p>
+ * This implementation only handles a subtly undocumented subset of git features.
+ *
+ */
+public class FileRepository extends Repository {
+ private final FileBasedConfig userConfig;
+
+ private final FileBasedConfig repoConfig;
+
+ private final RefDatabase refs;
+
+ private final ObjectDirectory objectDatabase;
+
+ /**
+ * Construct a representation of a Git repository.
+ * <p>
+ * The work tree, object directory, alternate object directories and index
+ * file locations are deduced from the given git directory and the default
+ * rules by running {@link FileRepositoryBuilder}. This constructor is the
+ * same as saying:
+ *
+ * <pre>
+ * new FileRepositoryBuilder().setGitDir(gitDir).build()
+ * </pre>
+ *
+ * @param gitDir
+ * GIT_DIR (the location of the repository metadata).
+ * @throws IOException
+ * the repository appears to already exist but cannot be
+ * accessed.
+ * @see FileRepositoryBuilder
+ */
+ public FileRepository(final File gitDir) throws IOException {
+ this(new FileRepositoryBuilder().setGitDir(gitDir).setup());
+ }
+
+ /**
+ * Create a repository using the local file system.
+ *
+ * @param options
+ * description of the repository's important paths.
+ * @throws IOException
+ * the user configuration file or repository configuration file
+ * cannot be accessed.
+ */
+ public FileRepository(final BaseRepositoryBuilder options) throws IOException {
+ super(options);
+
+ userConfig = SystemReader.getInstance().openUserConfig(getFS());
+ repoConfig = new FileBasedConfig(userConfig, //
+ getFS().resolve(getDirectory(), "config"), //
+ getFS());
+
+ loadUserConfig();
+ loadRepoConfig();
+
+ refs = new RefDirectory(this);
+ objectDatabase = new ObjectDirectory(repoConfig, //
+ options.getObjectDirectory(), //
+ options.getAlternateObjectDirectories(), //
+ getFS());
+ getListenerList().addConfigChangedListener(objectDatabase);
+
+ if (objectDatabase.exists()) {
+ final String repositoryFormatVersion = getConfig().getString(
+ ConfigConstants.CONFIG_CORE_SECTION, null,
+ ConfigConstants.CONFIG_KEY_REPO_FORMAT_VERSION);
+ if (!"0".equals(repositoryFormatVersion)) {
+ throw new IOException(MessageFormat.format(
+ JGitText.get().unknownRepositoryFormat2,
+ repositoryFormatVersion));
+ }
+ }
+ }
+
+ private void loadUserConfig() throws IOException {
+ try {
+ userConfig.load();
+ } catch (ConfigInvalidException e1) {
+ IOException e2 = new IOException(MessageFormat.format(JGitText
+ .get().userConfigFileInvalid, userConfig.getFile()
+ .getAbsolutePath(), e1));
+ e2.initCause(e1);
+ throw e2;
+ }
+ }
+
+ private void loadRepoConfig() throws IOException {
+ try {
+ repoConfig.load();
+ } catch (ConfigInvalidException e1) {
+ IOException e2 = new IOException(JGitText.get().unknownRepositoryFormat);
+ e2.initCause(e1);
+ throw e2;
+ }
+ }
+
+ /**
+ * Create a new Git repository initializing the necessary files and
+ * directories.
+ *
+ * @param bare
+ * if true, a bare repository is created.
+ *
+ * @throws IOException
+ * in case of IO problem
+ */
+ public void create(boolean bare) throws IOException {
+ final FileBasedConfig cfg = getConfig();
+ if (cfg.getFile().exists()) {
+ throw new IllegalStateException(MessageFormat.format(
+ JGitText.get().repositoryAlreadyExists, getDirectory()));
+ }
+ getDirectory().mkdirs();
+ refs.create();
+ objectDatabase.create();
+
+ new File(getDirectory(), "branches").mkdir();
+
+ RefUpdate head = updateRef(Constants.HEAD);
+ head.disableRefLog();
+ head.link(Constants.R_HEADS + Constants.MASTER);
+
+ cfg.setInt(ConfigConstants.CONFIG_CORE_SECTION, null,
+ ConfigConstants.CONFIG_KEY_REPO_FORMAT_VERSION, 0);
+ cfg.setBoolean(ConfigConstants.CONFIG_CORE_SECTION, null,
+ ConfigConstants.CONFIG_KEY_FILEMODE, true);
+ if (bare)
+ cfg.setBoolean(ConfigConstants.CONFIG_CORE_SECTION, null,
+ ConfigConstants.CONFIG_KEY_BARE, true);
+ cfg.setBoolean(ConfigConstants.CONFIG_CORE_SECTION, null,
+ ConfigConstants.CONFIG_KEY_LOGALLREFUPDATES, !bare);
+ cfg.setBoolean(ConfigConstants.CONFIG_CORE_SECTION, null,
+ ConfigConstants.CONFIG_KEY_AUTOCRLF, false);
+ cfg.save();
+ }
+
+ /**
+ * @return the directory containing the objects owned by this repository.
+ */
+ public File getObjectsDirectory() {
+ return objectDatabase.getDirectory();
+ }
+
+ /**
+ * @return the object database which stores this repository's data.
+ */
+ public ObjectDirectory getObjectDatabase() {
+ return objectDatabase;
+ }
+
+ /** @return the reference database which stores the reference namespace. */
+ public RefDatabase getRefDatabase() {
+ return refs;
+ }
+
+ /**
+ * @return the configuration of this repository
+ */
+ public FileBasedConfig getConfig() {
+ if (userConfig.isOutdated()) {
+ try {
+ loadUserConfig();
+ } catch (IOException e) {
+ throw new RuntimeException(e);
+ }
+ }
+ if (repoConfig.isOutdated()) {
+ try {
+ loadRepoConfig();
+ } catch (IOException e) {
+ throw new RuntimeException(e);
+ }
+ }
+ return repoConfig;
+ }
+
+ /**
+ * Objects known to exist but not expressed by {@link #getAllRefs()}.
+ * <p>
+ * When a repository borrows objects from another repository, it can
+ * advertise that it safely has that other repository's references, without
+ * exposing any other details about the other repository. This may help
+ * a client trying to push changes avoid pushing more than it needs to.
+ *
+ * @return unmodifiable collection of other known objects.
+ */
+ public Set<ObjectId> getAdditionalHaves() {
+ HashSet<ObjectId> r = new HashSet<ObjectId>();
+ for (AlternateHandle d : objectDatabase. myAlternates()) {
+ if (d instanceof AlternateRepository) {
+ Repository repo;
+
+ repo = ((AlternateRepository) d).repository;
+ for (Ref ref : repo.getAllRefs().values())
+ r.add(ref.getObjectId());
+ r.addAll(repo.getAdditionalHaves());
+ }
+ }
+ return r;
+ }
+
+ /**
+ * Add a single existing pack to the list of available pack files.
+ *
+ * @param pack
+ * path of the pack file to open.
+ * @param idx
+ * path of the corresponding index file.
+ * @throws IOException
+ * index file could not be opened, read, or is not recognized as
+ * a Git pack file index.
+ */
+ public void openPack(final File pack, final File idx) throws IOException {
+ objectDatabase.openPack(pack, idx);
+ }
+
+ /**
+ * Force a scan for changed refs.
+ *
+ * @throws IOException
+ */
+ public void scanForRepoChanges() throws IOException {
+ getAllRefs(); // This will look for changes to refs
+ if (!isBare())
+ getIndex(); // This will detect changes in the index
+ }
+
+ /**
+ * @param refName
+ * @return a {@link ReflogReader} for the supplied refname, or null if the
+ * named ref does not exist.
+ * @throws IOException the ref could not be accessed.
+ */
+ public ReflogReader getReflogReader(String refName) throws IOException {
+ Ref ref = getRef(refName);
+ if (ref != null)
+ return new ReflogReader(this, ref.getName());
+ return null;
+ }
+}
diff --git a/org.eclipse.jgit/src/org/eclipse/jgit/storage/file/FileRepositoryBuilder.java b/org.eclipse.jgit/src/org/eclipse/jgit/storage/file/FileRepositoryBuilder.java
new file mode 100644
index 0000000..31d3e99
--- /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}.
+ * <p>
+ * Applications must set one of {@link #setGitDir(File)} or
+ * {@link #setWorkTree(File)}, or use {@link #readEnvironment()} or
+ * {@link #findGitDir()} in order to configure the minimum property set
+ * necessary to open a repository.
+ * <p>
+ * Single repository applications trying to be compatible with other Git
+ * implementations are encouraged to use a model such as:
+ *
+ * <pre>
+ * new FileRepositoryBuilder() //
+ * .setGitDir(gitDirArgument) // --git-dir if supplied, no-op if null
+ * .readEnviroment() // scan environment GIT_* variables
+ * .findGitDir() // scan up the file system tree
+ * .build()
+ * </pre>
+ */
+public class FileRepositoryBuilder extends
+ BaseRepositoryBuilder<FileRepositoryBuilder, FileRepository> {
+ /**
+ * Create a repository matching the configuration in this builder.
+ * <p>
+ * If an option was not set, the build method will try to default the option
+ * based on other options. If insufficient information is available, an
+ * exception is thrown to the caller.
+ *
+ * @return a repository matching this configuration.
+ * @throws IllegalArgumentException
+ * insufficient parameters were set.
+ * @throws IOException
+ * the repository could not be accessed to configure the rest of
+ * the builder's parameters.
+ */
+ @Override
+ public FileRepository build() throws IOException {
+ return new FileRepository(setup());
+ }
+}
diff --git a/org.eclipse.jgit/src/org/eclipse/jgit/storage/file/LargePackedDeltaObject.java b/org.eclipse.jgit/src/org/eclipse/jgit/storage/file/LargePackedDeltaObject.java
new file mode 100644
index 0000000..53a0e61
--- /dev/null
+++ b/org.eclipse.jgit/src/org/eclipse/jgit/storage/file/LargePackedDeltaObject.java
@@ -0,0 +1,230 @@
+/*
+ * Copyright (C) 2010, Google Inc.
+ * and other copyright owners as documented in the project's IP log.
+ *
+ * This program and the accompanying materials are made available
+ * under the terms of the Eclipse Distribution License v1.0 which
+ * accompanies this distribution, is reproduced below, and is
+ * available at http://www.eclipse.org/org/documents/edl-v10.php
+ *
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or
+ * without modification, are permitted provided that the following
+ * conditions are met:
+ *
+ * - Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ *
+ * - Redistributions in binary form must reproduce the above
+ * copyright notice, this list of conditions and the following
+ * disclaimer in the documentation and/or other materials provided
+ * with the distribution.
+ *
+ * - Neither the name of the Eclipse Foundation, Inc. nor the
+ * names of its contributors may be used to endorse or promote
+ * products derived from this software without specific prior
+ * written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND
+ * CONTRIBUTORS "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES,
+ * INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
+ * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR
+ * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
+ * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT
+ * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
+ * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER
+ * CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT,
+ * STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
+ * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF
+ * ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ */
+
+package org.eclipse.jgit.storage.file;
+
+import java.io.BufferedInputStream;
+import java.io.IOException;
+import java.io.InputStream;
+import java.util.zip.DataFormatException;
+import java.util.zip.InflaterInputStream;
+
+import org.eclipse.jgit.errors.IncorrectObjectTypeException;
+import org.eclipse.jgit.errors.LargeObjectException;
+import org.eclipse.jgit.errors.MissingObjectException;
+import org.eclipse.jgit.lib.Constants;
+import org.eclipse.jgit.lib.ObjectId;
+import org.eclipse.jgit.lib.ObjectLoader;
+import org.eclipse.jgit.lib.ObjectStream;
+import org.eclipse.jgit.storage.pack.BinaryDelta;
+import org.eclipse.jgit.storage.pack.DeltaStream;
+
+class LargePackedDeltaObject extends ObjectLoader {
+ private static final long SIZE_UNKNOWN = -1;
+
+ private int type;
+
+ private long size;
+
+ private final long objectOffset;
+
+ private final long baseOffset;
+
+ private final int headerLength;
+
+ private final PackFile pack;
+
+ private final FileObjectDatabase db;
+
+ LargePackedDeltaObject(long objectOffset,
+ long baseOffset, int headerLength, PackFile pack,
+ FileObjectDatabase db) {
+ this.type = Constants.OBJ_BAD;
+ this.size = SIZE_UNKNOWN;
+ this.objectOffset = objectOffset;
+ this.baseOffset = baseOffset;
+ this.headerLength = headerLength;
+ this.pack = pack;
+ this.db = db;
+ }
+
+ @Override
+ public int getType() {
+ if (type == Constants.OBJ_BAD) {
+ WindowCursor wc = new WindowCursor(db);
+ try {
+ type = pack.getObjectType(wc, objectOffset);
+ } catch (IOException packGone) {
+ // If the pack file cannot be pinned into the cursor, it
+ // probably was repacked recently. Go find the object
+ // again and get the type from that location instead.
+ //
+ try {
+ type = wc.open(getObjectId()).getType();
+ } catch (IOException packGone2) {
+ // "He's dead, Jim." We just can't discover the type
+ // and the interface isn't supposed to be lazy here.
+ // Report an invalid type code instead, callers will
+ // wind up bailing out with an error at some point.
+ }
+ } finally {
+ wc.release();
+ }
+ }
+ return type;
+ }
+
+ @Override
+ public long getSize() {
+ if (size == SIZE_UNKNOWN) {
+ WindowCursor wc = new WindowCursor(db);
+ try {
+ byte[] b = pack.getDeltaHeader(wc, objectOffset + headerLength);
+ size = BinaryDelta.getResultSize(b);
+ } catch (DataFormatException objectCorrupt) {
+ // The zlib stream for the delta is corrupt. We probably
+ // cannot access the object. Keep the size negative and
+ // report that bogus result to the caller.
+ } catch (IOException packGone) {
+ // If the pack file cannot be pinned into the cursor, it
+ // probably was repacked recently. Go find the object
+ // again and get the size from that location instead.
+ //
+ try {
+ size = wc.open(getObjectId()).getSize();
+ } catch (IOException packGone2) {
+ // "He's dead, Jim." We just can't discover the size
+ // and the interface isn't supposed to be lazy here.
+ // Report an invalid type code instead, callers will
+ // wind up bailing out with an error at some point.
+ }
+ } finally {
+ wc.release();
+ }
+ }
+ return size;
+ }
+
+ @Override
+ public boolean isLarge() {
+ return true;
+ }
+
+ @Override
+ public byte[] getCachedBytes() throws LargeObjectException {
+ try {
+ throw new LargeObjectException(getObjectId());
+ } catch (IOException cannotObtainId) {
+ throw new LargeObjectException();
+ }
+ }
+
+ @Override
+ public ObjectStream openStream() throws MissingObjectException, IOException {
+ final WindowCursor wc = new WindowCursor(db);
+ InputStream in = open(wc);
+ in = new BufferedInputStream(in, 8192);
+ return new ObjectStream.Filter(getType(), size, in) {
+ @Override
+ public void close() throws IOException {
+ wc.release();
+ super.close();
+ }
+ };
+ }
+
+ private InputStream open(final WindowCursor wc)
+ throws MissingObjectException, IOException,
+ IncorrectObjectTypeException {
+ InputStream delta;
+ try {
+ delta = new PackInputStream(pack, objectOffset + headerLength, wc);
+ } catch (IOException packGone) {
+ // If the pack file cannot be pinned into the cursor, it
+ // probably was repacked recently. Go find the object
+ // again and open the stream from that location instead.
+ //
+ return wc.open(getObjectId()).openStream();
+ }
+ delta = new InflaterInputStream(delta);
+
+ final ObjectLoader base = pack.load(wc, baseOffset);
+ DeltaStream ds = new DeltaStream(delta) {
+ private long baseSize = SIZE_UNKNOWN;
+
+ @Override
+ protected InputStream openBase() throws IOException {
+ InputStream in;
+ if (base instanceof LargePackedDeltaObject)
+ in = ((LargePackedDeltaObject) base).open(wc);
+ else
+ in = base.openStream();
+ if (baseSize == SIZE_UNKNOWN) {
+ if (in instanceof DeltaStream)
+ baseSize = ((DeltaStream) in).getSize();
+ else if (in instanceof ObjectStream)
+ baseSize = ((ObjectStream) in).getSize();
+ }
+ return in;
+ }
+
+ @Override
+ protected long getBaseSize() throws IOException {
+ if (baseSize == SIZE_UNKNOWN) {
+ // This code path should never be used as DeltaStream
+ // is supposed to open the stream first, which would
+ // initialize the size for us directly from the stream.
+ baseSize = base.getSize();
+ }
+ return baseSize;
+ }
+ };
+ if (size == SIZE_UNKNOWN)
+ size = ds.getSize();
+ return ds;
+ }
+
+ private ObjectId getObjectId() throws IOException {
+ return pack.findObjectForOffset(objectOffset);
+ }
+}
diff --git a/org.eclipse.jgit/src/org/eclipse/jgit/storage/file/LargePackedWholeObject.java b/org.eclipse.jgit/src/org/eclipse/jgit/storage/file/LargePackedWholeObject.java
new file mode 100644
index 0000000..9f5b804
--- /dev/null
+++ b/org.eclipse.jgit/src/org/eclipse/jgit/storage/file/LargePackedWholeObject.java
@@ -0,0 +1,130 @@
+/*
+ * Copyright (C) 2010, Google Inc.
+ * and other copyright owners as documented in the project's IP log.
+ *
+ * This program and the accompanying materials are made available
+ * under the terms of the Eclipse Distribution License v1.0 which
+ * accompanies this distribution, is reproduced below, and is
+ * available at http://www.eclipse.org/org/documents/edl-v10.php
+ *
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or
+ * without modification, are permitted provided that the following
+ * conditions are met:
+ *
+ * - Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ *
+ * - Redistributions in binary form must reproduce the above
+ * copyright notice, this list of conditions and the following
+ * disclaimer in the documentation and/or other materials provided
+ * with the distribution.
+ *
+ * - Neither the name of the Eclipse Foundation, Inc. nor the
+ * names of its contributors may be used to endorse or promote
+ * products derived from this software without specific prior
+ * written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND
+ * CONTRIBUTORS "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES,
+ * INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
+ * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR
+ * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
+ * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT
+ * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
+ * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER
+ * CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT,
+ * STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
+ * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF
+ * ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ */
+
+package org.eclipse.jgit.storage.file;
+
+import java.io.BufferedInputStream;
+import java.io.IOException;
+import java.io.InputStream;
+import java.util.zip.InflaterInputStream;
+
+import org.eclipse.jgit.errors.LargeObjectException;
+import org.eclipse.jgit.errors.MissingObjectException;
+import org.eclipse.jgit.lib.ObjectId;
+import org.eclipse.jgit.lib.ObjectLoader;
+import org.eclipse.jgit.lib.ObjectStream;
+
+class LargePackedWholeObject extends ObjectLoader {
+ private final int type;
+
+ private final long size;
+
+ private final long objectOffset;
+
+ private final int headerLength;
+
+ private final PackFile pack;
+
+ private final FileObjectDatabase db;
+
+ LargePackedWholeObject(int type, long size, long objectOffset,
+ int headerLength, PackFile pack, FileObjectDatabase db) {
+ this.type = type;
+ this.size = size;
+ this.objectOffset = objectOffset;
+ this.headerLength = headerLength;
+ this.pack = pack;
+ this.db = db;
+ }
+
+ @Override
+ public int getType() {
+ return type;
+ }
+
+ @Override
+ public long getSize() {
+ return size;
+ }
+
+ @Override
+ public boolean isLarge() {
+ return true;
+ }
+
+ @Override
+ public byte[] getCachedBytes() throws LargeObjectException {
+ try {
+ throw new LargeObjectException(getObjectId());
+ } catch (IOException cannotObtainId) {
+ throw new LargeObjectException();
+ }
+ }
+
+ @Override
+ public ObjectStream openStream() throws MissingObjectException, IOException {
+ WindowCursor wc = new WindowCursor(db);
+ InputStream in;
+ try {
+ in = new PackInputStream(pack, objectOffset + headerLength, wc);
+ } catch (IOException packGone) {
+ // If the pack file cannot be pinned into the cursor, it
+ // probably was repacked recently. Go find the object
+ // again and open the stream from that location instead.
+ //
+ return wc.open(getObjectId(), type).openStream();
+ }
+
+ in = new BufferedInputStream( //
+ new InflaterInputStream( //
+ in, //
+ wc.inflater(), //
+ 8192), //
+ 8192);
+ return new ObjectStream.Filter(type, size, in);
+ }
+
+ private ObjectId getObjectId() throws IOException {
+ return pack.findObjectForOffset(objectOffset);
+ }
+}
diff --git a/org.eclipse.jgit/src/org/eclipse/jgit/storage/file/LocalObjectRepresentation.java b/org.eclipse.jgit/src/org/eclipse/jgit/storage/file/LocalObjectRepresentation.java
new file mode 100644
index 0000000..08bb8e6
--- /dev/null
+++ b/org.eclipse.jgit/src/org/eclipse/jgit/storage/file/LocalObjectRepresentation.java
@@ -0,0 +1,118 @@
+/*
+ * 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.storage.pack.StoredObjectRepresentation;
+
+class LocalObjectRepresentation extends StoredObjectRepresentation {
+ static LocalObjectRepresentation newWhole(PackFile f, long p, long length) {
+ LocalObjectRepresentation r = new LocalObjectRepresentation() {
+ @Override
+ public int getFormat() {
+ return PACK_WHOLE;
+ }
+ };
+ r.pack = f;
+ r.offset = p;
+ r.length = length;
+ return r;
+ }
+
+ static LocalObjectRepresentation newDelta(PackFile f, long p, long n,
+ ObjectId base) {
+ LocalObjectRepresentation r = new Delta();
+ r.pack = f;
+ r.offset = p;
+ r.length = n;
+ r.baseId = base;
+ return r;
+ }
+
+ static LocalObjectRepresentation newDelta(PackFile f, long p, long n,
+ long base) {
+ LocalObjectRepresentation r = new Delta();
+ r.pack = f;
+ r.offset = p;
+ r.length = n;
+ r.baseOffset = base;
+ return r;
+ }
+
+ PackFile pack;
+
+ long offset;
+
+ long length;
+
+ private long baseOffset;
+
+ private ObjectId baseId;
+
+ @Override
+ public int getWeight() {
+ return (int) Math.min(length, Integer.MAX_VALUE);
+ }
+
+ @Override
+ public ObjectId getDeltaBase() {
+ if (baseId == null && getFormat() == PACK_DELTA) {
+ try {
+ baseId = pack.findObjectForOffset(baseOffset);
+ } catch (IOException error) {
+ return null;
+ }
+ }
+ return baseId;
+ }
+
+ private static final class Delta extends LocalObjectRepresentation {
+ @Override
+ public int getFormat() {
+ return PACK_DELTA;
+ }
+ }
+}
diff --git a/org.eclipse.jgit/src/org/eclipse/jgit/lib/RepositoryChangedEvent.java b/org.eclipse.jgit/src/org/eclipse/jgit/storage/file/LocalObjectToPack.java
similarity index 68%
copy from org.eclipse.jgit/src/org/eclipse/jgit/lib/RepositoryChangedEvent.java
copy to org.eclipse.jgit/src/org/eclipse/jgit/storage/file/LocalObjectToPack.java
index 495049c..c7ef2c9 100644
--- a/org.eclipse.jgit/src/org/eclipse/jgit/lib/RepositoryChangedEvent.java
+++ b/org.eclipse.jgit/src/org/eclipse/jgit/storage/file/LocalObjectToPack.java
@@ -1,5 +1,5 @@
/*
- * Copyright (C) 2008, Robin Rosenberg <robin.rosenberg@dewire.com>
+ * Copyright (C) 2010, Google Inc.
* and other copyright owners as documented in the project's IP log.
*
* This program and the accompanying materials are made available
@@ -41,30 +41,38 @@
* ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
*/
-package org.eclipse.jgit.lib;
+package org.eclipse.jgit.storage.file;
-/**
- * This class passes information about changed refs to a
- * {@link RepositoryListener}
- *
- * Currently only a reference to the repository is passed.
- */
-public class RepositoryChangedEvent {
- private final Repository repository;
+import org.eclipse.jgit.revwalk.RevObject;
+import org.eclipse.jgit.storage.pack.ObjectToPack;
+import org.eclipse.jgit.storage.pack.StoredObjectRepresentation;
- RepositoryChangedEvent(final Repository repository) {
- this.repository = repository;
- }
+/** {@link ObjectToPack} for {@link ObjectDirectory}. */
+class LocalObjectToPack extends ObjectToPack {
+ /** Pack to reuse compressed data from, otherwise null. */
+ PackFile pack;
- /**
- * @return the repository that was changed
- */
- public Repository getRepository() {
- return repository;
+ /** Offset of the object's header in {@link #pack}. */
+ long offset;
+
+ /** Length of the data section of the object. */
+ long length;
+
+ LocalObjectToPack(RevObject obj) {
+ super(obj);
}
@Override
- public String toString() {
- return "RepositoryChangedEvent[" + repository + "]";
+ protected void clearReuseAsIs() {
+ super.clearReuseAsIs();
+ pack = null;
+ }
+
+ @Override
+ public void select(StoredObjectRepresentation ref) {
+ LocalObjectRepresentation ptr = (LocalObjectRepresentation) ref;
+ this.pack = ptr.pack;
+ this.offset = ptr.offset;
+ this.length = ptr.length;
}
}
diff --git a/org.eclipse.jgit/src/org/eclipse/jgit/lib/LockFile.java b/org.eclipse.jgit/src/org/eclipse/jgit/storage/file/LockFile.java
similarity index 93%
rename from org.eclipse.jgit/src/org/eclipse/jgit/lib/LockFile.java
rename to org.eclipse.jgit/src/org/eclipse/jgit/storage/file/LockFile.java
index 13f158d..e8bc3e2 100644
--- a/org.eclipse.jgit/src/org/eclipse/jgit/lib/LockFile.java
+++ b/org.eclipse.jgit/src/org/eclipse/jgit/storage/file/LockFile.java
@@ -42,7 +42,7 @@
* ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
*/
-package org.eclipse.jgit.lib;
+package org.eclipse.jgit.storage.file;
import java.io.BufferedOutputStream;
import java.io.File;
@@ -57,6 +57,9 @@
import java.text.MessageFormat;
import org.eclipse.jgit.JGitText;
+import org.eclipse.jgit.lib.Constants;
+import org.eclipse.jgit.lib.ObjectId;
+import org.eclipse.jgit.util.FS;
/**
* Git style file locking and replacement.
@@ -92,15 +95,21 @@ public boolean accept(File dir, String name) {
private long commitLastModified;
+ private final FS fs;
+
/**
* Create a new lock for any file.
*
* @param f
* the file that will be locked.
+ * @param fs
+ * the file system abstraction which will be necessary to perform
+ * certain file system operations.
*/
- public LockFile(final File f) {
+ public LockFile(final File f, FS fs) {
ref = f;
lck = new File(ref.getParentFile(), ref.getName() + SUFFIX);
+ this.fs = fs;
}
/**
@@ -391,13 +400,32 @@ public boolean commit() {
saveStatInformation();
if (lck.renameTo(ref))
return true;
- if (!ref.exists() || ref.delete())
+ if (!ref.exists() || deleteRef())
if (lck.renameTo(ref))
return true;
unlock();
return false;
}
+ private boolean deleteRef() {
+ if (!fs.retryFailedLockFileCommit())
+ return ref.delete();
+
+ // File deletion fails on windows if another thread is
+ // concurrently reading the same file. So try a few times.
+ //
+ for (int attempts = 0; attempts < 10; attempts++) {
+ if (ref.delete())
+ return true;
+ try {
+ Thread.sleep(100);
+ } catch (InterruptedException e) {
+ return false;
+ }
+ }
+ return false;
+ }
+
private void saveStatInformation() {
if (needStatInformation)
commitLastModified = lck.lastModified();
diff --git a/org.eclipse.jgit/src/org/eclipse/jgit/lib/ObjectDirectory.java b/org.eclipse.jgit/src/org/eclipse/jgit/storage/file/ObjectDirectory.java
similarity index 73%
rename from org.eclipse.jgit/src/org/eclipse/jgit/lib/ObjectDirectory.java
rename to org.eclipse.jgit/src/org/eclipse/jgit/storage/file/ObjectDirectory.java
index 9a5bcdb..6fe4fd7 100644
--- a/org.eclipse.jgit/src/org/eclipse/jgit/lib/ObjectDirectory.java
+++ b/org.eclipse.jgit/src/org/eclipse/jgit/storage/file/ObjectDirectory.java
@@ -41,10 +41,11 @@
* ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
*/
-package org.eclipse.jgit.lib;
+package org.eclipse.jgit.storage.file;
import java.io.BufferedReader;
import java.io.File;
+import java.io.FileInputStream;
import java.io.FileNotFoundException;
import java.io.FileReader;
import java.io.IOException;
@@ -62,7 +63,19 @@
import org.eclipse.jgit.JGitText;
import org.eclipse.jgit.errors.PackMismatchException;
+import org.eclipse.jgit.events.ConfigChangedEvent;
+import org.eclipse.jgit.events.ConfigChangedListener;
+import org.eclipse.jgit.lib.AnyObjectId;
+import org.eclipse.jgit.lib.Config;
+import org.eclipse.jgit.lib.CoreConfig;
+import org.eclipse.jgit.lib.ObjectDatabase;
+import org.eclipse.jgit.lib.ObjectId;
+import org.eclipse.jgit.lib.ObjectInserter;
+import org.eclipse.jgit.lib.ObjectLoader;
+import org.eclipse.jgit.lib.RepositoryCache;
import org.eclipse.jgit.lib.RepositoryCache.FileKey;
+import org.eclipse.jgit.storage.pack.ObjectToPack;
+import org.eclipse.jgit.storage.pack.PackWriter;
import org.eclipse.jgit.util.FS;
/**
@@ -72,10 +85,23 @@
* where objects are stored loose by hashing them into directories by their
* {@link ObjectId}, or are stored in compressed containers known as
* {@link PackFile}s.
+ * <p>
+ * Optionally an object database can reference one or more alternates; other
+ * ObjectDatabase instances that are searched in addition to the current
+ * database.
+ * <p>
+ * Databases are divided into two halves: a half that is considered to be fast
+ * to search (the {@code PackFile}s), and a half that is considered to be slow
+ * to search (loose objects). When alternates are present the fast half is fully
+ * searched (recursively through all alternates) before the slow half is
+ * considered.
*/
-public class ObjectDirectory extends ObjectDatabase {
+public class ObjectDirectory extends FileObjectDatabase implements
+ ConfigChangedListener {
private static final PackList NO_PACKS = new PackList(-1, -1, new PackFile[0]);
+ private final Config config;
+
private final File objects;
private final File infoDirectory;
@@ -86,29 +112,53 @@ public class ObjectDirectory extends ObjectDatabase {
private final AtomicReference<PackList> packList;
- private final File[] alternateObjectDir;
-
private final FS fs;
+ private final AtomicReference<AlternateHandle[]> alternates;
+
+ private int streamFileThreshold;
+
/**
* Initialize a reference to an on-disk object directory.
*
+ * @param cfg
+ * configuration this directory consults for write settings.
* @param dir
* the location of the <code>objects</code> directory.
- * @param alternateObjectDir
+ * @param alternatePaths
* a list of alternate object directories
* @param fs
- * the file system abstraction which will be necessary to
- * perform certain file system operations.
+ * the file system abstraction which will be necessary to perform
+ * certain file system operations.
+ * @throws IOException
+ * an alternate object cannot be opened.
*/
- public ObjectDirectory(final File dir, File[] alternateObjectDir, FS fs) {
+ public ObjectDirectory(final Config cfg, final File dir,
+ File[] alternatePaths, FS fs) throws IOException {
+ config = cfg;
objects = dir;
- this.alternateObjectDir = alternateObjectDir;
infoDirectory = new File(objects, "info");
packDirectory = new File(objects, "pack");
alternatesFile = new File(infoDirectory, "alternates");
packList = new AtomicReference<PackList>(NO_PACKS);
this.fs = fs;
+
+ alternates = new AtomicReference<AlternateHandle[]>();
+ if (alternatePaths != null) {
+ AlternateHandle[] alt;
+
+ alt = new AlternateHandle[alternatePaths.length];
+ for (int i = 0; i < alternatePaths.length; i++)
+ alt[i] = openAlternate(alternatePaths[i]);
+ alternates.set(alt);
+ }
+
+ onConfigChanged(new ConfigChangedEvent());
+ }
+
+ public void onConfigChanged(ConfigChangedEvent event) {
+ CoreConfig core = config.get(CoreConfig.KEY);
+ streamFileThreshold = core.getStreamFileThreshold();
}
/**
@@ -131,11 +181,24 @@ public void create() throws IOException {
}
@Override
- public void closeSelf() {
+ public ObjectInserter newInserter() {
+ return new ObjectDirectoryInserter(this, config);
+ }
+
+ @Override
+ public void close() {
final PackList packs = packList.get();
packList.set(NO_PACKS);
for (final PackFile p : packs.packs)
p.close();
+
+ // Fully close all loaded alternates and clear the alternate list.
+ AlternateHandle[] alt = alternates.get();
+ if (alt != null) {
+ alternates.set(null);
+ for(final AlternateHandle od : alt)
+ od.close();
+ }
}
/**
@@ -199,8 +262,7 @@ public String toString() {
return "ObjectDirectory[" + getDirectory() + "]";
}
- @Override
- protected boolean hasObject1(final AnyObjectId objectId) {
+ boolean hasObject1(final AnyObjectId objectId) {
for (final PackFile p : packList.get().packs) {
try {
if (p.hasObject(objectId)) {
@@ -218,18 +280,15 @@ protected boolean hasObject1(final AnyObjectId objectId) {
return false;
}
- @Override
- protected ObjectLoader openObject1(final WindowCursor curs,
+ ObjectLoader openObject1(final WindowCursor curs,
final AnyObjectId objectId) throws IOException {
PackList pList = packList.get();
SEARCH: for (;;) {
for (final PackFile p : pList.packs) {
try {
- final PackedObjectLoader ldr = p.get(curs, objectId);
- if (ldr != null) {
- ldr.materialize(curs);
+ final ObjectLoader ldr = p.get(curs, objectId);
+ if (ldr != null)
return ldr;
- }
} catch (PackMismatchException e) {
// Pack was modified; refresh the entire pack list.
//
@@ -245,18 +304,56 @@ protected ObjectLoader openObject1(final WindowCursor curs,
}
}
- @Override
- void openObjectInAllPacks1(final Collection<PackedObjectLoader> out,
- final WindowCursor curs, final AnyObjectId objectId)
+ long getObjectSize1(final WindowCursor curs, final AnyObjectId objectId)
throws IOException {
PackList pList = packList.get();
SEARCH: for (;;) {
for (final PackFile p : pList.packs) {
try {
- final PackedObjectLoader ldr = p.get(curs, objectId);
- if (ldr != null) {
- out.add(ldr);
- }
+ long sz = p.getObjectSize(curs, objectId);
+ if (0 <= sz)
+ return sz;
+ } catch (PackMismatchException e) {
+ // Pack was modified; refresh the entire pack list.
+ //
+ pList = scanPacks(pList);
+ continue SEARCH;
+ } catch (IOException e) {
+ // Assume the pack is corrupted.
+ //
+ removePack(p);
+ }
+ }
+ return -1;
+ }
+ }
+
+ @Override
+ long getObjectSize2(WindowCursor curs, String objectName,
+ AnyObjectId objectId) throws IOException {
+ try {
+ File path = fileFor(objectName);
+ FileInputStream in = new FileInputStream(path);
+ try {
+ return UnpackedObject.getSize(in, objectId, curs);
+ } finally {
+ in.close();
+ }
+ } catch (FileNotFoundException noFile) {
+ return -1;
+ }
+ }
+
+ @Override
+ void selectObjectRepresentation(PackWriter packer, ObjectToPack otp,
+ WindowCursor curs) throws IOException {
+ PackList pList = packList.get();
+ SEARCH: for (;;) {
+ for (final PackFile p : pList.packs) {
+ try {
+ LocalObjectRepresentation rep = p.representation(curs, otp);
+ if (rep != null)
+ packer.select(otp, rep);
} catch (PackMismatchException e) {
// Pack was modified; refresh the entire pack list.
//
@@ -270,26 +367,32 @@ void openObjectInAllPacks1(final Collection<PackedObjectLoader> out,
}
break SEARCH;
}
+
+ for (AlternateHandle h : myAlternates())
+ h.db.selectObjectRepresentation(packer, otp, curs);
}
- @Override
- protected boolean hasObject2(final String objectName) {
+ boolean hasObject2(final String objectName) {
return fileFor(objectName).exists();
}
- @Override
- protected ObjectLoader openObject2(final WindowCursor curs,
+ ObjectLoader openObject2(final WindowCursor curs,
final String objectName, final AnyObjectId objectId)
throws IOException {
try {
- return new UnpackedObjectLoader(fileFor(objectName), objectId);
+ File path = fileFor(objectName);
+ FileInputStream in = new FileInputStream(path);
+ try {
+ return UnpackedObject.open(in, path, objectId, curs);
+ } finally {
+ in.close();
+ }
} catch (FileNotFoundException noFile) {
return null;
}
}
- @Override
- protected boolean tryAgain1() {
+ boolean tryAgain1() {
final PackList old = packList.get();
if (old.tryAgain(packDirectory.lastModified()))
return old != scanPacks(old);
@@ -459,29 +562,36 @@ private Set<String> listPackDirectory() {
return nameSet;
}
- @Override
- protected ObjectDatabase[] loadAlternates() throws IOException {
- final List<ObjectDatabase> l = new ArrayList<ObjectDatabase>(4);
- if (alternateObjectDir != null) {
- for (File d : alternateObjectDir) {
- l.add(openAlternate(d));
- }
- } else {
- final BufferedReader br = open(alternatesFile);
- try {
- String line;
- while ((line = br.readLine()) != null) {
- l.add(openAlternate(line));
+ AlternateHandle[] myAlternates() {
+ AlternateHandle[] alt = alternates.get();
+ if (alt == null) {
+ synchronized (alternates) {
+ alt = alternates.get();
+ if (alt == null) {
+ try {
+ alt = loadAlternates();
+ } catch (IOException e) {
+ alt = new AlternateHandle[0];
+ }
+ alternates.set(alt);
}
- } finally {
- br.close();
}
}
+ return alt;
+ }
- if (l.isEmpty()) {
- return NO_ALTERNATES;
+ private AlternateHandle[] loadAlternates() throws IOException {
+ final List<AlternateHandle> l = new ArrayList<AlternateHandle>(4);
+ final BufferedReader br = open(alternatesFile);
+ try {
+ String line;
+ while ((line = br.readLine()) != null) {
+ l.add(openAlternate(line));
+ }
+ } finally {
+ br.close();
}
- return l.toArray(new ObjectDatabase[l.size()]);
+ return l.toArray(new AlternateHandle[l.size()]);
}
private static BufferedReader open(final File f)
@@ -489,19 +599,22 @@ private static BufferedReader open(final File f)
return new BufferedReader(new FileReader(f));
}
- private ObjectDatabase openAlternate(final String location)
+ private AlternateHandle openAlternate(final String location)
throws IOException {
final File objdir = fs.resolve(objects, location);
return openAlternate(objdir);
}
- private ObjectDatabase openAlternate(File objdir) throws IOException {
+ private AlternateHandle openAlternate(File objdir) throws IOException {
final File parent = objdir.getParentFile();
if (FileKey.isGitRepository(parent, fs)) {
- final Repository db = RepositoryCache.open(FileKey.exact(parent, fs));
- return new AlternateRepositoryDatabase(db);
+ FileKey key = FileKey.exact(parent, fs);
+ FileRepository db = (FileRepository) RepositoryCache.open(key);
+ return new AlternateRepository(db);
}
- return new ObjectDirectory(objdir, null, fs);
+
+ ObjectDirectory db = new ObjectDirectory(config, objdir, null, fs);
+ return new AlternateHandle(db);
}
private static final class PackList {
@@ -566,6 +679,15 @@ boolean tryAgain(final long currLastModified) {
@Override
public ObjectDatabase newCachedDatabase() {
+ return newCachedFileObjectDatabase();
+ }
+
+ FileObjectDatabase newCachedFileObjectDatabase() {
return new CachedObjectDirectory(this);
}
+
+ @Override
+ int getStreamFileThreshold() {
+ return streamFileThreshold;
+ }
}
diff --git a/org.eclipse.jgit/src/org/eclipse/jgit/storage/file/ObjectDirectoryInserter.java b/org.eclipse.jgit/src/org/eclipse/jgit/storage/file/ObjectDirectoryInserter.java
new file mode 100644
index 0000000..5016679
--- /dev/null
+++ b/org.eclipse.jgit/src/org/eclipse/jgit/storage/file/ObjectDirectoryInserter.java
@@ -0,0 +1,182 @@
+/*
+ * Copyright (C) 2007, Robin Rosenberg <robin.rosenberg@dewire.com>
+ * Copyright (C) 2008, Shawn O. Pearce <spearce@spearce.org>
+ * Copyright (C) 2009, Google Inc.
+ * and other copyright owners as documented in the project's IP log.
+ *
+ * This program and the accompanying materials are made available
+ * under the terms of the Eclipse Distribution License v1.0 which
+ * accompanies this distribution, is reproduced below, and is
+ * available at http://www.eclipse.org/org/documents/edl-v10.php
+ *
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or
+ * without modification, are permitted provided that the following
+ * conditions are met:
+ *
+ * - Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ *
+ * - Redistributions in binary form must reproduce the above
+ * copyright notice, this list of conditions and the following
+ * disclaimer in the documentation and/or other materials provided
+ * with the distribution.
+ *
+ * - Neither the name of the Eclipse Foundation, Inc. nor the
+ * names of its contributors may be used to endorse or promote
+ * products derived from this software without specific prior
+ * written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND
+ * CONTRIBUTORS "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES,
+ * INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
+ * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR
+ * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
+ * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT
+ * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
+ * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER
+ * CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT,
+ * STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
+ * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF
+ * ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ */
+
+package org.eclipse.jgit.storage.file;
+
+import java.io.EOFException;
+import java.io.File;
+import java.io.FileNotFoundException;
+import java.io.FileOutputStream;
+import java.io.IOException;
+import java.io.InputStream;
+import java.io.OutputStream;
+import java.security.DigestOutputStream;
+import java.security.MessageDigest;
+import java.util.zip.Deflater;
+import java.util.zip.DeflaterOutputStream;
+
+import org.eclipse.jgit.errors.ObjectWritingException;
+import org.eclipse.jgit.lib.Config;
+import org.eclipse.jgit.lib.Constants;
+import org.eclipse.jgit.lib.CoreConfig;
+import org.eclipse.jgit.lib.ObjectId;
+import org.eclipse.jgit.lib.ObjectInserter;
+
+/** Creates loose objects in a {@link ObjectDirectory}. */
+class ObjectDirectoryInserter extends ObjectInserter {
+ private final ObjectDirectory db;
+
+ private final Config config;
+
+ private Deflater deflate;
+
+ ObjectDirectoryInserter(final ObjectDirectory dest, final Config cfg) {
+ db = dest;
+ config = cfg;
+ }
+
+ @Override
+ public ObjectId insert(final int type, long len, final InputStream is)
+ throws IOException {
+ final MessageDigest md = digest();
+ final File tmp = toTemp(md, type, len, is);
+ final ObjectId id = ObjectId.fromRaw(md.digest());
+ if (db.has(id)) {
+ // Object is already in the repository, remove temporary file.
+ //
+ tmp.delete();
+ return id;
+ }
+
+ final File dst = db.fileFor(id);
+ if (tmp.renameTo(dst))
+ return id;
+
+ // Maybe the directory doesn't exist yet as the object
+ // directories are always lazily created. Note that we
+ // try the rename first as the directory likely does exist.
+ //
+ dst.getParentFile().mkdir();
+ if (tmp.renameTo(dst))
+ return id;
+
+ if (db.has(id)) {
+ tmp.delete();
+ return id;
+ }
+
+ // The object failed to be renamed into its proper
+ // location and it doesn't exist in the repository
+ // either. We really don't know what went wrong, so
+ // fail.
+ //
+ tmp.delete();
+ throw new ObjectWritingException("Unable to create new object: " + dst);
+ }
+
+ @Override
+ public void flush() throws IOException {
+ // Do nothing. Objects are immediately visible.
+ }
+
+ @Override
+ public void release() {
+ if (deflate != null) {
+ try {
+ deflate.end();
+ } finally {
+ deflate = null;
+ }
+ }
+ }
+
+ private File toTemp(final MessageDigest md, final int type, long len,
+ final InputStream is) throws IOException, FileNotFoundException,
+ Error {
+ boolean delete = true;
+ File tmp = File.createTempFile("noz", null, db.getDirectory());
+ try {
+ DigestOutputStream dOut = new DigestOutputStream(
+ compress(new FileOutputStream(tmp)), md);
+ try {
+ dOut.write(Constants.encodedTypeString(type));
+ dOut.write((byte) ' ');
+ dOut.write(Constants.encodeASCII(len));
+ dOut.write((byte) 0);
+
+ final byte[] buf = buffer();
+ while (len > 0) {
+ int n = is.read(buf, 0, (int) Math.min(len, buf.length));
+ if (n <= 0)
+ throw shortInput(len);
+ dOut.write(buf, 0, n);
+ len -= n;
+ }
+ } finally {
+ dOut.close();
+ }
+
+ tmp.setReadOnly();
+ delete = false;
+ return tmp;
+ } finally {
+ if (delete)
+ tmp.delete();
+ }
+ }
+
+ private DeflaterOutputStream compress(final OutputStream out) {
+ if (deflate == null)
+ deflate = new Deflater(config.get(CoreConfig.KEY).getCompression());
+ else
+ deflate.reset();
+ return new DeflaterOutputStream(out, deflate);
+ }
+
+ private static EOFException shortInput(long missing) {
+ return new EOFException("Input did not match supplied length. "
+ + missing + " bytes are missing.");
+ }
+}
diff --git a/org.eclipse.jgit/src/org/eclipse/jgit/storage/file/PackFile.java b/org.eclipse.jgit/src/org/eclipse/jgit/storage/file/PackFile.java
new file mode 100644
index 0000000..e74a7c0
--- /dev/null
+++ b/org.eclipse.jgit/src/org/eclipse/jgit/storage/file/PackFile.java
@@ -0,0 +1,920 @@
+/*
+ * Copyright (C) 2008-2009, Google Inc.
+ * Copyright (C) 2007, Robin Rosenberg <robin.rosenberg@dewire.com>
+ * Copyright (C) 2006-2008, Shawn O. Pearce <spearce@spearce.org>
+ * and other copyright owners as documented in the project's IP log.
+ *
+ * This program and the accompanying materials are made available
+ * under the terms of the Eclipse Distribution License v1.0 which
+ * accompanies this distribution, is reproduced below, and is
+ * available at http://www.eclipse.org/org/documents/edl-v10.php
+ *
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or
+ * without modification, are permitted provided that the following
+ * conditions are met:
+ *
+ * - Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ *
+ * - Redistributions in binary form must reproduce the above
+ * copyright notice, this list of conditions and the following
+ * disclaimer in the documentation and/or other materials provided
+ * with the distribution.
+ *
+ * - Neither the name of the Eclipse Foundation, Inc. nor the
+ * names of its contributors may be used to endorse or promote
+ * products derived from this software without specific prior
+ * written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND
+ * CONTRIBUTORS "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES,
+ * INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
+ * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR
+ * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
+ * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT
+ * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
+ * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER
+ * CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT,
+ * STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
+ * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF
+ * ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ */
+
+package org.eclipse.jgit.storage.file;
+
+import java.io.EOFException;
+import java.io.File;
+import java.io.IOException;
+import java.io.RandomAccessFile;
+import java.nio.MappedByteBuffer;
+import java.nio.channels.FileChannel.MapMode;
+import java.text.MessageFormat;
+import java.util.Arrays;
+import java.util.Collections;
+import java.util.Comparator;
+import java.util.Iterator;
+import java.util.zip.CRC32;
+import java.util.zip.DataFormatException;
+import java.util.zip.Inflater;
+
+import org.eclipse.jgit.JGitText;
+import org.eclipse.jgit.errors.CorruptObjectException;
+import org.eclipse.jgit.errors.MissingObjectException;
+import org.eclipse.jgit.errors.PackInvalidException;
+import org.eclipse.jgit.errors.PackMismatchException;
+import org.eclipse.jgit.errors.StoredObjectRepresentationNotAvailableException;
+import org.eclipse.jgit.lib.AnyObjectId;
+import org.eclipse.jgit.lib.Constants;
+import org.eclipse.jgit.lib.ObjectId;
+import org.eclipse.jgit.lib.ObjectLoader;
+import org.eclipse.jgit.storage.pack.BinaryDelta;
+import org.eclipse.jgit.storage.pack.ObjectToPack;
+import org.eclipse.jgit.storage.pack.PackOutputStream;
+import org.eclipse.jgit.util.LongList;
+import org.eclipse.jgit.util.NB;
+import org.eclipse.jgit.util.RawParseUtils;
+
+/**
+ * A Git version 2 pack file representation. A pack file contains Git objects in
+ * delta packed format yielding high compression of lots of object where some
+ * objects are similar.
+ */
+public class PackFile implements Iterable<PackIndex.MutableEntry> {
+ /** Sorts PackFiles to be most recently created to least recently created. */
+ public static Comparator<PackFile> SORT = new Comparator<PackFile>() {
+ public int compare(final PackFile a, final PackFile b) {
+ return b.packLastModified - a.packLastModified;
+ }
+ };
+
+ private final File idxFile;
+
+ private final File packFile;
+
+ final int hash;
+
+ private RandomAccessFile fd;
+
+ /** Serializes reads performed against {@link #fd}. */
+ private final Object readLock = new Object();
+
+ long length;
+
+ private int activeWindows;
+
+ private int activeCopyRawData;
+
+ private int packLastModified;
+
+ private volatile boolean invalid;
+
+ private byte[] packChecksum;
+
+ private PackIndex loadedIdx;
+
+ private PackReverseIndex reverseIdx;
+
+ /**
+ * Objects we have tried to read, and discovered to be corrupt.
+ * <p>
+ * The list is allocated after the first corruption is found, and filled in
+ * as more entries are discovered. Typically this list is never used, as
+ * pack files do not usually contain corrupt objects.
+ */
+ private volatile LongList corruptObjects;
+
+ /**
+ * Construct a reader for an existing, pre-indexed packfile.
+ *
+ * @param idxFile
+ * path of the <code>.idx</code> file listing the contents.
+ * @param packFile
+ * path of the <code>.pack</code> file holding the data.
+ */
+ public PackFile(final File idxFile, final File packFile) {
+ this.idxFile = idxFile;
+ this.packFile = packFile;
+ this.packLastModified = (int) (packFile.lastModified() >> 10);
+
+ // Multiply by 31 here so we can more directly combine with another
+ // value in WindowCache.hash(), without doing the multiply there.
+ //
+ hash = System.identityHashCode(this) * 31;
+ length = Long.MAX_VALUE;
+ }
+
+ private synchronized PackIndex idx() throws IOException {
+ if (loadedIdx == null) {
+ if (invalid)
+ throw new PackInvalidException(packFile);
+
+ try {
+ final PackIndex idx = PackIndex.open(idxFile);
+
+ if (packChecksum == null)
+ packChecksum = idx.packChecksum;
+ else if (!Arrays.equals(packChecksum, idx.packChecksum))
+ throw new PackMismatchException(JGitText.get().packChecksumMismatch);
+
+ loadedIdx = idx;
+ } catch (IOException e) {
+ invalid = true;
+ throw e;
+ }
+ }
+ return loadedIdx;
+ }
+
+ /** @return the File object which locates this pack on disk. */
+ public File getPackFile() {
+ return packFile;
+ }
+
+ /**
+ * Determine if an object is contained within the pack file.
+ * <p>
+ * For performance reasons only the index file is searched; the main pack
+ * content is ignored entirely.
+ * </p>
+ *
+ * @param id
+ * the object to look for. Must not be null.
+ * @return true if the object is in this pack; false otherwise.
+ * @throws IOException
+ * the index file cannot be loaded into memory.
+ */
+ public boolean hasObject(final AnyObjectId id) throws IOException {
+ final long offset = idx().findOffset(id);
+ return 0 < offset && !isCorrupt(offset);
+ }
+
+ /**
+ * Get an object from this pack.
+ *
+ * @param curs
+ * temporary working space associated with the calling thread.
+ * @param id
+ * the object to obtain from the pack. Must not be null.
+ * @return the object loader for the requested object if it is contained in
+ * this pack; null if the object was not found.
+ * @throws IOException
+ * the pack file or the index could not be read.
+ */
+ ObjectLoader get(final WindowCursor curs, final AnyObjectId id)
+ throws IOException {
+ final long offset = idx().findOffset(id);
+ return 0 < offset && !isCorrupt(offset) ? load(curs, offset) : null;
+ }
+
+ /**
+ * Close the resources utilized by this repository
+ */
+ public void close() {
+ UnpackedObjectCache.purge(this);
+ WindowCache.purge(this);
+ synchronized (this) {
+ loadedIdx = null;
+ reverseIdx = null;
+ }
+ }
+
+ /**
+ * Provide iterator over entries in associated pack index, that should also
+ * exist in this pack file. Objects returned by such iterator are mutable
+ * during iteration.
+ * <p>
+ * Iterator returns objects in SHA-1 lexicographical order.
+ * </p>
+ *
+ * @return iterator over entries of associated pack index
+ *
+ * @see PackIndex#iterator()
+ */
+ public Iterator<PackIndex.MutableEntry> iterator() {
+ try {
+ return idx().iterator();
+ } catch (IOException e) {
+ return Collections.<PackIndex.MutableEntry> emptyList().iterator();
+ }
+ }
+
+ /**
+ * Obtain the total number of objects available in this pack. This method
+ * relies on pack index, giving number of effectively available objects.
+ *
+ * @return number of objects in index of this pack, likewise in this pack
+ * @throws IOException
+ * the index file cannot be loaded into memory.
+ */
+ long getObjectCount() throws IOException {
+ return idx().getObjectCount();
+ }
+
+ /**
+ * Search for object id with the specified start offset in associated pack
+ * (reverse) index.
+ *
+ * @param offset
+ * start offset of object to find
+ * @return object id for this offset, or null if no object was found
+ * @throws IOException
+ * the index file cannot be loaded into memory.
+ */
+ ObjectId findObjectForOffset(final long offset) throws IOException {
+ return getReverseIdx().findObject(offset);
+ }
+
+ private final UnpackedObjectCache.Entry readCache(final long position) {
+ return UnpackedObjectCache.get(this, position);
+ }
+
+ private final void saveCache(final long position, final byte[] data, final int type) {
+ UnpackedObjectCache.store(this, position, data, type);
+ }
+
+ private final byte[] decompress(final long position, final long totalSize,
+ final WindowCursor curs) throws IOException, DataFormatException {
+ final byte[] dstbuf = new byte[(int) totalSize];
+ if (curs.inflate(this, position, dstbuf, 0) != totalSize)
+ throw new EOFException(MessageFormat.format(JGitText.get().shortCompressedStreamAt, position));
+ return dstbuf;
+ }
+
+ final void copyAsIs(PackOutputStream out, LocalObjectToPack src,
+ WindowCursor curs) throws IOException,
+ StoredObjectRepresentationNotAvailableException {
+ beginCopyAsIs(src);
+ try {
+ copyAsIs2(out, src, curs);
+ } finally {
+ endCopyAsIs();
+ }
+ }
+
+ private void copyAsIs2(PackOutputStream out, LocalObjectToPack src,
+ WindowCursor curs) throws IOException,
+ StoredObjectRepresentationNotAvailableException {
+ final CRC32 crc1 = new CRC32();
+ final CRC32 crc2 = new CRC32();
+ final byte[] buf = out.getCopyBuffer();
+
+ // Rip apart the header so we can discover the size.
+ //
+ readFully(src.offset, buf, 0, 20, curs);
+ int c = buf[0] & 0xff;
+ final int typeCode = (c >> 4) & 7;
+ long inflatedLength = c & 15;
+ int shift = 4;
+ int headerCnt = 1;
+ while ((c & 0x80) != 0) {
+ c = buf[headerCnt++] & 0xff;
+ inflatedLength += (c & 0x7f) << shift;
+ shift += 7;
+ }
+
+ if (typeCode == Constants.OBJ_OFS_DELTA) {
+ do {
+ c = buf[headerCnt++] & 0xff;
+ } while ((c & 128) != 0);
+ crc1.update(buf, 0, headerCnt);
+ crc2.update(buf, 0, headerCnt);
+ } else if (typeCode == Constants.OBJ_REF_DELTA) {
+ crc1.update(buf, 0, headerCnt);
+ crc2.update(buf, 0, headerCnt);
+
+ readFully(src.offset + headerCnt, buf, 0, 20, curs);
+ crc1.update(buf, 0, 20);
+ crc2.update(buf, 0, headerCnt);
+ headerCnt += 20;
+ } else {
+ crc1.update(buf, 0, headerCnt);
+ crc2.update(buf, 0, headerCnt);
+ }
+
+ final long dataOffset = src.offset + headerCnt;
+ final long dataLength = src.length;
+ final long expectedCRC;
+ final ByteArrayWindow quickCopy;
+
+ // Verify the object isn't corrupt before sending. If it is,
+ // we report it missing instead.
+ //
+ try {
+ quickCopy = curs.quickCopy(this, dataOffset, dataLength);
+
+ if (idx().hasCRC32Support()) {
+ // Index has the CRC32 code cached, validate the object.
+ //
+ expectedCRC = idx().findCRC32(src);
+ if (quickCopy != null) {
+ quickCopy.crc32(crc1, dataOffset, (int) dataLength);
+ } else {
+ long pos = dataOffset;
+ long cnt = dataLength;
+ while (cnt > 0) {
+ final int n = (int) Math.min(cnt, buf.length);
+ readFully(pos, buf, 0, n, curs);
+ crc1.update(buf, 0, n);
+ pos += n;
+ cnt -= n;
+ }
+ }
+ if (crc1.getValue() != expectedCRC) {
+ setCorrupt(src.offset);
+ throw new CorruptObjectException(MessageFormat.format(
+ JGitText.get().objectAtHasBadZlibStream,
+ src.offset, getPackFile()));
+ }
+ } else {
+ // We don't have a CRC32 code in the index, so compute it
+ // now while inflating the raw data to get zlib to tell us
+ // whether or not the data is safe.
+ //
+ Inflater inf = curs.inflater();
+ byte[] tmp = new byte[1024];
+ if (quickCopy != null) {
+ quickCopy.check(inf, tmp, dataOffset, (int) dataLength);
+ } else {
+ long pos = dataOffset;
+ long cnt = dataLength;
+ while (cnt > 0) {
+ final int n = (int) Math.min(cnt, buf.length);
+ readFully(pos, buf, 0, n, curs);
+ crc1.update(buf, 0, n);
+ inf.setInput(buf, 0, n);
+ while (inf.inflate(tmp, 0, tmp.length) > 0)
+ continue;
+ pos += n;
+ cnt -= n;
+ }
+ }
+ if (!inf.finished() || inf.getBytesRead() != dataLength) {
+ setCorrupt(src.offset);
+ throw new EOFException(MessageFormat.format(
+ JGitText.get().shortCompressedStreamAt,
+ src.offset));
+ }
+ expectedCRC = crc1.getValue();
+ }
+ } catch (DataFormatException dataFormat) {
+ setCorrupt(src.offset);
+
+ CorruptObjectException corruptObject = new CorruptObjectException(
+ MessageFormat.format(
+ JGitText.get().objectAtHasBadZlibStream,
+ src.offset, getPackFile()));
+ corruptObject.initCause(dataFormat);
+
+ StoredObjectRepresentationNotAvailableException gone;
+ gone = new StoredObjectRepresentationNotAvailableException(src);
+ gone.initCause(corruptObject);
+ throw gone;
+
+ } catch (IOException ioError) {
+ StoredObjectRepresentationNotAvailableException gone;
+ gone = new StoredObjectRepresentationNotAvailableException(src);
+ gone.initCause(ioError);
+ throw gone;
+ }
+
+ if (quickCopy != null) {
+ // The entire object fits into a single byte array window slice,
+ // and we have it pinned. Write this out without copying.
+ //
+ out.writeHeader(src, inflatedLength);
+ quickCopy.write(out, dataOffset, (int) dataLength);
+
+ } else if (dataLength <= buf.length) {
+ // Tiny optimization: Lots of objects are very small deltas or
+ // deflated commits that are likely to fit in the copy buffer.
+ //
+ out.writeHeader(src, inflatedLength);
+ out.write(buf, 0, (int) dataLength);
+ } else {
+ // Now we are committed to sending the object. As we spool it out,
+ // check its CRC32 code to make sure there wasn't corruption between
+ // the verification we did above, and us actually outputting it.
+ //
+ out.writeHeader(src, inflatedLength);
+ long pos = dataOffset;
+ long cnt = dataLength;
+ while (cnt > 0) {
+ final int n = (int) Math.min(cnt, buf.length);
+ readFully(pos, buf, 0, n, curs);
+ crc2.update(buf, 0, n);
+ out.write(buf, 0, n);
+ pos += n;
+ cnt -= n;
+ }
+ if (crc2.getValue() != expectedCRC) {
+ throw new CorruptObjectException(MessageFormat.format(JGitText
+ .get().objectAtHasBadZlibStream, src.offset,
+ getPackFile()));
+ }
+ }
+ }
+
+ boolean invalid() {
+ return invalid;
+ }
+
+ private void readFully(final long position, final byte[] dstbuf,
+ int dstoff, final int cnt, final WindowCursor curs)
+ throws IOException {
+ if (curs.copy(this, position, dstbuf, dstoff, cnt) != cnt)
+ throw new EOFException();
+ }
+
+ private synchronized void beginCopyAsIs(ObjectToPack otp)
+ throws StoredObjectRepresentationNotAvailableException {
+ if (++activeCopyRawData == 1 && activeWindows == 0) {
+ try {
+ doOpen();
+ } catch (IOException thisPackNotValid) {
+ StoredObjectRepresentationNotAvailableException gone;
+
+ gone = new StoredObjectRepresentationNotAvailableException(otp);
+ gone.initCause(thisPackNotValid);
+ throw gone;
+ }
+ }
+ }
+
+ private synchronized void endCopyAsIs() {
+ if (--activeCopyRawData == 0 && activeWindows == 0)
+ doClose();
+ }
+
+ synchronized boolean beginWindowCache() throws IOException {
+ if (++activeWindows == 1) {
+ if (activeCopyRawData == 0)
+ doOpen();
+ return true;
+ }
+ return false;
+ }
+
+ synchronized boolean endWindowCache() {
+ final boolean r = --activeWindows == 0;
+ if (r && activeCopyRawData == 0)
+ doClose();
+ return r;
+ }
+
+ private void doOpen() throws IOException {
+ try {
+ if (invalid)
+ throw new PackInvalidException(packFile);
+ synchronized (readLock) {
+ fd = new RandomAccessFile(packFile, "r");
+ length = fd.length();
+ onOpenPack();
+ }
+ } catch (IOException ioe) {
+ openFail();
+ throw ioe;
+ } catch (RuntimeException re) {
+ openFail();
+ throw re;
+ } catch (Error re) {
+ openFail();
+ throw re;
+ }
+ }
+
+ private void openFail() {
+ activeWindows = 0;
+ activeCopyRawData = 0;
+ invalid = true;
+ doClose();
+ }
+
+ private void doClose() {
+ synchronized (readLock) {
+ if (fd != null) {
+ try {
+ fd.close();
+ } catch (IOException err) {
+ // Ignore a close event. We had it open only for reading.
+ // There should not be errors related to network buffers
+ // not flushed, etc.
+ }
+ fd = null;
+ }
+ }
+ }
+
+ ByteArrayWindow read(final long pos, int size) throws IOException {
+ synchronized (readLock) {
+ if (length < pos + size)
+ size = (int) (length - pos);
+ final byte[] buf = new byte[size];
+ fd.seek(pos);
+ fd.readFully(buf, 0, size);
+ return new ByteArrayWindow(this, pos, buf);
+ }
+ }
+
+ ByteWindow mmap(final long pos, int size) throws IOException {
+ synchronized (readLock) {
+ if (length < pos + size)
+ size = (int) (length - pos);
+
+ MappedByteBuffer map;
+ try {
+ map = fd.getChannel().map(MapMode.READ_ONLY, pos, size);
+ } catch (IOException ioe1) {
+ // The most likely reason this failed is the JVM has run out
+ // of virtual memory. We need to discard quickly, and try to
+ // force the GC to finalize and release any existing mappings.
+ //
+ System.gc();
+ System.runFinalization();
+ map = fd.getChannel().map(MapMode.READ_ONLY, pos, size);
+ }
+
+ if (map.hasArray())
+ return new ByteArrayWindow(this, pos, map.array());
+ return new ByteBufferWindow(this, pos, map);
+ }
+ }
+
+ private void onOpenPack() throws IOException {
+ final PackIndex idx = idx();
+ final byte[] buf = new byte[20];
+
+ fd.seek(0);
+ fd.readFully(buf, 0, 12);
+ if (RawParseUtils.match(buf, 0, Constants.PACK_SIGNATURE) != 4)
+ throw new IOException(JGitText.get().notAPACKFile);
+ final long vers = NB.decodeUInt32(buf, 4);
+ final long packCnt = NB.decodeUInt32(buf, 8);
+ if (vers != 2 && vers != 3)
+ throw new IOException(MessageFormat.format(JGitText.get().unsupportedPackVersion, vers));
+
+ if (packCnt != idx.getObjectCount())
+ throw new PackMismatchException(MessageFormat.format(
+ JGitText.get().packObjectCountMismatch, packCnt, idx.getObjectCount(), getPackFile()));
+
+ fd.seek(length - 20);
+ fd.read(buf, 0, 20);
+ if (!Arrays.equals(buf, packChecksum))
+ throw new PackMismatchException(MessageFormat.format(
+ JGitText.get().packObjectCountMismatch
+ , ObjectId.fromRaw(buf).name()
+ , ObjectId.fromRaw(idx.packChecksum).name()
+ , getPackFile()));
+ }
+
+ ObjectLoader load(final WindowCursor curs, final long pos)
+ throws IOException {
+ final byte[] ib = curs.tempId;
+ readFully(pos, ib, 0, 20, curs);
+ int c = ib[0] & 0xff;
+ final int type = (c >> 4) & 7;
+ long sz = c & 15;
+ int shift = 4;
+ int p = 1;
+ while ((c & 0x80) != 0) {
+ c = ib[p++] & 0xff;
+ sz += (c & 0x7f) << shift;
+ shift += 7;
+ }
+
+ try {
+ switch (type) {
+ case Constants.OBJ_COMMIT:
+ case Constants.OBJ_TREE:
+ case Constants.OBJ_BLOB:
+ case Constants.OBJ_TAG: {
+ if (sz < curs.getStreamFileThreshold()) {
+ byte[] data = decompress(pos + p, sz, curs);
+ return new ObjectLoader.SmallObject(type, data);
+ }
+ return new LargePackedWholeObject(type, sz, pos, p, this, curs.db);
+ }
+
+ case Constants.OBJ_OFS_DELTA: {
+ c = ib[p++] & 0xff;
+ long ofs = c & 127;
+ while ((c & 128) != 0) {
+ ofs += 1;
+ c = ib[p++] & 0xff;
+ ofs <<= 7;
+ ofs += (c & 127);
+ }
+ return loadDelta(pos, p, sz, pos - ofs, curs);
+ }
+
+ case Constants.OBJ_REF_DELTA: {
+ readFully(pos + p, ib, 0, 20, curs);
+ long ofs = findDeltaBase(ObjectId.fromRaw(ib));
+ return loadDelta(pos, p + 20, sz, ofs, curs);
+ }
+
+ default:
+ throw new IOException(MessageFormat.format(
+ JGitText.get().unknownObjectType, type));
+ }
+ } catch (DataFormatException dfe) {
+ CorruptObjectException coe = new CorruptObjectException(
+ MessageFormat.format(
+ JGitText.get().objectAtHasBadZlibStream, pos,
+ getPackFile()));
+ coe.initCause(dfe);
+ throw coe;
+ }
+ }
+
+ private long findDeltaBase(ObjectId baseId) throws IOException,
+ MissingObjectException {
+ long ofs = idx().findOffset(baseId);
+ if (ofs < 0)
+ throw new MissingObjectException(baseId,
+ JGitText.get().missingDeltaBase);
+ return ofs;
+ }
+
+ private ObjectLoader loadDelta(long posSelf, int hdrLen, long sz,
+ long posBase, WindowCursor curs) throws IOException,
+ DataFormatException {
+ if (curs.getStreamFileThreshold() <= sz) {
+ // The delta instruction stream itself is pretty big, and
+ // that implies the resulting object is going to be massive.
+ // Use only the large delta format here.
+ //
+ return new LargePackedDeltaObject(posSelf, posBase, hdrLen, //
+ this, curs.db);
+ }
+
+ byte[] data;
+ int type;
+
+ UnpackedObjectCache.Entry e = readCache(posBase);
+ if (e != null) {
+ data = e.data;
+ type = e.type;
+ } else {
+ ObjectLoader p = load(curs, posBase);
+ if (p.isLarge()) {
+ // The base itself is large. We have to produce a large
+ // delta stream as we don't want to build the whole base.
+ //
+ return new LargePackedDeltaObject(posSelf, posBase, hdrLen,
+ this, curs.db);
+ }
+ data = p.getCachedBytes();
+ type = p.getType();
+ saveCache(posBase, data, type);
+ }
+
+ // At this point we have the base, and its small, and the delta
+ // stream also is small, so the result object cannot be more than
+ // 2x our small size. This occurs if the delta instructions were
+ // "copy entire base, literal insert entire delta". Go with the
+ // faster small object style at this point.
+ //
+ data = BinaryDelta.apply(data, decompress(posSelf + hdrLen, sz, curs));
+ return new ObjectLoader.SmallObject(type, data);
+ }
+
+ byte[] getDeltaHeader(WindowCursor wc, long pos)
+ throws IOException, DataFormatException {
+ // The delta stream starts as two variable length integers. If we
+ // assume they are 64 bits each, we need 16 bytes to encode them,
+ // plus 2 extra bytes for the variable length overhead. So 18 is
+ // the longest delta instruction header.
+ //
+ final byte[] hdr = new byte[18];
+ wc.inflate(this, pos, hdr, 0);
+ return hdr;
+ }
+
+ int getObjectType(final WindowCursor curs, long pos) throws IOException {
+ final byte[] ib = curs.tempId;
+ for (;;) {
+ readFully(pos, ib, 0, 20, curs);
+ int c = ib[0] & 0xff;
+ final int type = (c >> 4) & 7;
+ int shift = 4;
+ int p = 1;
+ while ((c & 0x80) != 0) {
+ c = ib[p++] & 0xff;
+ shift += 7;
+ }
+
+ switch (type) {
+ case Constants.OBJ_COMMIT:
+ case Constants.OBJ_TREE:
+ case Constants.OBJ_BLOB:
+ case Constants.OBJ_TAG:
+ return type;
+
+ case Constants.OBJ_OFS_DELTA: {
+ c = ib[p++] & 0xff;
+ long ofs = c & 127;
+ while ((c & 128) != 0) {
+ ofs += 1;
+ c = ib[p++] & 0xff;
+ ofs <<= 7;
+ ofs += (c & 127);
+ }
+ pos = pos - ofs;
+ continue;
+ }
+
+ case Constants.OBJ_REF_DELTA: {
+ readFully(pos + p, ib, 0, 20, curs);
+ pos = findDeltaBase(ObjectId.fromRaw(ib));
+ continue;
+ }
+
+ default:
+ throw new IOException(MessageFormat.format(
+ JGitText.get().unknownObjectType, type));
+ }
+ }
+ }
+
+ long getObjectSize(final WindowCursor curs, final AnyObjectId id)
+ throws IOException {
+ final long offset = idx().findOffset(id);
+ return 0 < offset ? getObjectSize(curs, offset) : -1;
+ }
+
+ long getObjectSize(final WindowCursor curs, final long pos)
+ throws IOException {
+ final byte[] ib = curs.tempId;
+ readFully(pos, ib, 0, 20, curs);
+ int c = ib[0] & 0xff;
+ final int type = (c >> 4) & 7;
+ long sz = c & 15;
+ int shift = 4;
+ int p = 1;
+ while ((c & 0x80) != 0) {
+ c = ib[p++] & 0xff;
+ sz += (c & 0x7f) << shift;
+ shift += 7;
+ }
+
+ long deltaAt;
+ switch (type) {
+ case Constants.OBJ_COMMIT:
+ case Constants.OBJ_TREE:
+ case Constants.OBJ_BLOB:
+ case Constants.OBJ_TAG:
+ return sz;
+
+ case Constants.OBJ_OFS_DELTA:
+ c = ib[p++] & 0xff;
+ while ((c & 128) != 0)
+ c = ib[p++] & 0xff;
+ deltaAt = pos + p;
+ break;
+
+ case Constants.OBJ_REF_DELTA:
+ deltaAt = pos + p + 20;
+ break;
+
+ default:
+ throw new IOException(MessageFormat.format(
+ JGitText.get().unknownObjectType, type));
+ }
+
+ try {
+ return BinaryDelta.getResultSize(getDeltaHeader(curs, deltaAt));
+ } catch (DataFormatException e) {
+ throw new CorruptObjectException(MessageFormat.format(JGitText
+ .get().objectAtHasBadZlibStream, pos, getPackFile()));
+ }
+ }
+
+ LocalObjectRepresentation representation(final WindowCursor curs,
+ final AnyObjectId objectId) throws IOException {
+ final long pos = idx().findOffset(objectId);
+ if (pos < 0)
+ return null;
+
+ final byte[] ib = curs.tempId;
+ readFully(pos, ib, 0, 20, curs);
+ int c = ib[0] & 0xff;
+ int p = 1;
+ final int typeCode = (c >> 4) & 7;
+ while ((c & 0x80) != 0)
+ c = ib[p++] & 0xff;
+
+ long len = (findEndOffset(pos) - pos);
+ switch (typeCode) {
+ case Constants.OBJ_COMMIT:
+ case Constants.OBJ_TREE:
+ case Constants.OBJ_BLOB:
+ case Constants.OBJ_TAG:
+ return LocalObjectRepresentation.newWhole(this, pos, len - p);
+
+ case Constants.OBJ_OFS_DELTA: {
+ c = ib[p++] & 0xff;
+ long ofs = c & 127;
+ while ((c & 128) != 0) {
+ ofs += 1;
+ c = ib[p++] & 0xff;
+ ofs <<= 7;
+ ofs += (c & 127);
+ }
+ ofs = pos - ofs;
+ return LocalObjectRepresentation.newDelta(this, pos, len - p, ofs);
+ }
+
+ case Constants.OBJ_REF_DELTA: {
+ len -= p;
+ len -= Constants.OBJECT_ID_LENGTH;
+ readFully(pos + p, ib, 0, 20, curs);
+ ObjectId id = ObjectId.fromRaw(ib);
+ return LocalObjectRepresentation.newDelta(this, pos, len, id);
+ }
+
+ default:
+ throw new IOException(MessageFormat.format(
+ JGitText.get().unknownObjectType, typeCode));
+ }
+ }
+
+ private long findEndOffset(final long startOffset)
+ throws IOException, CorruptObjectException {
+ final long maxOffset = length - 20;
+ return getReverseIdx().findNextOffset(startOffset, maxOffset);
+ }
+
+ private synchronized PackReverseIndex getReverseIdx() throws IOException {
+ if (reverseIdx == null)
+ reverseIdx = new PackReverseIndex(idx());
+ return reverseIdx;
+ }
+
+ private boolean isCorrupt(long offset) {
+ LongList list = corruptObjects;
+ if (list == null)
+ return false;
+ synchronized (list) {
+ return list.contains(offset);
+ }
+ }
+
+ private void setCorrupt(long offset) {
+ LongList list = corruptObjects;
+ if (list == null) {
+ synchronized (readLock) {
+ list = corruptObjects;
+ if (list == null) {
+ list = new LongList();
+ corruptObjects = list;
+ }
+ }
+ }
+ synchronized (list) {
+ list.add(offset);
+ }
+ }
+}
diff --git a/org.eclipse.jgit/src/org/eclipse/jgit/lib/PackIndex.java b/org.eclipse.jgit/src/org/eclipse/jgit/storage/file/PackIndex.java
similarity index 98%
rename from org.eclipse.jgit/src/org/eclipse/jgit/lib/PackIndex.java
rename to org.eclipse.jgit/src/org/eclipse/jgit/storage/file/PackIndex.java
index 13985e7..62d1c9d 100644
--- a/org.eclipse.jgit/src/org/eclipse/jgit/lib/PackIndex.java
+++ b/org.eclipse.jgit/src/org/eclipse/jgit/storage/file/PackIndex.java
@@ -42,7 +42,7 @@
* ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
*/
-package org.eclipse.jgit.lib;
+package org.eclipse.jgit.storage.file;
import java.io.File;
import java.io.FileInputStream;
@@ -53,6 +53,9 @@
import org.eclipse.jgit.JGitText;
import org.eclipse.jgit.errors.MissingObjectException;
+import org.eclipse.jgit.lib.AnyObjectId;
+import org.eclipse.jgit.lib.MutableObjectId;
+import org.eclipse.jgit.lib.ObjectId;
import org.eclipse.jgit.util.IO;
import org.eclipse.jgit.util.NB;
diff --git a/org.eclipse.jgit/src/org/eclipse/jgit/lib/PackIndexV1.java b/org.eclipse.jgit/src/org/eclipse/jgit/storage/file/PackIndexV1.java
similarity index 97%
rename from org.eclipse.jgit/src/org/eclipse/jgit/lib/PackIndexV1.java
rename to org.eclipse.jgit/src/org/eclipse/jgit/storage/file/PackIndexV1.java
index bb7cd8b..3b68edc 100644
--- a/org.eclipse.jgit/src/org/eclipse/jgit/lib/PackIndexV1.java
+++ b/org.eclipse.jgit/src/org/eclipse/jgit/storage/file/PackIndexV1.java
@@ -44,7 +44,7 @@
* ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
*/
-package org.eclipse.jgit.lib;
+package org.eclipse.jgit.storage.file;
import java.io.IOException;
import java.io.InputStream;
@@ -53,6 +53,9 @@
import java.util.NoSuchElementException;
import org.eclipse.jgit.errors.CorruptObjectException;
+import org.eclipse.jgit.lib.AnyObjectId;
+import org.eclipse.jgit.lib.Constants;
+import org.eclipse.jgit.lib.ObjectId;
import org.eclipse.jgit.util.IO;
import org.eclipse.jgit.util.NB;
diff --git a/org.eclipse.jgit/src/org/eclipse/jgit/lib/PackIndexV2.java b/org.eclipse.jgit/src/org/eclipse/jgit/storage/file/PackIndexV2.java
similarity index 97%
rename from org.eclipse.jgit/src/org/eclipse/jgit/lib/PackIndexV2.java
rename to org.eclipse.jgit/src/org/eclipse/jgit/storage/file/PackIndexV2.java
index 128b2df..cef7cc4 100644
--- a/org.eclipse.jgit/src/org/eclipse/jgit/lib/PackIndexV2.java
+++ b/org.eclipse.jgit/src/org/eclipse/jgit/storage/file/PackIndexV2.java
@@ -41,7 +41,7 @@
* ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
*/
-package org.eclipse.jgit.lib;
+package org.eclipse.jgit.storage.file;
import java.io.IOException;
import java.io.InputStream;
@@ -51,6 +51,9 @@
import org.eclipse.jgit.JGitText;
import org.eclipse.jgit.errors.MissingObjectException;
+import org.eclipse.jgit.lib.AnyObjectId;
+import org.eclipse.jgit.lib.Constants;
+import org.eclipse.jgit.lib.ObjectId;
import org.eclipse.jgit.util.IO;
import org.eclipse.jgit.util.NB;
diff --git a/org.eclipse.jgit/src/org/eclipse/jgit/lib/PackIndexWriter.java b/org.eclipse.jgit/src/org/eclipse/jgit/storage/file/PackIndexWriter.java
similarity index 98%
rename from org.eclipse.jgit/src/org/eclipse/jgit/lib/PackIndexWriter.java
rename to org.eclipse.jgit/src/org/eclipse/jgit/storage/file/PackIndexWriter.java
index 4d2714b..6bd73ad 100644
--- a/org.eclipse.jgit/src/org/eclipse/jgit/lib/PackIndexWriter.java
+++ b/org.eclipse.jgit/src/org/eclipse/jgit/storage/file/PackIndexWriter.java
@@ -42,7 +42,7 @@
* ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
*/
-package org.eclipse.jgit.lib;
+package org.eclipse.jgit.storage.file;
import java.io.BufferedOutputStream;
import java.io.IOException;
@@ -52,6 +52,8 @@
import java.util.List;
import org.eclipse.jgit.JGitText;
+import org.eclipse.jgit.lib.Constants;
+import org.eclipse.jgit.lib.ObjectId;
import org.eclipse.jgit.transport.PackedObjectInfo;
import org.eclipse.jgit.util.NB;
diff --git a/org.eclipse.jgit/src/org/eclipse/jgit/lib/PackIndexWriterV1.java b/org.eclipse.jgit/src/org/eclipse/jgit/storage/file/PackIndexWriterV1.java
similarity index 98%
rename from org.eclipse.jgit/src/org/eclipse/jgit/lib/PackIndexWriterV1.java
rename to org.eclipse.jgit/src/org/eclipse/jgit/storage/file/PackIndexWriterV1.java
index eb44b3a..722ab0e 100644
--- a/org.eclipse.jgit/src/org/eclipse/jgit/lib/PackIndexWriterV1.java
+++ b/org.eclipse.jgit/src/org/eclipse/jgit/storage/file/PackIndexWriterV1.java
@@ -42,7 +42,7 @@
* ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
*/
-package org.eclipse.jgit.lib;
+package org.eclipse.jgit.storage.file;
import java.io.IOException;
import java.io.OutputStream;
diff --git a/org.eclipse.jgit/src/org/eclipse/jgit/lib/PackIndexWriterV2.java b/org.eclipse.jgit/src/org/eclipse/jgit/storage/file/PackIndexWriterV2.java
similarity index 98%
rename from org.eclipse.jgit/src/org/eclipse/jgit/lib/PackIndexWriterV2.java
rename to org.eclipse.jgit/src/org/eclipse/jgit/storage/file/PackIndexWriterV2.java
index b6ac7b8..21ebd1c 100644
--- a/org.eclipse.jgit/src/org/eclipse/jgit/lib/PackIndexWriterV2.java
+++ b/org.eclipse.jgit/src/org/eclipse/jgit/storage/file/PackIndexWriterV2.java
@@ -41,7 +41,7 @@
* ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
*/
-package org.eclipse.jgit.lib;
+package org.eclipse.jgit.storage.file;
import java.io.IOException;
import java.io.OutputStream;
diff --git a/org.eclipse.jgit/src/org/eclipse/jgit/lib/RepositoryAdapter.java b/org.eclipse.jgit/src/org/eclipse/jgit/storage/file/PackInputStream.java
similarity index 69%
copy from org.eclipse.jgit/src/org/eclipse/jgit/lib/RepositoryAdapter.java
copy to org.eclipse.jgit/src/org/eclipse/jgit/storage/file/PackInputStream.java
index e43c33a..5425eed 100644
--- a/org.eclipse.jgit/src/org/eclipse/jgit/lib/RepositoryAdapter.java
+++ b/org.eclipse.jgit/src/org/eclipse/jgit/storage/file/PackInputStream.java
@@ -1,5 +1,5 @@
/*
- * Copyright (C) 2008, Robin Rosenberg <robin.rosenberg@dewire.com>
+ * Copyright (C) 2010, Google Inc.
* and other copyright owners as documented in the project's IP log.
*
* This program and the accompanying materials are made available
@@ -41,20 +41,45 @@
* ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
*/
-package org.eclipse.jgit.lib;
+package org.eclipse.jgit.storage.file;
-/**
- * A default {@link RepositoryListener} that does nothing except invoke an
- * optional general method for any repository change.
- */
-public class RepositoryAdapter implements RepositoryListener {
+import java.io.IOException;
+import java.io.InputStream;
- public void indexChanged(final IndexChangedEvent e) {
- // Empty
+class PackInputStream extends InputStream {
+ private final WindowCursor wc;
+
+ private final PackFile pack;
+
+ private long pos;
+
+ PackInputStream(PackFile pack, long pos, WindowCursor wc)
+ throws IOException {
+ this.pack = pack;
+ this.pos = pos;
+ this.wc = wc;
+
+ // Pin the first window, to ensure the pack is open and valid.
+ //
+ wc.pin(pack, pos);
}
- public void refsChanged(final RefsChangedEvent e) {
- // Empty
+ @Override
+ public int read(byte[] b, int off, int len) throws IOException {
+ int n = wc.copy(pack, pos, b, off, len);
+ pos += n;
+ return n;
}
-}
+ @Override
+ public int read() throws IOException {
+ byte[] buf = new byte[1];
+ int n = read(buf, 0, 1);
+ return n == 1 ? buf[0] & 0xff : -1;
+ }
+
+ @Override
+ public void close() {
+ wc.release();
+ }
+}
\ No newline at end of file
diff --git a/org.eclipse.jgit/src/org/eclipse/jgit/lib/PackLock.java b/org.eclipse.jgit/src/org/eclipse/jgit/storage/file/PackLock.java
similarity index 89%
rename from org.eclipse.jgit/src/org/eclipse/jgit/lib/PackLock.java
rename to org.eclipse.jgit/src/org/eclipse/jgit/storage/file/PackLock.java
index de8e3fa..dd08dfb 100644
--- a/org.eclipse.jgit/src/org/eclipse/jgit/lib/PackLock.java
+++ b/org.eclipse.jgit/src/org/eclipse/jgit/storage/file/PackLock.java
@@ -41,25 +41,32 @@
* ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
*/
-package org.eclipse.jgit.lib;
+package org.eclipse.jgit.storage.file;
import java.io.File;
import java.io.IOException;
+import org.eclipse.jgit.lib.Constants;
+import org.eclipse.jgit.util.FS;
+
/** Keeps track of a {@link PackFile}'s associated <code>.keep</code> file. */
public class PackLock {
private final File keepFile;
+ private final FS fs;
/**
* Create a new lock for a pack file.
*
* @param packFile
* location of the <code>pack-*.pack</code> file.
+ * @param fs
+ * the filesystem abstraction used by the repository.
*/
- public PackLock(final File packFile) {
+ public PackLock(final File packFile, final FS fs) {
final File p = packFile.getParentFile();
final String n = packFile.getName();
keepFile = new File(p, n.substring(0, n.length() - 5) + ".keep");
+ this.fs = fs;
}
/**
@@ -76,7 +83,7 @@ public boolean lock(String msg) throws IOException {
return false;
if (!msg.endsWith("\n"))
msg += "\n";
- final LockFile lf = new LockFile(keepFile);
+ final LockFile lf = new LockFile(keepFile, fs);
if (!lf.lock())
return false;
lf.write(Constants.encode(msg));
diff --git a/org.eclipse.jgit/src/org/eclipse/jgit/lib/PackReverseIndex.java b/org.eclipse.jgit/src/org/eclipse/jgit/storage/file/PackReverseIndex.java
similarity index 97%
rename from org.eclipse.jgit/src/org/eclipse/jgit/lib/PackReverseIndex.java
rename to org.eclipse.jgit/src/org/eclipse/jgit/storage/file/PackReverseIndex.java
index f4f57ae..96abaee 100644
--- a/org.eclipse.jgit/src/org/eclipse/jgit/lib/PackReverseIndex.java
+++ b/org.eclipse.jgit/src/org/eclipse/jgit/storage/file/PackReverseIndex.java
@@ -41,14 +41,15 @@
* ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
*/
-package org.eclipse.jgit.lib;
+package org.eclipse.jgit.storage.file;
import java.text.MessageFormat;
import java.util.Arrays;
import org.eclipse.jgit.JGitText;
import org.eclipse.jgit.errors.CorruptObjectException;
-import org.eclipse.jgit.lib.PackIndex.MutableEntry;
+import org.eclipse.jgit.lib.ObjectId;
+import org.eclipse.jgit.storage.file.PackIndex.MutableEntry;
/**
* <p>
diff --git a/org.eclipse.jgit/src/org/eclipse/jgit/lib/RefDirectory.java b/org.eclipse.jgit/src/org/eclipse/jgit/storage/file/RefDirectory.java
similarity index 95%
rename from org.eclipse.jgit/src/org/eclipse/jgit/lib/RefDirectory.java
rename to org.eclipse.jgit/src/org/eclipse/jgit/storage/file/RefDirectory.java
index 302b63b..b22b14a 100644
--- a/org.eclipse.jgit/src/org/eclipse/jgit/lib/RefDirectory.java
+++ b/org.eclipse.jgit/src/org/eclipse/jgit/storage/file/RefDirectory.java
@@ -44,7 +44,7 @@
* ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
*/
-package org.eclipse.jgit.lib;
+package org.eclipse.jgit.storage.file;
import static org.eclipse.jgit.lib.Constants.CHARSET;
import static org.eclipse.jgit.lib.Constants.HEAD;
@@ -74,7 +74,21 @@
import java.util.concurrent.atomic.AtomicReference;
import org.eclipse.jgit.JGitText;
+import org.eclipse.jgit.errors.MissingObjectException;
import org.eclipse.jgit.errors.ObjectWritingException;
+import org.eclipse.jgit.events.RefsChangedEvent;
+import org.eclipse.jgit.lib.Constants;
+import org.eclipse.jgit.lib.CoreConfig;
+import org.eclipse.jgit.lib.ObjectId;
+import org.eclipse.jgit.lib.ObjectIdRef;
+import org.eclipse.jgit.lib.PersonIdent;
+import org.eclipse.jgit.lib.Ref;
+import org.eclipse.jgit.lib.RefComparator;
+import org.eclipse.jgit.lib.RefDatabase;
+import org.eclipse.jgit.lib.RefUpdate;
+import org.eclipse.jgit.lib.RefWriter;
+import org.eclipse.jgit.lib.Repository;
+import org.eclipse.jgit.lib.SymbolicRef;
import org.eclipse.jgit.revwalk.RevObject;
import org.eclipse.jgit.revwalk.RevTag;
import org.eclipse.jgit.revwalk.RevWalk;
@@ -109,7 +123,7 @@ public class RefDirectory extends RefDatabase {
/** If in the header, denotes the file has peeled data. */
public static final String PACKED_REFS_PEELED = " peeled"; //$NON-NLS-1$
- private final Repository parent;
+ private final FileRepository parent;
private final File gitDir;
@@ -150,7 +164,7 @@ public class RefDirectory extends RefDatabase {
*/
private final AtomicInteger lastNotifiedModCnt = new AtomicInteger();
- RefDirectory(final Repository db) {
+ RefDirectory(final FileRepository db) {
final FS fs = db.getFS();
parent = db;
gitDir = db.getDirectory();
@@ -405,20 +419,7 @@ public Ref peel(final Ref ref) throws IOException {
if (leaf.isPeeled() || leaf.getObjectId() == null)
return ref;
- RevWalk rw = new RevWalk(getRepository());
- RevObject obj = rw.parseAny(leaf.getObjectId());
- ObjectIdRef newLeaf;
- if (obj instanceof RevTag) {
- do {
- obj = rw.parseAny(((RevTag) obj).getObject());
- } while (obj instanceof RevTag);
-
- newLeaf = new ObjectIdRef.PeeledTag(leaf.getStorage(), leaf
- .getName(), leaf.getObjectId(), obj.copy());
- } else {
- newLeaf = new ObjectIdRef.PeeledNonTag(leaf.getStorage(), leaf
- .getName(), leaf.getObjectId());
- }
+ ObjectIdRef newLeaf = doPeel(leaf);
// Try to remember this peeling in the cache, so we don't have to do
// it again in the future, but only if the reference is unchanged.
@@ -435,6 +436,23 @@ public Ref peel(final Ref ref) throws IOException {
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);
@@ -494,7 +512,8 @@ void delete(RefDirectoryUpdate update) throws IOException {
// we don't miss an edit made externally.
final PackedRefList packed = getPackedRefs();
if (packed.contains(name)) {
- LockFile lck = new LockFile(packedRefsFile);
+ LockFile lck = new LockFile(packedRefsFile,
+ update.getRepository().getFS());
if (!lck.lock())
throw new IOException(MessageFormat.format(
JGitText.get().cannotLockFile, packedRefsFile));
@@ -590,7 +609,7 @@ else if (log.isFile())
}
private boolean isLogAllRefUpdates() {
- return parent.getConfig().getCore().isLogAllRefUpdates();
+ return parent.getConfig().get(CoreConfig.KEY).isLogAllRefUpdates();
}
private boolean shouldAutoCreateLog(final String refName) {
@@ -833,7 +852,7 @@ private void fireRefsChanged() {
final int last = lastNotifiedModCnt.get();
final int curr = modCnt.get();
if (last != curr && lastNotifiedModCnt.compareAndSet(last, curr))
- parent.fireRefsChanged();
+ parent.fireEvent(new RefsChangedEvent());
}
/**
diff --git a/org.eclipse.jgit/src/org/eclipse/jgit/lib/RefDirectoryRename.java b/org.eclipse.jgit/src/org/eclipse/jgit/storage/file/RefDirectoryRename.java
similarity index 96%
rename from org.eclipse.jgit/src/org/eclipse/jgit/lib/RefDirectoryRename.java
rename to org.eclipse.jgit/src/org/eclipse/jgit/storage/file/RefDirectoryRename.java
index fec00d9..4f3efe3 100644
--- a/org.eclipse.jgit/src/org/eclipse/jgit/lib/RefDirectoryRename.java
+++ b/org.eclipse.jgit/src/org/eclipse/jgit/storage/file/RefDirectoryRename.java
@@ -42,11 +42,15 @@
* ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
*/
-package org.eclipse.jgit.lib;
+package org.eclipse.jgit.storage.file;
import java.io.File;
import java.io.IOException;
+import org.eclipse.jgit.lib.Constants;
+import org.eclipse.jgit.lib.ObjectId;
+import org.eclipse.jgit.lib.RefRename;
+import org.eclipse.jgit.lib.RefUpdate;
import org.eclipse.jgit.lib.RefUpdate.Result;
import org.eclipse.jgit.revwalk.RevWalk;
@@ -88,10 +92,10 @@ 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();
+ final RevWalk rw = new RevWalk(refdb.getRepository());
try {
// First backup the source so its never unreachable.
tmp.setNewObjectId(objId);
@@ -173,6 +177,7 @@ protected Result doRename() throws IOException {
} catch (IOException err) {
refdb.fileFor(tmp.getName()).delete();
}
+ rw.release();
}
}
diff --git a/org.eclipse.jgit/src/org/eclipse/jgit/lib/RefDirectoryUpdate.java b/org.eclipse.jgit/src/org/eclipse/jgit/storage/file/RefDirectoryUpdate.java
similarity index 94%
rename from org.eclipse.jgit/src/org/eclipse/jgit/lib/RefDirectoryUpdate.java
rename to org.eclipse.jgit/src/org/eclipse/jgit/storage/file/RefDirectoryUpdate.java
index 447be10..a9f0548 100644
--- a/org.eclipse.jgit/src/org/eclipse/jgit/lib/RefDirectoryUpdate.java
+++ b/org.eclipse.jgit/src/org/eclipse/jgit/storage/file/RefDirectoryUpdate.java
@@ -42,12 +42,16 @@
* ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
*/
-package org.eclipse.jgit.lib;
+package org.eclipse.jgit.storage.file;
import static org.eclipse.jgit.lib.Constants.encode;
import java.io.IOException;
+import org.eclipse.jgit.lib.Ref;
+import org.eclipse.jgit.lib.RefUpdate;
+import org.eclipse.jgit.lib.Repository;
+
/** Updates any reference stored by {@link RefDirectory}. */
class RefDirectoryUpdate extends RefUpdate {
private final RefDirectory database;
@@ -75,7 +79,7 @@ protected boolean tryLock(boolean deref) throws IOException {
if (deref)
dst = dst.getLeaf();
String name = dst.getName();
- lock = new LockFile(database.fileFor(name));
+ lock = new LockFile(database.fileFor(name), getRepository().getFS());
if (lock.lock()) {
dst = database.getRef(name);
setOldObjectId(dst != null ? dst.getObjectId() : null);
diff --git a/org.eclipse.jgit/src/org/eclipse/jgit/lib/ReflogReader.java b/org.eclipse.jgit/src/org/eclipse/jgit/storage/file/ReflogReader.java
similarity index 96%
rename from org.eclipse.jgit/src/org/eclipse/jgit/lib/ReflogReader.java
rename to org.eclipse.jgit/src/org/eclipse/jgit/storage/file/ReflogReader.java
index 4c5503f..7521430 100644
--- a/org.eclipse.jgit/src/org/eclipse/jgit/lib/ReflogReader.java
+++ b/org.eclipse.jgit/src/org/eclipse/jgit/storage/file/ReflogReader.java
@@ -42,7 +42,7 @@
* ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
*/
-package org.eclipse.jgit.lib;
+package org.eclipse.jgit.storage.file;
import java.io.File;
import java.io.FileNotFoundException;
@@ -52,6 +52,10 @@
import java.util.List;
import org.eclipse.jgit.JGitText;
+import org.eclipse.jgit.lib.Constants;
+import org.eclipse.jgit.lib.ObjectId;
+import org.eclipse.jgit.lib.PersonIdent;
+import org.eclipse.jgit.lib.Repository;
import org.eclipse.jgit.util.IO;
import org.eclipse.jgit.util.RawParseUtils;
diff --git a/org.eclipse.jgit/src/org/eclipse/jgit/storage/file/UnpackedObject.java b/org.eclipse.jgit/src/org/eclipse/jgit/storage/file/UnpackedObject.java
new file mode 100644
index 0000000..78e7b10
--- /dev/null
+++ b/org.eclipse.jgit/src/org/eclipse/jgit/storage/file/UnpackedObject.java
@@ -0,0 +1,413 @@
+/*
+ * Copyright (C) 2007, Robin Rosenberg <robin.rosenberg@dewire.com>
+ * Copyright (C) 2006-2008, Shawn O. Pearce <spearce@spearce.org>
+ * Copyright (C) 2010, Google Inc.
+ * and other copyright owners as documented in the project's IP log.
+ *
+ * This program and the accompanying materials are made available
+ * under the terms of the Eclipse Distribution License v1.0 which
+ * accompanies this distribution, is reproduced below, and is
+ * available at http://www.eclipse.org/org/documents/edl-v10.php
+ *
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or
+ * without modification, are permitted provided that the following
+ * conditions are met:
+ *
+ * - Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ *
+ * - Redistributions in binary form must reproduce the above
+ * copyright notice, this list of conditions and the following
+ * disclaimer in the documentation and/or other materials provided
+ * with the distribution.
+ *
+ * - Neither the name of the Eclipse Foundation, Inc. nor the
+ * names of its contributors may be used to endorse or promote
+ * products derived from this software without specific prior
+ * written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND
+ * CONTRIBUTORS "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES,
+ * INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
+ * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR
+ * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
+ * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT
+ * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
+ * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER
+ * CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT,
+ * STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
+ * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF
+ * ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ */
+
+package org.eclipse.jgit.storage.file;
+
+import java.io.BufferedInputStream;
+import java.io.ByteArrayInputStream;
+import java.io.File;
+import java.io.FileInputStream;
+import java.io.FileNotFoundException;
+import java.io.IOException;
+import java.io.InputStream;
+import java.util.zip.DataFormatException;
+import java.util.zip.Inflater;
+import java.util.zip.InflaterInputStream;
+import java.util.zip.ZipException;
+
+import org.eclipse.jgit.JGitText;
+import org.eclipse.jgit.errors.CorruptObjectException;
+import org.eclipse.jgit.errors.LargeObjectException;
+import org.eclipse.jgit.errors.MissingObjectException;
+import org.eclipse.jgit.lib.AnyObjectId;
+import org.eclipse.jgit.lib.Constants;
+import org.eclipse.jgit.lib.InflaterCache;
+import org.eclipse.jgit.lib.ObjectId;
+import org.eclipse.jgit.lib.ObjectLoader;
+import org.eclipse.jgit.lib.ObjectStream;
+import org.eclipse.jgit.util.IO;
+import org.eclipse.jgit.util.MutableInteger;
+import org.eclipse.jgit.util.RawParseUtils;
+
+/**
+ * Loose object loader. This class loads an object not stored in a pack.
+ */
+public class UnpackedObject {
+ private static final int BUFFER_SIZE = 8192;
+
+ /**
+ * Parse an object from the unpacked object format.
+ *
+ * @param raw
+ * complete contents of the compressed object.
+ * @param id
+ * expected ObjectId of the object, used only for error reporting
+ * in exceptions.
+ * @return loader to read the inflated contents.
+ * @throws IOException
+ * the object cannot be parsed.
+ */
+ public static ObjectLoader parse(byte[] raw, AnyObjectId id)
+ throws IOException {
+ WindowCursor wc = new WindowCursor(null);
+ try {
+ return open(new ByteArrayInputStream(raw), null, id, wc);
+ } finally {
+ wc.release();
+ }
+ }
+
+ static ObjectLoader open(InputStream in, File path, AnyObjectId id,
+ WindowCursor wc) throws IOException {
+ try {
+ in = buffer(in);
+ in.mark(20);
+ final byte[] hdr = new byte[64];
+ IO.readFully(in, hdr, 0, 2);
+
+ if (isStandardFormat(hdr)) {
+ in.reset();
+ Inflater inf = wc.inflater();
+ InputStream zIn = inflate(in, inf);
+ int avail = readSome(zIn, hdr, 0, 64);
+ if (avail < 5)
+ throw new CorruptObjectException(id,
+ JGitText.get().corruptObjectNoHeader);
+
+ final MutableInteger p = new MutableInteger();
+ int type = Constants.decodeTypeString(id, hdr, (byte) ' ', p);
+ long size = RawParseUtils.parseLongBase10(hdr, p.value, p);
+ if (size < 0)
+ throw new CorruptObjectException(id,
+ JGitText.get().corruptObjectNegativeSize);
+ if (hdr[p.value++] != 0)
+ throw new CorruptObjectException(id,
+ JGitText.get().corruptObjectGarbageAfterSize);
+ if (path == null && Integer.MAX_VALUE < size)
+ throw new LargeObjectException(id.copy());
+ if (size < wc.getStreamFileThreshold() || path == null) {
+ byte[] data = new byte[(int) size];
+ int n = avail - p.value;
+ if (n > 0)
+ System.arraycopy(hdr, p.value, data, 0, n);
+ IO.readFully(zIn, data, n, data.length - n);
+ checkValidEndOfStream(in, inf, id, hdr);
+ return new ObjectLoader.SmallObject(type, data);
+ }
+ return new LargeObject(type, size, path, id, wc.db);
+
+ } else {
+ readSome(in, hdr, 2, 18);
+ int c = hdr[0] & 0xff;
+ int type = (c >> 4) & 7;
+ long size = c & 15;
+ int shift = 4;
+ int p = 1;
+ while ((c & 0x80) != 0) {
+ c = hdr[p++] & 0xff;
+ size += (c & 0x7f) << shift;
+ shift += 7;
+ }
+
+ switch (type) {
+ case Constants.OBJ_COMMIT:
+ case Constants.OBJ_TREE:
+ case Constants.OBJ_BLOB:
+ case Constants.OBJ_TAG:
+ // Acceptable types for a loose object.
+ break;
+ default:
+ throw new CorruptObjectException(id,
+ JGitText.get().corruptObjectInvalidType);
+ }
+
+ if (path == null && Integer.MAX_VALUE < size)
+ throw new LargeObjectException(id.copy());
+ if (size < wc.getStreamFileThreshold() || path == null) {
+ in.reset();
+ IO.skipFully(in, p);
+ Inflater inf = wc.inflater();
+ InputStream zIn = inflate(in, inf);
+ byte[] data = new byte[(int) size];
+ IO.readFully(zIn, data, 0, data.length);
+ checkValidEndOfStream(in, inf, id, hdr);
+ return new ObjectLoader.SmallObject(type, data);
+ }
+ return new LargeObject(type, size, path, id, wc.db);
+ }
+ } catch (ZipException badStream) {
+ throw new CorruptObjectException(id,
+ JGitText.get().corruptObjectBadStream);
+ }
+ }
+
+ static long getSize(InputStream in, AnyObjectId id, WindowCursor wc)
+ throws IOException {
+ try {
+ in = buffer(in);
+ in.mark(20);
+ final byte[] hdr = new byte[64];
+ IO.readFully(in, hdr, 0, 2);
+
+ if (isStandardFormat(hdr)) {
+ in.reset();
+ Inflater inf = wc.inflater();
+ InputStream zIn = inflate(in, inf);
+ int avail = readSome(zIn, hdr, 0, 64);
+ if (avail < 5)
+ throw new CorruptObjectException(id,
+ JGitText.get().corruptObjectNoHeader);
+
+ final MutableInteger p = new MutableInteger();
+ Constants.decodeTypeString(id, hdr, (byte) ' ', p);
+ long size = RawParseUtils.parseLongBase10(hdr, p.value, p);
+ if (size < 0)
+ throw new CorruptObjectException(id,
+ JGitText.get().corruptObjectNegativeSize);
+ return size;
+
+ } else {
+ readSome(in, hdr, 2, 18);
+ int c = hdr[0] & 0xff;
+ long size = c & 15;
+ int shift = 4;
+ int p = 1;
+ while ((c & 0x80) != 0) {
+ c = hdr[p++] & 0xff;
+ size += (c & 0x7f) << shift;
+ shift += 7;
+ }
+ return size;
+ }
+ } catch (ZipException badStream) {
+ throw new CorruptObjectException(id,
+ JGitText.get().corruptObjectBadStream);
+ }
+ }
+
+ private static void checkValidEndOfStream(InputStream in, Inflater inf,
+ AnyObjectId id, final byte[] buf) throws IOException,
+ CorruptObjectException {
+ for (;;) {
+ int r;
+ try {
+ r = inf.inflate(buf);
+ } catch (DataFormatException e) {
+ throw new CorruptObjectException(id,
+ JGitText.get().corruptObjectBadStream);
+ }
+ if (r != 0)
+ throw new CorruptObjectException(id,
+ JGitText.get().corruptObjectIncorrectLength);
+
+ if (inf.finished()) {
+ if (inf.getRemaining() != 0 || in.read() != -1)
+ throw new CorruptObjectException(id,
+ JGitText.get().corruptObjectBadStream);
+ break;
+ }
+
+ if (!inf.needsInput())
+ throw new CorruptObjectException(id,
+ JGitText.get().corruptObjectBadStream);
+
+ r = in.read(buf);
+ if (r <= 0)
+ throw new CorruptObjectException(id,
+ JGitText.get().corruptObjectBadStream);
+ inf.setInput(buf, 0, r);
+ }
+ }
+
+ private static boolean isStandardFormat(final byte[] hdr) {
+ // Try to determine if this is a standard format loose object or
+ // a pack style loose object. The standard format is completely
+ // compressed with zlib so the first byte must be 0x78 (15-bit
+ // window size, deflated) and the first 16 bit word must be
+ // evenly divisible by 31. Otherwise its a pack style object.
+ //
+ final int fb = hdr[0] & 0xff;
+ return fb == 0x78 && (((fb << 8) | hdr[1] & 0xff) % 31) == 0;
+ }
+
+ private static InputStream inflate(final InputStream in, final long size,
+ final ObjectId id) {
+ final Inflater inf = InflaterCache.get();
+ return new InflaterInputStream(in, inf) {
+ private long remaining = size;
+
+ @Override
+ public int read(byte[] b, int off, int cnt) throws IOException {
+ try {
+ int r = super.read(b, off, cnt);
+ if (r > 0)
+ remaining -= r;
+ return r;
+ } catch (ZipException badStream) {
+ throw new CorruptObjectException(id,
+ JGitText.get().corruptObjectBadStream);
+ }
+ }
+
+ @Override
+ public void close() throws IOException {
+ try {
+ if (remaining <= 0)
+ checkValidEndOfStream(in, inf, id, new byte[64]);
+ } finally {
+ InflaterCache.release(inf);
+ super.close();
+ }
+ }
+ };
+ }
+
+ private static InflaterInputStream inflate(InputStream in, Inflater inf) {
+ return new InflaterInputStream(in, inf, BUFFER_SIZE);
+ }
+
+ private static BufferedInputStream buffer(InputStream in) {
+ return new BufferedInputStream(in, BUFFER_SIZE);
+ }
+
+ private static int readSome(InputStream in, final byte[] hdr, int off,
+ int cnt) throws IOException {
+ int avail = 0;
+ while (0 < cnt) {
+ int n = in.read(hdr, off, cnt);
+ if (n < 0)
+ break;
+ avail += n;
+ cnt -= n;
+ }
+ return avail;
+ }
+
+ private static final class LargeObject extends ObjectLoader {
+ private final int type;
+
+ private final long size;
+
+ private final File path;
+
+ private final ObjectId id;
+
+ private final FileObjectDatabase source;
+
+ private LargeObject(int type, long size, File path, AnyObjectId id,
+ FileObjectDatabase db) {
+ this.type = type;
+ this.size = size;
+ this.path = path;
+ this.id = id.copy();
+ this.source = db;
+ }
+
+ @Override
+ public int getType() {
+ return type;
+ }
+
+ @Override
+ public long getSize() {
+ return size;
+ }
+
+ @Override
+ public boolean isLarge() {
+ return true;
+ }
+
+ @Override
+ public byte[] getCachedBytes() throws LargeObjectException {
+ throw new LargeObjectException(id);
+ }
+
+ @Override
+ public ObjectStream openStream() throws MissingObjectException,
+ IOException {
+ InputStream in;
+ try {
+ in = buffer(new FileInputStream(path));
+ } catch (FileNotFoundException gone) {
+ // If the loose file no longer exists, it may have been
+ // moved into a pack file in the mean time. Try again
+ // to locate the object.
+ //
+ return source.open(id, type).openStream();
+ }
+
+ boolean ok = false;
+ try {
+ final byte[] hdr = new byte[64];
+ in.mark(20);
+ IO.readFully(in, hdr, 0, 2);
+
+ if (isStandardFormat(hdr)) {
+ in.reset();
+ in = buffer(inflate(in, size, id));
+ while (0 < in.read())
+ continue;
+ } else {
+ readSome(in, hdr, 2, 18);
+ int c = hdr[0] & 0xff;
+ int p = 1;
+ while ((c & 0x80) != 0)
+ c = hdr[p++] & 0xff;
+
+ in.reset();
+ IO.skipFully(in, p);
+ in = buffer(inflate(in, size, id));
+ }
+
+ ok = true;
+ return new ObjectStream.Filter(type, size, in);
+ } finally {
+ if (!ok)
+ in.close();
+ }
+ }
+ }
+}
diff --git a/org.eclipse.jgit/src/org/eclipse/jgit/lib/UnpackedObjectCache.java b/org.eclipse.jgit/src/org/eclipse/jgit/storage/file/UnpackedObjectCache.java
similarity index 98%
rename from org.eclipse.jgit/src/org/eclipse/jgit/lib/UnpackedObjectCache.java
rename to org.eclipse.jgit/src/org/eclipse/jgit/storage/file/UnpackedObjectCache.java
index 3cef482..92f4824 100644
--- a/org.eclipse.jgit/src/org/eclipse/jgit/lib/UnpackedObjectCache.java
+++ b/org.eclipse.jgit/src/org/eclipse/jgit/storage/file/UnpackedObjectCache.java
@@ -41,7 +41,7 @@
* ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
*/
-package org.eclipse.jgit.lib;
+package org.eclipse.jgit.storage.file;
import java.lang.ref.SoftReference;
diff --git a/org.eclipse.jgit/src/org/eclipse/jgit/lib/WindowCache.java b/org.eclipse.jgit/src/org/eclipse/jgit/storage/file/WindowCache.java
similarity index 99%
rename from org.eclipse.jgit/src/org/eclipse/jgit/lib/WindowCache.java
rename to org.eclipse.jgit/src/org/eclipse/jgit/storage/file/WindowCache.java
index a44a30e..39633ee 100644
--- a/org.eclipse.jgit/src/org/eclipse/jgit/lib/WindowCache.java
+++ b/org.eclipse.jgit/src/org/eclipse/jgit/storage/file/WindowCache.java
@@ -42,7 +42,7 @@
* ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
*/
-package org.eclipse.jgit.lib;
+package org.eclipse.jgit.storage.file;
import java.io.IOException;
import java.lang.ref.ReferenceQueue;
diff --git a/org.eclipse.jgit/src/org/eclipse/jgit/lib/WindowCacheConfig.java b/org.eclipse.jgit/src/org/eclipse/jgit/storage/file/WindowCacheConfig.java
similarity index 98%
rename from org.eclipse.jgit/src/org/eclipse/jgit/lib/WindowCacheConfig.java
rename to org.eclipse.jgit/src/org/eclipse/jgit/storage/file/WindowCacheConfig.java
index 2d8aef3..48d7018 100644
--- a/org.eclipse.jgit/src/org/eclipse/jgit/lib/WindowCacheConfig.java
+++ b/org.eclipse.jgit/src/org/eclipse/jgit/storage/file/WindowCacheConfig.java
@@ -41,7 +41,9 @@
* ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
*/
-package org.eclipse.jgit.lib;
+package org.eclipse.jgit.storage.file;
+
+import org.eclipse.jgit.lib.Config;
/** Configuration parameters for {@link WindowCache}. */
public class WindowCacheConfig {
diff --git a/org.eclipse.jgit/src/org/eclipse/jgit/lib/WindowCursor.java b/org.eclipse.jgit/src/org/eclipse/jgit/storage/file/WindowCursor.java
similarity index 60%
rename from org.eclipse.jgit/src/org/eclipse/jgit/lib/WindowCursor.java
rename to org.eclipse.jgit/src/org/eclipse/jgit/storage/file/WindowCursor.java
index 968c92e..5376d07 100644
--- a/org.eclipse.jgit/src/org/eclipse/jgit/lib/WindowCursor.java
+++ b/org.eclipse.jgit/src/org/eclipse/jgit/storage/file/WindowCursor.java
@@ -42,14 +42,28 @@
* ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
*/
-package org.eclipse.jgit.lib;
+package org.eclipse.jgit.storage.file;
import java.io.IOException;
import java.util.zip.DataFormatException;
import java.util.zip.Inflater;
+import org.eclipse.jgit.errors.IncorrectObjectTypeException;
+import org.eclipse.jgit.errors.MissingObjectException;
+import org.eclipse.jgit.errors.StoredObjectRepresentationNotAvailableException;
+import org.eclipse.jgit.lib.AnyObjectId;
+import org.eclipse.jgit.lib.Constants;
+import org.eclipse.jgit.lib.InflaterCache;
+import org.eclipse.jgit.lib.ObjectLoader;
+import org.eclipse.jgit.lib.ObjectReader;
+import org.eclipse.jgit.revwalk.RevObject;
+import org.eclipse.jgit.storage.pack.ObjectReuseAsIs;
+import org.eclipse.jgit.storage.pack.ObjectToPack;
+import org.eclipse.jgit.storage.pack.PackOutputStream;
+import org.eclipse.jgit.storage.pack.PackWriter;
+
/** Active handle to a ByteWindow. */
-public final class WindowCursor {
+final class WindowCursor extends ObjectReader implements ObjectReuseAsIs {
/** Temporary buffer large enough for at least one raw object id. */
final byte[] tempId = new byte[Constants.OBJECT_ID_LENGTH];
@@ -57,6 +71,62 @@ public final class WindowCursor {
private ByteWindow window;
+ final FileObjectDatabase db;
+
+ WindowCursor(FileObjectDatabase db) {
+ this.db = db;
+ }
+
+ @Override
+ public ObjectReader newReader() {
+ return new WindowCursor(db);
+ }
+
+ public boolean has(AnyObjectId objectId) throws IOException {
+ return db.has(objectId);
+ }
+
+ public ObjectLoader open(AnyObjectId objectId, int typeHint)
+ throws MissingObjectException, IncorrectObjectTypeException,
+ IOException {
+ final ObjectLoader ldr = db.openObject(this, objectId);
+ if (ldr == null) {
+ if (typeHint == OBJ_ANY)
+ throw new MissingObjectException(objectId.copy(), "unknown");
+ throw new MissingObjectException(objectId.copy(), typeHint);
+ }
+ if (typeHint != OBJ_ANY && ldr.getType() != typeHint)
+ throw new IncorrectObjectTypeException(objectId.copy(), typeHint);
+ return ldr;
+ }
+
+ public long getObjectSize(AnyObjectId objectId, int typeHint)
+ throws MissingObjectException, IncorrectObjectTypeException,
+ IOException {
+ long sz = db.getObjectSize(this, objectId);
+ if (sz < 0) {
+ if (typeHint == OBJ_ANY)
+ throw new MissingObjectException(objectId.copy(), "unknown");
+ throw new MissingObjectException(objectId.copy(), typeHint);
+ }
+ return sz;
+ }
+
+ public LocalObjectToPack newObjectToPack(RevObject obj) {
+ return new LocalObjectToPack(obj);
+ }
+
+ public void selectObjectRepresentation(PackWriter packer, ObjectToPack otp)
+ throws IOException, MissingObjectException {
+ db.selectObjectRepresentation(packer, otp, this);
+ }
+
+ public void copyObjectAsIs(PackOutputStream out, ObjectToPack otp)
+ throws IOException, StoredObjectRepresentationNotAvailableException {
+ LocalObjectToPack src = (LocalObjectToPack) otp;
+ src.pack.copyAsIs(out, src, this);
+ }
+
/**
* Copy bytes from the window to a caller supplied buffer.
*
@@ -73,8 +143,8 @@ public final class WindowCursor {
* bytes remaining in the window starting at offset
* <code>pos</code>.
* @return number of bytes actually copied; this may be less than
- * <code>cnt</code> if <code>cnt</code> exceeded the number of
- * bytes available.
+ * <code>cnt</code> if <code>cnt</code> exceeded the number of bytes
+ * available.
* @throws IOException
* this cursor does not match the provider or id and the proper
* window could not be acquired through the provider's cache.
@@ -94,7 +164,7 @@ int copy(final PackFile pack, long position, final byte[] dstbuf,
}
/**
- * Pump bytes into the supplied inflater as input.
+ * Inflate a region of the pack starting at {@code position}.
*
* @param pack
* the file the desired window is stored within.
@@ -117,25 +187,36 @@ int copy(final PackFile pack, long position, final byte[] dstbuf,
int inflate(final PackFile pack, long position, final byte[] dstbuf,
int dstoff) throws IOException, DataFormatException {
prepareInflater();
- for (;;) {
- pin(pack, position);
- dstoff = window.inflate(position, dstbuf, dstoff, inf);
- if (inf.finished())
- return dstoff;
- position = window.end;
- }
+ pin(pack, position);
+ position += window.setInput(position, inf);
+ do {
+ int n = inf.inflate(dstbuf, dstoff, dstbuf.length - dstoff);
+ if (n == 0) {
+ if (inf.needsInput()) {
+ pin(pack, position);
+ position += window.setInput(position, inf);
+ } else if (inf.finished())
+ return dstoff;
+ else
+ throw new DataFormatException();
+ }
+ dstoff += n;
+ } while (dstoff < dstbuf.length);
+ return dstoff;
}
- void inflateVerify(final PackFile pack, long position)
- throws IOException, DataFormatException {
+ ByteArrayWindow quickCopy(PackFile p, long pos, long cnt)
+ throws IOException {
+ pin(p, pos);
+ if (window instanceof ByteArrayWindow
+ && window.contains(p, pos + (cnt - 1)))
+ return (ByteArrayWindow) window;
+ return null;
+ }
+
+ Inflater inflater() {
prepareInflater();
- for (;;) {
- pin(pack, position);
- window.inflateVerify(position, inf);
- if (inf.finished())
- return;
- position = window.end;
- }
+ return inf;
}
private void prepareInflater() {
@@ -145,7 +226,7 @@ private void prepareInflater() {
inf.reset();
}
- private void pin(final PackFile pack, final long position)
+ void pin(final PackFile pack, final long position)
throws IOException {
final ByteWindow w = window;
if (w == null || !w.contains(pack, position)) {
@@ -159,6 +240,12 @@ private void pin(final PackFile pack, final long position)
}
}
+ int getStreamFileThreshold() {
+ if (db == null)
+ return ObjectLoader.STREAM_THRESHOLD;
+ return db.getStreamFileThreshold();
+ }
+
/** Release the current window cursor. */
public void release() {
window = null;
@@ -168,14 +255,4 @@ public void release() {
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/storage/pack/BinaryDelta.java b/org.eclipse.jgit/src/org/eclipse/jgit/storage/pack/BinaryDelta.java
new file mode 100644
index 0000000..1d433d7
--- /dev/null
+++ b/org.eclipse.jgit/src/org/eclipse/jgit/storage/pack/BinaryDelta.java
@@ -0,0 +1,286 @@
+/*
+ * Copyright (C) 2007, Robin Rosenberg <robin.rosenberg@dewire.com>
+ * Copyright (C) 2006-2007, Shawn O. Pearce <spearce@spearce.org>
+ * and other copyright owners as documented in the project's IP log.
+ *
+ * This program and the accompanying materials are made available
+ * under the terms of the Eclipse Distribution License v1.0 which
+ * accompanies this distribution, is reproduced below, and is
+ * available at http://www.eclipse.org/org/documents/edl-v10.php
+ *
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or
+ * without modification, are permitted provided that the following
+ * conditions are met:
+ *
+ * - Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ *
+ * - Redistributions in binary form must reproduce the above
+ * copyright notice, this list of conditions and the following
+ * disclaimer in the documentation and/or other materials provided
+ * with the distribution.
+ *
+ * - Neither the name of the Eclipse Foundation, Inc. nor the
+ * names of its contributors may be used to endorse or promote
+ * products derived from this software without specific prior
+ * written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND
+ * CONTRIBUTORS "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES,
+ * INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
+ * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR
+ * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
+ * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT
+ * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
+ * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER
+ * CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT,
+ * STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
+ * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF
+ * ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ */
+
+package org.eclipse.jgit.storage.pack;
+
+import org.eclipse.jgit.JGitText;
+import org.eclipse.jgit.util.QuotedString;
+import org.eclipse.jgit.util.RawParseUtils;
+
+/**
+ * Recreate a stream from a base stream and a GIT pack delta.
+ * <p>
+ * This entire class is heavily cribbed from <code>patch-delta.c</code> in the
+ * GIT project. The original delta patching code was written by Nicolas Pitre
+ * (<nico@cam.org>).
+ * </p>
+ */
+public class BinaryDelta {
+ /**
+ * Length of the base object in the delta stream.
+ *
+ * @param delta
+ * the delta stream, or at least the header of it.
+ * @return the base object's size.
+ */
+ public static long getBaseSize(final byte[] delta) {
+ int p = 0;
+ long baseLen = 0;
+ int c, shift = 0;
+ do {
+ c = delta[p++] & 0xff;
+ baseLen |= (c & 0x7f) << shift;
+ shift += 7;
+ } while ((c & 0x80) != 0);
+ return baseLen;
+ }
+
+ /**
+ * Length of the resulting object in the delta stream.
+ *
+ * @param delta
+ * the delta stream, or at least the header of it.
+ * @return the resulting object's size.
+ */
+ public static long getResultSize(final byte[] delta) {
+ int p = 0;
+
+ // Skip length of the base object.
+ //
+ int c;
+ do {
+ c = delta[p++] & 0xff;
+ } while ((c & 0x80) != 0);
+
+ long resLen = 0;
+ int shift = 0;
+ do {
+ c = delta[p++] & 0xff;
+ resLen |= (c & 0x7f) << shift;
+ shift += 7;
+ } while ((c & 0x80) != 0);
+ return resLen;
+ }
+
+ /**
+ * Apply the changes defined by delta to the data in base, yielding a new
+ * array of bytes.
+ *
+ * @param base
+ * some byte representing an object of some kind.
+ * @param delta
+ * a git pack delta defining the transform from one version to
+ * another.
+ * @return patched base
+ */
+ public static final byte[] apply(final byte[] base, final byte[] delta) {
+ int deltaPtr = 0;
+
+ // Length of the base object (a variable length int).
+ //
+ int baseLen = 0;
+ int c, shift = 0;
+ do {
+ c = delta[deltaPtr++] & 0xff;
+ baseLen |= (c & 0x7f) << shift;
+ shift += 7;
+ } while ((c & 0x80) != 0);
+ if (base.length != baseLen)
+ throw new IllegalArgumentException(
+ JGitText.get().baseLengthIncorrect);
+
+ // Length of the resulting object (a variable length int).
+ //
+ int resLen = 0;
+ shift = 0;
+ do {
+ c = delta[deltaPtr++] & 0xff;
+ resLen |= (c & 0x7f) << shift;
+ shift += 7;
+ } while ((c & 0x80) != 0);
+
+ final byte[] result = new byte[resLen];
+ int resultPtr = 0;
+ while (deltaPtr < delta.length) {
+ final int cmd = delta[deltaPtr++] & 0xff;
+ if ((cmd & 0x80) != 0) {
+ // Determine the segment of the base which should
+ // be copied into the output. The segment is given
+ // as an offset and a length.
+ //
+ int copyOffset = 0;
+ if ((cmd & 0x01) != 0)
+ copyOffset = delta[deltaPtr++] & 0xff;
+ if ((cmd & 0x02) != 0)
+ copyOffset |= (delta[deltaPtr++] & 0xff) << 8;
+ if ((cmd & 0x04) != 0)
+ copyOffset |= (delta[deltaPtr++] & 0xff) << 16;
+ if ((cmd & 0x08) != 0)
+ copyOffset |= (delta[deltaPtr++] & 0xff) << 24;
+
+ int copySize = 0;
+ if ((cmd & 0x10) != 0)
+ copySize = delta[deltaPtr++] & 0xff;
+ if ((cmd & 0x20) != 0)
+ copySize |= (delta[deltaPtr++] & 0xff) << 8;
+ if ((cmd & 0x40) != 0)
+ copySize |= (delta[deltaPtr++] & 0xff) << 16;
+ if (copySize == 0)
+ copySize = 0x10000;
+
+ System.arraycopy(base, copyOffset, result, resultPtr, copySize);
+ resultPtr += copySize;
+ } else if (cmd != 0) {
+ // Anything else the data is literal within the delta
+ // itself.
+ //
+ System.arraycopy(delta, deltaPtr, result, resultPtr, cmd);
+ deltaPtr += cmd;
+ resultPtr += cmd;
+ } else {
+ // cmd == 0 has been reserved for future encoding but
+ // for now its not acceptable.
+ //
+ throw new IllegalArgumentException(
+ JGitText.get().unsupportedCommand0);
+ }
+ }
+
+ return result;
+ }
+
+ /**
+ * Format this delta as a human readable string.
+ *
+ * @param delta
+ * the delta instruction sequence to format.
+ * @return the formatted delta.
+ */
+ public static String format(byte[] delta) {
+ return format(delta, true);
+ }
+
+ /**
+ * Format this delta as a human readable string.
+ *
+ * @param delta
+ * the delta instruction sequence to format.
+ * @param includeHeader
+ * true if the header (base size and result size) should be
+ * included in the formatting.
+ * @return the formatted delta.
+ */
+ public static String format(byte[] delta, boolean includeHeader) {
+ StringBuilder r = new StringBuilder();
+ int deltaPtr = 0;
+
+ long baseLen = 0;
+ int c, shift = 0;
+ do {
+ c = delta[deltaPtr++] & 0xff;
+ baseLen |= (c & 0x7f) << shift;
+ shift += 7;
+ } while ((c & 0x80) != 0);
+
+ long resLen = 0;
+ shift = 0;
+ do {
+ c = delta[deltaPtr++] & 0xff;
+ resLen |= (c & 0x7f) << shift;
+ shift += 7;
+ } while ((c & 0x80) != 0);
+
+ if (includeHeader)
+ r.append("DELTA( BASE=" + baseLen + " RESULT=" + resLen + " )\n");
+
+ while (deltaPtr < delta.length) {
+ final int cmd = delta[deltaPtr++] & 0xff;
+ if ((cmd & 0x80) != 0) {
+ // Determine the segment of the base which should
+ // be copied into the output. The segment is given
+ // as an offset and a length.
+ //
+ int copyOffset = 0;
+ if ((cmd & 0x01) != 0)
+ copyOffset = delta[deltaPtr++] & 0xff;
+ if ((cmd & 0x02) != 0)
+ copyOffset |= (delta[deltaPtr++] & 0xff) << 8;
+ if ((cmd & 0x04) != 0)
+ copyOffset |= (delta[deltaPtr++] & 0xff) << 16;
+ if ((cmd & 0x08) != 0)
+ copyOffset |= (delta[deltaPtr++] & 0xff) << 24;
+
+ int copySize = 0;
+ if ((cmd & 0x10) != 0)
+ copySize = delta[deltaPtr++] & 0xff;
+ if ((cmd & 0x20) != 0)
+ copySize |= (delta[deltaPtr++] & 0xff) << 8;
+ if ((cmd & 0x40) != 0)
+ copySize |= (delta[deltaPtr++] & 0xff) << 16;
+ if (copySize == 0)
+ copySize = 0x10000;
+
+ r.append(" COPY (" + copyOffset + ", " + copySize + ")\n");
+
+ } else if (cmd != 0) {
+ // Anything else the data is literal within the delta
+ // itself.
+ //
+ r.append(" INSERT(");
+ r.append(QuotedString.GIT_PATH.quote(RawParseUtils.decode(
+ delta, deltaPtr, deltaPtr + cmd)));
+ r.append(")\n");
+ deltaPtr += cmd;
+ } else {
+ // cmd == 0 has been reserved for future encoding but
+ // for now its not acceptable.
+ //
+ throw new IllegalArgumentException(
+ JGitText.get().unsupportedCommand0);
+ }
+ }
+
+ return r.toString();
+ }
+}
diff --git a/org.eclipse.jgit/src/org/eclipse/jgit/storage/pack/DeltaCache.java b/org.eclipse.jgit/src/org/eclipse/jgit/storage/pack/DeltaCache.java
new file mode 100644
index 0000000..93eab19
--- /dev/null
+++ b/org.eclipse.jgit/src/org/eclipse/jgit/storage/pack/DeltaCache.java
@@ -0,0 +1,134 @@
+/*
+ * Copyright (C) 2010, Google Inc.
+ * and other copyright owners as documented in the project's IP log.
+ *
+ * This program and the accompanying materials are made available
+ * under the terms of the Eclipse Distribution License v1.0 which
+ * accompanies this distribution, is reproduced below, and is
+ * available at http://www.eclipse.org/org/documents/edl-v10.php
+ *
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or
+ * without modification, are permitted provided that the following
+ * conditions are met:
+ *
+ * - Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ *
+ * - Redistributions in binary form must reproduce the above
+ * copyright notice, this list of conditions and the following
+ * disclaimer in the documentation and/or other materials provided
+ * with the distribution.
+ *
+ * - Neither the name of the Eclipse Foundation, Inc. nor the
+ * names of its contributors may be used to endorse or promote
+ * products derived from this software without specific prior
+ * written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND
+ * CONTRIBUTORS "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES,
+ * INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
+ * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR
+ * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
+ * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT
+ * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
+ * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER
+ * CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT,
+ * STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
+ * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF
+ * ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ */
+
+package org.eclipse.jgit.storage.pack;
+
+import java.lang.ref.ReferenceQueue;
+import java.lang.ref.SoftReference;
+
+class DeltaCache {
+ private final long size;
+
+ private final int entryLimit;
+
+ private final ReferenceQueue<byte[]> queue;
+
+ private long used;
+
+ DeltaCache(PackConfig pc) {
+ size = pc.getDeltaCacheSize();
+ entryLimit = pc.getDeltaCacheLimit();
+ queue = new ReferenceQueue<byte[]>();
+ }
+
+ boolean canCache(int length, ObjectToPack src, ObjectToPack res) {
+ // If the cache would overflow, don't store.
+ //
+ if (0 < size && size < used + length) {
+ checkForGarbageCollectedObjects();
+ if (0 < size && size < used + length)
+ return false;
+ }
+
+ if (length < entryLimit) {
+ used += length;
+ return true;
+ }
+
+ // If the combined source files are multiple megabytes but the delta
+ // is on the order of a kilobyte or two, this was likely costly to
+ // construct. Cache it anyway, even though its over the limit.
+ //
+ if (length >> 10 < (src.getWeight() >> 20) + (res.getWeight() >> 21)) {
+ used += length;
+ return true;
+ }
+
+ return false;
+ }
+
+ void credit(int reservedSize) {
+ used -= reservedSize;
+ }
+
+ Ref cache(byte[] data, int actLen, int reservedSize) {
+ // The caller may have had to allocate more space than is
+ // required. If we are about to waste anything, shrink it.
+ //
+ data = resize(data, actLen);
+
+ // When we reserved space for this item we did it for the
+ // inflated size of the delta, but we were just given the
+ // compressed version. Adjust the cache cost to match.
+ //
+ if (reservedSize != data.length) {
+ used -= reservedSize;
+ used += data.length;
+ }
+ return new Ref(data, queue);
+ }
+
+ byte[] resize(byte[] data, int actLen) {
+ if (data.length != actLen) {
+ byte[] nbuf = new byte[actLen];
+ System.arraycopy(data, 0, nbuf, 0, actLen);
+ data = nbuf;
+ }
+ return data;
+ }
+
+ private void checkForGarbageCollectedObjects() {
+ Ref r;
+ while ((r = (Ref) queue.poll()) != null)
+ used -= r.cost;
+ }
+
+ static class Ref extends SoftReference<byte[]> {
+ final int cost;
+
+ Ref(byte[] array, ReferenceQueue<byte[]> queue) {
+ super(array, queue);
+ cost = array.length;
+ }
+ }
+}
diff --git a/org.eclipse.jgit/src/org/eclipse/jgit/storage/pack/DeltaEncoder.java b/org.eclipse.jgit/src/org/eclipse/jgit/storage/pack/DeltaEncoder.java
new file mode 100644
index 0000000..204030b
--- /dev/null
+++ b/org.eclipse.jgit/src/org/eclipse/jgit/storage/pack/DeltaEncoder.java
@@ -0,0 +1,294 @@
+/*
+ * Copyright (C) 2010, Google Inc.
+ * and other copyright owners as documented in the project's IP log.
+ *
+ * This program and the accompanying materials are made available
+ * under the terms of the Eclipse Distribution License v1.0 which
+ * accompanies this distribution, is reproduced below, and is
+ * available at http://www.eclipse.org/org/documents/edl-v10.php
+ *
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or
+ * without modification, are permitted provided that the following
+ * conditions are met:
+ *
+ * - Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ *
+ * - Redistributions in binary form must reproduce the above
+ * copyright notice, this list of conditions and the following
+ * disclaimer in the documentation and/or other materials provided
+ * with the distribution.
+ *
+ * - Neither the name of the Eclipse Foundation, Inc. nor the
+ * names of its contributors may be used to endorse or promote
+ * products derived from this software without specific prior
+ * written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND
+ * CONTRIBUTORS "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES,
+ * INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
+ * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR
+ * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
+ * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT
+ * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
+ * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER
+ * CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT,
+ * STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
+ * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF
+ * ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ */
+
+package org.eclipse.jgit.storage.pack;
+
+import java.io.IOException;
+import java.io.OutputStream;
+
+import org.eclipse.jgit.lib.Constants;
+
+/** Encodes an instruction stream for {@link BinaryDelta}. */
+public class DeltaEncoder {
+ /**
+ * Maximum number of bytes to be copied in pack v2 format.
+ * <p>
+ * Historical limitations have this at 64k, even though current delta
+ * decoders recognize larger copy instructions.
+ */
+ private static final int MAX_V2_COPY = 0x10000;
+
+ /*
+ * Maximum number of bytes to be copied in pack v3 format.
+ *
+ * Current delta decoders can recognize a copy instruction with a count that
+ * is this large, but the historical limitation of {@link MAX_V2_COPY} is
+ * still used.
+ */
+ // private static final int MAX_V3_COPY = (0xff << 16) | (0xff << 8) | 0xff;
+
+ /** Maximum number of bytes used by a copy instruction. */
+ private static final int MAX_COPY_CMD_SIZE = 8;
+
+ /** Maximum length that an an insert command can encode at once. */
+ private static final int MAX_INSERT_DATA_SIZE = 127;
+
+ private final OutputStream out;
+
+ private final byte[] buf = new byte[MAX_COPY_CMD_SIZE * 4];
+
+ private final int limit;
+
+ private int size;
+
+ /**
+ * Create an encoder with no upper bound on the instruction stream size.
+ *
+ * @param out
+ * buffer to store the instructions written.
+ * @param baseSize
+ * size of the base object, in bytes.
+ * @param resultSize
+ * size of the resulting object, after applying this instruction
+ * stream to the base object, in bytes.
+ * @throws IOException
+ * the output buffer cannot store the instruction stream's
+ * header with the size fields.
+ */
+ public DeltaEncoder(OutputStream out, long baseSize, long resultSize)
+ throws IOException {
+ this(out, baseSize, resultSize, 0);
+ }
+
+ /**
+ * Create an encoder with an upper limit on the instruction size.
+ *
+ * @param out
+ * buffer to store the instructions written.
+ * @param baseSize
+ * size of the base object, in bytes.
+ * @param resultSize
+ * size of the resulting object, after applying this instruction
+ * stream to the base object, in bytes.
+ * @param limit
+ * maximum number of bytes to write to the out buffer declaring
+ * the stream is over limit and should be discarded. May be 0 to
+ * specify an infinite limit.
+ * @throws IOException
+ * the output buffer cannot store the instruction stream's
+ * header with the size fields.
+ */
+ public DeltaEncoder(OutputStream out, long baseSize, long resultSize,
+ int limit) throws IOException {
+ this.out = out;
+ this.limit = limit;
+ writeVarint(baseSize);
+ writeVarint(resultSize);
+ }
+
+ private void writeVarint(long sz) throws IOException {
+ int p = 0;
+ while (sz >= 0x80) {
+ buf[p++] = (byte) (0x80 | (((int) sz) & 0x7f));
+ sz >>>= 7;
+ }
+ buf[p++] = (byte) (((int) sz) & 0x7f);
+ size += p;
+ if (limit <= 0 || size < limit)
+ out.write(buf, 0, p);
+ }
+
+ /** @return current size of the delta stream, in bytes. */
+ public int getSize() {
+ return size;
+ }
+
+ /**
+ * Insert a literal string of text, in UTF-8 encoding.
+ *
+ * @param text
+ * the string to insert.
+ * @return true if the insert fits within the limit; false if the insert
+ * would cause the instruction stream to exceed the limit.
+ * @throws IOException
+ * the instruction buffer can't store the instructions.
+ */
+ public boolean insert(String text) throws IOException {
+ return insert(Constants.encode(text));
+ }
+
+ /**
+ * Insert a literal binary sequence.
+ *
+ * @param text
+ * the binary to insert.
+ * @return true if the insert fits within the limit; false if the insert
+ * would cause the instruction stream to exceed the limit.
+ * @throws IOException
+ * the instruction buffer can't store the instructions.
+ */
+ public boolean insert(byte[] text) throws IOException {
+ return insert(text, 0, text.length);
+ }
+
+ /**
+ * Insert a literal binary sequence.
+ *
+ * @param text
+ * the binary to insert.
+ * @param off
+ * offset within {@code text} to start copying from.
+ * @param cnt
+ * number of bytes to insert.
+ * @return true if the insert fits within the limit; false if the insert
+ * would cause the instruction stream to exceed the limit.
+ * @throws IOException
+ * the instruction buffer can't store the instructions.
+ */
+ public boolean insert(byte[] text, int off, int cnt)
+ throws IOException {
+ if (cnt <= 0)
+ return true;
+ if (0 < limit) {
+ int hdrs = cnt / MAX_INSERT_DATA_SIZE;
+ if (cnt % MAX_INSERT_DATA_SIZE != 0)
+ hdrs++;
+ if (limit < size + hdrs + cnt)
+ return false;
+ }
+ do {
+ int n = Math.min(MAX_INSERT_DATA_SIZE, cnt);
+ out.write((byte) n);
+ out.write(text, off, n);
+ off += n;
+ cnt -= n;
+ size += 1 + n;
+ } while (0 < cnt);
+ return true;
+ }
+
+ /**
+ * Create a copy instruction to copy from the base object.
+ *
+ * @param offset
+ * position in the base object to copy from. This is absolute,
+ * from the beginning of the base.
+ * @param cnt
+ * number of bytes to copy.
+ * @return true if the copy fits within the limit; false if the copy
+ * would cause the instruction stream to exceed the limit.
+ * @throws IOException
+ * the instruction buffer cannot store the instructions.
+ */
+ public boolean copy(long offset, int cnt) throws IOException {
+ if (cnt == 0)
+ return true;
+
+ int p = 0;
+
+ // We cannot encode more than MAX_V2_COPY bytes in a single
+ // command, so encode that much and start a new command.
+ // This limit is imposed by the pack file format rules.
+ //
+ while (MAX_V2_COPY < cnt) {
+ p = encodeCopy(p, offset, MAX_V2_COPY);
+ offset += MAX_V2_COPY;
+ cnt -= MAX_V2_COPY;
+
+ if (buf.length < p + MAX_COPY_CMD_SIZE) {
+ if (0 < limit && limit < size + p)
+ return false;
+ out.write(buf, 0, p);
+ size += p;
+ p = 0;
+ }
+ }
+
+ p = encodeCopy(p, offset, cnt);
+ if (0 < limit && limit < size + p)
+ return false;
+ out.write(buf, 0, p);
+ size += p;
+ return true;
+ }
+
+ private int encodeCopy(int p, long offset, int cnt) {
+ int cmd = 0x80;
+ final int cmdPtr = p++; // save room for the command
+
+ if ((offset & 0xff) != 0) {
+ cmd |= 0x01;
+ buf[p++] = (byte) (offset & 0xff);
+ }
+ if ((offset & (0xff << 8)) != 0) {
+ cmd |= 0x02;
+ buf[p++] = (byte) ((offset >>> 8) & 0xff);
+ }
+ if ((offset & (0xff << 16)) != 0) {
+ cmd |= 0x04;
+ buf[p++] = (byte) ((offset >>> 16) & 0xff);
+ }
+ if ((offset & (0xff << 24)) != 0) {
+ cmd |= 0x08;
+ buf[p++] = (byte) ((offset >>> 24) & 0xff);
+ }
+
+ if (cnt != MAX_V2_COPY) {
+ if ((cnt & 0xff) != 0) {
+ cmd |= 0x10;
+ buf[p++] = (byte) (cnt & 0xff);
+ }
+ if ((cnt & (0xff << 8)) != 0) {
+ cmd |= 0x20;
+ buf[p++] = (byte) ((cnt >>> 8) & 0xff);
+ }
+ if ((cnt & (0xff << 16)) != 0) {
+ cmd |= 0x40;
+ buf[p++] = (byte) ((cnt >>> 16) & 0xff);
+ }
+ }
+
+ buf[cmdPtr] = (byte) cmd;
+ return p;
+ }
+}
diff --git a/org.eclipse.jgit/src/org/eclipse/jgit/storage/pack/DeltaIndex.java b/org.eclipse.jgit/src/org/eclipse/jgit/storage/pack/DeltaIndex.java
new file mode 100644
index 0000000..e548cc9
--- /dev/null
+++ b/org.eclipse.jgit/src/org/eclipse/jgit/storage/pack/DeltaIndex.java
@@ -0,0 +1,586 @@
+/*
+ * Copyright (C) 2010, Google Inc.
+ * and other copyright owners as documented in the project's IP log.
+ *
+ * This program and the accompanying materials are made available
+ * under the terms of the Eclipse Distribution License v1.0 which
+ * accompanies this distribution, is reproduced below, and is
+ * available at http://www.eclipse.org/org/documents/edl-v10.php
+ *
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or
+ * without modification, are permitted provided that the following
+ * conditions are met:
+ *
+ * - Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ *
+ * - Redistributions in binary form must reproduce the above
+ * copyright notice, this list of conditions and the following
+ * disclaimer in the documentation and/or other materials provided
+ * with the distribution.
+ *
+ * - Neither the name of the Eclipse Foundation, Inc. nor the
+ * names of its contributors may be used to endorse or promote
+ * products derived from this software without specific prior
+ * written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND
+ * CONTRIBUTORS "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES,
+ * INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
+ * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR
+ * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
+ * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT
+ * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
+ * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER
+ * CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT,
+ * STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
+ * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF
+ * ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ */
+
+package org.eclipse.jgit.storage.pack;
+
+import java.io.IOException;
+import java.io.OutputStream;
+
+/**
+ * Index of blocks in a source file.
+ * <p>
+ * The index can be passed a result buffer, and output an instruction sequence
+ * that transforms the source buffer used by the index into the result buffer.
+ * The instruction sequence can be executed by {@link BinaryDelta} or
+ * {@link DeltaStream} to recreate the result buffer.
+ * <p>
+ * An index stores the entire contents of the source buffer, but also a table of
+ * block identities mapped to locations where the block appears in the source
+ * buffer. The mapping table uses 12 bytes for every 16 bytes of source buffer,
+ * and is therefore ~75% of the source buffer size. The overall index is ~1.75x
+ * the size of the source buffer. This relationship holds for any JVM, as only a
+ * constant number of objects are allocated per index. Callers can use the
+ * method {@link #getIndexSize()} to obtain a reasonably accurate estimate of
+ * the complete heap space used by this index.
+ * <p>
+ * A {@code DeltaIndex} is thread-safe. Concurrent threads can use the same
+ * index to encode delta instructions for different result buffers.
+ */
+public class DeltaIndex {
+ /** Number of bytes in a block. */
+ static final int BLKSZ = 16; // must be 16, see unrolled loop in hashBlock
+
+ /**
+ * Estimate the size of an index for a given source.
+ * <p>
+ * This is roughly a worst-case estimate. The actual index may be smaller.
+ *
+ * @param sourceLength
+ * length of the source, in bytes.
+ * @return estimated size. Approximately {@code 1.75 * sourceLength}.
+ */
+ public static long estimateIndexSize(int sourceLength) {
+ return sourceLength + (sourceLength * 3 / 4);
+ }
+
+ /**
+ * Maximum number of positions to consider for a given content hash.
+ * <p>
+ * All positions with the same content hash are stored into a single chain.
+ * The chain size is capped to ensure delta encoding stays linear time at
+ * O(len_src + len_dst) rather than quadratic at O(len_src * len_dst).
+ */
+ private static final int MAX_CHAIN_LENGTH = 64;
+
+ /** Original source file that we indexed. */
+ private final byte[] src;
+
+ /**
+ * Pointers into the {@link #entries} table, indexed by block hash.
+ * <p>
+ * A block hash is masked with {@link #tableMask} to become the array index
+ * of this table. The value stored here is the first index within
+ * {@link #entries} that starts the consecutive list of blocks with that
+ * same masked hash. If there are no matching blocks, 0 is stored instead.
+ * <p>
+ * Note that this table is always a power of 2 in size, to support fast
+ * normalization of a block hash into an array index.
+ */
+ private final int[] table;
+
+ /**
+ * Pairs of block hash value and {@link #src} offsets.
+ * <p>
+ * The very first entry in this table at index 0 is always empty, this is to
+ * allow fast evaluation when {@link #table} has no values under any given
+ * slot. Remaining entries are pairs of integers, with the upper 32 bits
+ * holding the block hash and the lower 32 bits holding the source offset.
+ */
+ private final long[] entries;
+
+ /** Mask to make block hashes into an array index for {@link #table}. */
+ private final int tableMask;
+
+ /**
+ * Construct an index from the source file.
+ *
+ * @param sourceBuffer
+ * the source file's raw contents. The buffer will be held by the
+ * index instance to facilitate matching, and therefore must not
+ * be modified by the caller.
+ */
+ public DeltaIndex(byte[] sourceBuffer) {
+ src = sourceBuffer;
+
+ DeltaIndexScanner scan = new DeltaIndexScanner(src, src.length);
+
+ // Reuse the same table the scanner made. We will replace the
+ // values at each position, but we want the same-length array.
+ //
+ table = scan.table;
+ tableMask = scan.tableMask;
+
+ // Because entry index 0 means there are no entries for the
+ // slot in the table, we have to allocate one extra position.
+ //
+ entries = new long[1 + countEntries(scan)];
+ copyEntries(scan);
+ }
+
+ private int countEntries(DeltaIndexScanner scan) {
+ // Figure out exactly how many entries we need. As we do the
+ // enumeration truncate any delta chains longer than what we
+ // are willing to scan during encode. This keeps the encode
+ // logic linear in the size of the input rather than quadratic.
+ //
+ int cnt = 0;
+ for (int i = 0; i < table.length; i++) {
+ int h = table[i];
+ if (h == 0)
+ continue;
+
+ int len = 0;
+ do {
+ if (++len == MAX_CHAIN_LENGTH) {
+ scan.next[h] = 0;
+ break;
+ }
+ h = scan.next[h];
+ } while (h != 0);
+ cnt += len;
+ }
+ return cnt;
+ }
+
+ private void copyEntries(DeltaIndexScanner scan) {
+ // Rebuild the entries list from the scanner, positioning all
+ // blocks in the same hash chain next to each other. We can
+ // then later discard the next list, along with the scanner.
+ //
+ int next = 1;
+ for (int i = 0; i < table.length; i++) {
+ int h = table[i];
+ if (h == 0)
+ continue;
+
+ table[i] = next;
+ do {
+ entries[next++] = scan.entries[h];
+ h = scan.next[h];
+ } while (h != 0);
+ }
+ }
+
+ /** @return size of the source buffer this index has scanned. */
+ public long getSourceSize() {
+ return src.length;
+ }
+
+ /**
+ * Get an estimate of the memory required by this index.
+ *
+ * @return an approximation of the number of bytes used by this index in
+ * memory. The size includes the cached source buffer size from
+ * {@link #getSourceSize()}, as well as a rough approximation of JVM
+ * object overheads.
+ */
+ public long getIndexSize() {
+ long sz = 8 /* object header */;
+ sz += 4 /* fields */* 4 /* guessed size per field */;
+ sz += sizeOf(src);
+ sz += sizeOf(table);
+ sz += sizeOf(entries);
+ return sz;
+ }
+
+ private static long sizeOf(byte[] b) {
+ return sizeOfArray(1, b.length);
+ }
+
+ private static long sizeOf(int[] b) {
+ return sizeOfArray(4, b.length);
+ }
+
+ private static long sizeOf(long[] b) {
+ return sizeOfArray(8, b.length);
+ }
+
+ private static int sizeOfArray(int entSize, int len) {
+ return 12 /* estimated array header size */+ (len * entSize);
+ }
+
+ /**
+ * Generate a delta sequence to recreate the result buffer.
+ * <p>
+ * There is no limit on the size of the delta sequence created. This is the
+ * same as {@code encode(out, res, 0)}.
+ *
+ * @param out
+ * stream to receive the delta instructions that can transform
+ * this index's source buffer into {@code res}. This stream
+ * should be buffered, as instructions are written directly to it
+ * in small bursts.
+ * @param res
+ * the desired result buffer. The generated instructions will
+ * recreate this buffer when applied to the source buffer stored
+ * within this index.
+ * @throws IOException
+ * the output stream refused to write the instructions.
+ */
+ public void encode(OutputStream out, byte[] res) throws IOException {
+ encode(out, res, 0 /* no limit */);
+ }
+
+ /**
+ * Generate a delta sequence to recreate the result buffer.
+ *
+ * @param out
+ * stream to receive the delta instructions that can transform
+ * this index's source buffer into {@code res}. This stream
+ * should be buffered, as instructions are written directly to it
+ * in small bursts. If the caller might need to discard the
+ * instructions (such as when deltaSizeLimit would be exceeded)
+ * the caller is responsible for discarding or rewinding the
+ * stream when this method returns false.
+ * @param res
+ * the desired result buffer. The generated instructions will
+ * recreate this buffer when applied to the source buffer stored
+ * within this index.
+ * @param deltaSizeLimit
+ * maximum number of bytes that the delta instructions can
+ * occupy. If the generated instructions would be longer than
+ * this amount, this method returns false. If 0, there is no
+ * limit on the length of delta created.
+ * @return true if the delta is smaller than deltaSizeLimit; false if the
+ * encoder aborted because the encoded delta instructions would be
+ * longer than deltaSizeLimit bytes.
+ * @throws IOException
+ * the output stream refused to write the instructions.
+ */
+ public boolean encode(OutputStream out, byte[] res, int deltaSizeLimit)
+ throws IOException {
+ final int end = res.length;
+ final DeltaEncoder enc = newEncoder(out, end, deltaSizeLimit);
+
+ // If either input is smaller than one full block, we simply punt
+ // and construct a delta as a literal. This implies that any file
+ // smaller than our block size is never delta encoded as the delta
+ // will always be larger than the file itself would be.
+ //
+ if (end < BLKSZ || table.length == 0)
+ return enc.insert(res);
+
+ // Bootstrap the scan by constructing a hash for the first block
+ // in the input.
+ //
+ int blkPtr = 0;
+ int blkEnd = BLKSZ;
+ int hash = hashBlock(res, 0);
+
+ int resPtr = 0;
+ while (blkEnd < end) {
+ final int tableIdx = hash & tableMask;
+ int entryIdx = table[tableIdx];
+ if (entryIdx == 0) {
+ // No matching blocks, slide forward one byte.
+ //
+ hash = step(hash, res[blkPtr++], res[blkEnd++]);
+ continue;
+ }
+
+ // For every possible location of the current block, try to
+ // extend the match out to the longest common substring.
+ //
+ int bestLen = -1;
+ int bestPtr = -1;
+ int bestNeg = 0;
+ do {
+ long ent = entries[entryIdx++];
+ if (keyOf(ent) == hash) {
+ int neg = 0;
+ if (resPtr < blkPtr) {
+ // If we need to do an insertion, check to see if
+ // moving the starting point of the copy backwards
+ // will allow us to shorten the insert. Our hash
+ // may not have allowed us to identify this area.
+ // Since it is quite fast to perform a negative
+ // scan, try to stretch backwards too.
+ //
+ neg = blkPtr - resPtr;
+ neg = negmatch(res, blkPtr, src, valOf(ent), neg);
+ }
+
+ int len = neg + fwdmatch(res, blkPtr, src, valOf(ent));
+ if (bestLen < len) {
+ bestLen = len;
+ bestPtr = valOf(ent);
+ bestNeg = neg;
+ }
+ } else if ((keyOf(ent) & tableMask) != tableIdx)
+ break;
+ } while (bestLen < 4096 && entryIdx < entries.length);
+
+ if (bestLen < BLKSZ) {
+ // All of the locations were false positives, or the copy
+ // is shorter than a block. In the latter case this won't
+ // give us a very great copy instruction, so delay and try
+ // at the next byte.
+ //
+ hash = step(hash, res[blkPtr++], res[blkEnd++]);
+ continue;
+ }
+
+ blkPtr -= bestNeg;
+
+ if (resPtr < blkPtr) {
+ // There are bytes between the last instruction we made
+ // and the current block pointer. None of these matched
+ // during the earlier iteration so insert them directly
+ // into the instruction stream.
+ //
+ int cnt = blkPtr - resPtr;
+ if (!enc.insert(res, resPtr, cnt))
+ return false;
+ }
+
+ if (!enc.copy(bestPtr - bestNeg, bestLen))
+ return false;
+
+ blkPtr += bestLen;
+ resPtr = blkPtr;
+ blkEnd = blkPtr + BLKSZ;
+
+ // If we don't have a full block available to us, abort now.
+ //
+ if (end <= blkEnd)
+ break;
+
+ // Start a new hash of the block after the copy region.
+ //
+ hash = hashBlock(res, blkPtr);
+ }
+
+ if (resPtr < end) {
+ // There were bytes at the end which didn't match, or maybe
+ // didn't make a full block. Insert whatever is left over.
+ //
+ int cnt = end - resPtr;
+ return enc.insert(res, resPtr, cnt);
+ }
+ return true;
+ }
+
+ private DeltaEncoder newEncoder(OutputStream out, long resSize, int limit)
+ throws IOException {
+ return new DeltaEncoder(out, getSourceSize(), resSize, limit);
+ }
+
+ private static int fwdmatch(byte[] res, int resPtr, byte[] src, int srcPtr) {
+ int start = resPtr;
+ for (; resPtr < res.length && srcPtr < src.length; resPtr++, srcPtr++) {
+ if (res[resPtr] != src[srcPtr])
+ break;
+ }
+ return resPtr - start;
+ }
+
+ private static int negmatch(byte[] res, int resPtr, byte[] src, int srcPtr,
+ int limit) {
+ if (srcPtr == 0)
+ return 0;
+
+ resPtr--;
+ srcPtr--;
+ int start = resPtr;
+ do {
+ if (res[resPtr] != src[srcPtr])
+ break;
+ resPtr--;
+ srcPtr--;
+ } while (0 <= srcPtr && 0 < --limit);
+ return start - resPtr;
+ }
+
+ public String toString() {
+ String[] units = { "bytes", "KiB", "MiB", "GiB" };
+ long sz = getIndexSize();
+ int u = 0;
+ while (1024 <= sz && u < units.length - 1) {
+ int rem = (int) (sz % 1024);
+ sz /= 1024;
+ if (rem != 0)
+ sz++;
+ u++;
+ }
+ return "DeltaIndex[" + sz + " " + units[u] + "]";
+ }
+
+ static int hashBlock(byte[] raw, int ptr) {
+ int hash;
+
+ // The first 4 steps collapse out into a 4 byte big-endian decode,
+ // with a larger right shift as we combined shift lefts together.
+ //
+ hash = ((raw[ptr] & 0xff) << 24) //
+ | ((raw[ptr + 1] & 0xff) << 16) //
+ | ((raw[ptr + 2] & 0xff) << 8) //
+ | (raw[ptr + 3] & 0xff);
+ hash ^= T[hash >>> 31];
+
+ hash = ((hash << 8) | (raw[ptr + 4] & 0xff)) ^ T[hash >>> 23];
+ hash = ((hash << 8) | (raw[ptr + 5] & 0xff)) ^ T[hash >>> 23];
+ hash = ((hash << 8) | (raw[ptr + 6] & 0xff)) ^ T[hash >>> 23];
+ hash = ((hash << 8) | (raw[ptr + 7] & 0xff)) ^ T[hash >>> 23];
+
+ hash = ((hash << 8) | (raw[ptr + 8] & 0xff)) ^ T[hash >>> 23];
+ hash = ((hash << 8) | (raw[ptr + 9] & 0xff)) ^ T[hash >>> 23];
+ hash = ((hash << 8) | (raw[ptr + 10] & 0xff)) ^ T[hash >>> 23];
+ hash = ((hash << 8) | (raw[ptr + 11] & 0xff)) ^ T[hash >>> 23];
+
+ hash = ((hash << 8) | (raw[ptr + 12] & 0xff)) ^ T[hash >>> 23];
+ hash = ((hash << 8) | (raw[ptr + 13] & 0xff)) ^ T[hash >>> 23];
+ hash = ((hash << 8) | (raw[ptr + 14] & 0xff)) ^ T[hash >>> 23];
+ hash = ((hash << 8) | (raw[ptr + 15] & 0xff)) ^ T[hash >>> 23];
+
+ return hash;
+ }
+
+ private static int step(int hash, byte toRemove, byte toAdd) {
+ hash ^= U[toRemove & 0xff];
+ return ((hash << 8) | (toAdd & 0xff)) ^ T[hash >>> 23];
+ }
+
+ private static int keyOf(long ent) {
+ return (int) (ent >>> 32);
+ }
+
+ private static int valOf(long ent) {
+ return (int) ent;
+ }
+
+ private static final int[] T = { 0x00000000, 0xd4c6b32d, 0x7d4bd577,
+ 0xa98d665a, 0x2e5119c3, 0xfa97aaee, 0x531accb4, 0x87dc7f99,
+ 0x5ca23386, 0x886480ab, 0x21e9e6f1, 0xf52f55dc, 0x72f32a45,
+ 0xa6359968, 0x0fb8ff32, 0xdb7e4c1f, 0x6d82d421, 0xb944670c,
+ 0x10c90156, 0xc40fb27b, 0x43d3cde2, 0x97157ecf, 0x3e981895,
+ 0xea5eabb8, 0x3120e7a7, 0xe5e6548a, 0x4c6b32d0, 0x98ad81fd,
+ 0x1f71fe64, 0xcbb74d49, 0x623a2b13, 0xb6fc983e, 0x0fc31b6f,
+ 0xdb05a842, 0x7288ce18, 0xa64e7d35, 0x219202ac, 0xf554b181,
+ 0x5cd9d7db, 0x881f64f6, 0x536128e9, 0x87a79bc4, 0x2e2afd9e,
+ 0xfaec4eb3, 0x7d30312a, 0xa9f68207, 0x007be45d, 0xd4bd5770,
+ 0x6241cf4e, 0xb6877c63, 0x1f0a1a39, 0xcbcca914, 0x4c10d68d,
+ 0x98d665a0, 0x315b03fa, 0xe59db0d7, 0x3ee3fcc8, 0xea254fe5,
+ 0x43a829bf, 0x976e9a92, 0x10b2e50b, 0xc4745626, 0x6df9307c,
+ 0xb93f8351, 0x1f8636de, 0xcb4085f3, 0x62cde3a9, 0xb60b5084,
+ 0x31d72f1d, 0xe5119c30, 0x4c9cfa6a, 0x985a4947, 0x43240558,
+ 0x97e2b675, 0x3e6fd02f, 0xeaa96302, 0x6d751c9b, 0xb9b3afb6,
+ 0x103ec9ec, 0xc4f87ac1, 0x7204e2ff, 0xa6c251d2, 0x0f4f3788,
+ 0xdb8984a5, 0x5c55fb3c, 0x88934811, 0x211e2e4b, 0xf5d89d66,
+ 0x2ea6d179, 0xfa606254, 0x53ed040e, 0x872bb723, 0x00f7c8ba,
+ 0xd4317b97, 0x7dbc1dcd, 0xa97aaee0, 0x10452db1, 0xc4839e9c,
+ 0x6d0ef8c6, 0xb9c84beb, 0x3e143472, 0xead2875f, 0x435fe105,
+ 0x97995228, 0x4ce71e37, 0x9821ad1a, 0x31accb40, 0xe56a786d,
+ 0x62b607f4, 0xb670b4d9, 0x1ffdd283, 0xcb3b61ae, 0x7dc7f990,
+ 0xa9014abd, 0x008c2ce7, 0xd44a9fca, 0x5396e053, 0x8750537e,
+ 0x2edd3524, 0xfa1b8609, 0x2165ca16, 0xf5a3793b, 0x5c2e1f61,
+ 0x88e8ac4c, 0x0f34d3d5, 0xdbf260f8, 0x727f06a2, 0xa6b9b58f,
+ 0x3f0c6dbc, 0xebcade91, 0x4247b8cb, 0x96810be6, 0x115d747f,
+ 0xc59bc752, 0x6c16a108, 0xb8d01225, 0x63ae5e3a, 0xb768ed17,
+ 0x1ee58b4d, 0xca233860, 0x4dff47f9, 0x9939f4d4, 0x30b4928e,
+ 0xe47221a3, 0x528eb99d, 0x86480ab0, 0x2fc56cea, 0xfb03dfc7,
+ 0x7cdfa05e, 0xa8191373, 0x01947529, 0xd552c604, 0x0e2c8a1b,
+ 0xdaea3936, 0x73675f6c, 0xa7a1ec41, 0x207d93d8, 0xf4bb20f5,
+ 0x5d3646af, 0x89f0f582, 0x30cf76d3, 0xe409c5fe, 0x4d84a3a4,
+ 0x99421089, 0x1e9e6f10, 0xca58dc3d, 0x63d5ba67, 0xb713094a,
+ 0x6c6d4555, 0xb8abf678, 0x11269022, 0xc5e0230f, 0x423c5c96,
+ 0x96faefbb, 0x3f7789e1, 0xebb13acc, 0x5d4da2f2, 0x898b11df,
+ 0x20067785, 0xf4c0c4a8, 0x731cbb31, 0xa7da081c, 0x0e576e46,
+ 0xda91dd6b, 0x01ef9174, 0xd5292259, 0x7ca44403, 0xa862f72e,
+ 0x2fbe88b7, 0xfb783b9a, 0x52f55dc0, 0x8633eeed, 0x208a5b62,
+ 0xf44ce84f, 0x5dc18e15, 0x89073d38, 0x0edb42a1, 0xda1df18c,
+ 0x739097d6, 0xa75624fb, 0x7c2868e4, 0xa8eedbc9, 0x0163bd93,
+ 0xd5a50ebe, 0x52797127, 0x86bfc20a, 0x2f32a450, 0xfbf4177d,
+ 0x4d088f43, 0x99ce3c6e, 0x30435a34, 0xe485e919, 0x63599680,
+ 0xb79f25ad, 0x1e1243f7, 0xcad4f0da, 0x11aabcc5, 0xc56c0fe8,
+ 0x6ce169b2, 0xb827da9f, 0x3ffba506, 0xeb3d162b, 0x42b07071,
+ 0x9676c35c, 0x2f49400d, 0xfb8ff320, 0x5202957a, 0x86c42657,
+ 0x011859ce, 0xd5deeae3, 0x7c538cb9, 0xa8953f94, 0x73eb738b,
+ 0xa72dc0a6, 0x0ea0a6fc, 0xda6615d1, 0x5dba6a48, 0x897cd965,
+ 0x20f1bf3f, 0xf4370c12, 0x42cb942c, 0x960d2701, 0x3f80415b,
+ 0xeb46f276, 0x6c9a8def, 0xb85c3ec2, 0x11d15898, 0xc517ebb5,
+ 0x1e69a7aa, 0xcaaf1487, 0x632272dd, 0xb7e4c1f0, 0x3038be69,
+ 0xe4fe0d44, 0x4d736b1e, 0x99b5d833 };
+
+ private static final int[] U = { 0x00000000, 0x12c6e90f, 0x258dd21e,
+ 0x374b3b11, 0x4b1ba43c, 0x59dd4d33, 0x6e967622, 0x7c509f2d,
+ 0x42f1fb55, 0x5037125a, 0x677c294b, 0x75bac044, 0x09ea5f69,
+ 0x1b2cb666, 0x2c678d77, 0x3ea16478, 0x51254587, 0x43e3ac88,
+ 0x74a89799, 0x666e7e96, 0x1a3ee1bb, 0x08f808b4, 0x3fb333a5,
+ 0x2d75daaa, 0x13d4bed2, 0x011257dd, 0x36596ccc, 0x249f85c3,
+ 0x58cf1aee, 0x4a09f3e1, 0x7d42c8f0, 0x6f8421ff, 0x768c3823,
+ 0x644ad12c, 0x5301ea3d, 0x41c70332, 0x3d979c1f, 0x2f517510,
+ 0x181a4e01, 0x0adca70e, 0x347dc376, 0x26bb2a79, 0x11f01168,
+ 0x0336f867, 0x7f66674a, 0x6da08e45, 0x5aebb554, 0x482d5c5b,
+ 0x27a97da4, 0x356f94ab, 0x0224afba, 0x10e246b5, 0x6cb2d998,
+ 0x7e743097, 0x493f0b86, 0x5bf9e289, 0x655886f1, 0x779e6ffe,
+ 0x40d554ef, 0x5213bde0, 0x2e4322cd, 0x3c85cbc2, 0x0bcef0d3,
+ 0x190819dc, 0x39dec36b, 0x2b182a64, 0x1c531175, 0x0e95f87a,
+ 0x72c56757, 0x60038e58, 0x5748b549, 0x458e5c46, 0x7b2f383e,
+ 0x69e9d131, 0x5ea2ea20, 0x4c64032f, 0x30349c02, 0x22f2750d,
+ 0x15b94e1c, 0x077fa713, 0x68fb86ec, 0x7a3d6fe3, 0x4d7654f2,
+ 0x5fb0bdfd, 0x23e022d0, 0x3126cbdf, 0x066df0ce, 0x14ab19c1,
+ 0x2a0a7db9, 0x38cc94b6, 0x0f87afa7, 0x1d4146a8, 0x6111d985,
+ 0x73d7308a, 0x449c0b9b, 0x565ae294, 0x4f52fb48, 0x5d941247,
+ 0x6adf2956, 0x7819c059, 0x04495f74, 0x168fb67b, 0x21c48d6a,
+ 0x33026465, 0x0da3001d, 0x1f65e912, 0x282ed203, 0x3ae83b0c,
+ 0x46b8a421, 0x547e4d2e, 0x6335763f, 0x71f39f30, 0x1e77becf,
+ 0x0cb157c0, 0x3bfa6cd1, 0x293c85de, 0x556c1af3, 0x47aaf3fc,
+ 0x70e1c8ed, 0x622721e2, 0x5c86459a, 0x4e40ac95, 0x790b9784,
+ 0x6bcd7e8b, 0x179de1a6, 0x055b08a9, 0x321033b8, 0x20d6dab7,
+ 0x73bd86d6, 0x617b6fd9, 0x563054c8, 0x44f6bdc7, 0x38a622ea,
+ 0x2a60cbe5, 0x1d2bf0f4, 0x0fed19fb, 0x314c7d83, 0x238a948c,
+ 0x14c1af9d, 0x06074692, 0x7a57d9bf, 0x689130b0, 0x5fda0ba1,
+ 0x4d1ce2ae, 0x2298c351, 0x305e2a5e, 0x0715114f, 0x15d3f840,
+ 0x6983676d, 0x7b458e62, 0x4c0eb573, 0x5ec85c7c, 0x60693804,
+ 0x72afd10b, 0x45e4ea1a, 0x57220315, 0x2b729c38, 0x39b47537,
+ 0x0eff4e26, 0x1c39a729, 0x0531bef5, 0x17f757fa, 0x20bc6ceb,
+ 0x327a85e4, 0x4e2a1ac9, 0x5cecf3c6, 0x6ba7c8d7, 0x796121d8,
+ 0x47c045a0, 0x5506acaf, 0x624d97be, 0x708b7eb1, 0x0cdbe19c,
+ 0x1e1d0893, 0x29563382, 0x3b90da8d, 0x5414fb72, 0x46d2127d,
+ 0x7199296c, 0x635fc063, 0x1f0f5f4e, 0x0dc9b641, 0x3a828d50,
+ 0x2844645f, 0x16e50027, 0x0423e928, 0x3368d239, 0x21ae3b36,
+ 0x5dfea41b, 0x4f384d14, 0x78737605, 0x6ab59f0a, 0x4a6345bd,
+ 0x58a5acb2, 0x6fee97a3, 0x7d287eac, 0x0178e181, 0x13be088e,
+ 0x24f5339f, 0x3633da90, 0x0892bee8, 0x1a5457e7, 0x2d1f6cf6,
+ 0x3fd985f9, 0x43891ad4, 0x514ff3db, 0x6604c8ca, 0x74c221c5,
+ 0x1b46003a, 0x0980e935, 0x3ecbd224, 0x2c0d3b2b, 0x505da406,
+ 0x429b4d09, 0x75d07618, 0x67169f17, 0x59b7fb6f, 0x4b711260,
+ 0x7c3a2971, 0x6efcc07e, 0x12ac5f53, 0x006ab65c, 0x37218d4d,
+ 0x25e76442, 0x3cef7d9e, 0x2e299491, 0x1962af80, 0x0ba4468f,
+ 0x77f4d9a2, 0x653230ad, 0x52790bbc, 0x40bfe2b3, 0x7e1e86cb,
+ 0x6cd86fc4, 0x5b9354d5, 0x4955bdda, 0x350522f7, 0x27c3cbf8,
+ 0x1088f0e9, 0x024e19e6, 0x6dca3819, 0x7f0cd116, 0x4847ea07,
+ 0x5a810308, 0x26d19c25, 0x3417752a, 0x035c4e3b, 0x119aa734,
+ 0x2f3bc34c, 0x3dfd2a43, 0x0ab61152, 0x1870f85d, 0x64206770,
+ 0x76e68e7f, 0x41adb56e, 0x536b5c61 };
+}
diff --git a/org.eclipse.jgit/src/org/eclipse/jgit/storage/pack/DeltaIndexScanner.java b/org.eclipse.jgit/src/org/eclipse/jgit/storage/pack/DeltaIndexScanner.java
new file mode 100644
index 0000000..d30690d
--- /dev/null
+++ b/org.eclipse.jgit/src/org/eclipse/jgit/storage/pack/DeltaIndexScanner.java
@@ -0,0 +1,130 @@
+/*
+ * Copyright (C) 2010, Google Inc.
+ * and other copyright owners as documented in the project's IP log.
+ *
+ * This program and the accompanying materials are made available
+ * under the terms of the Eclipse Distribution License v1.0 which
+ * accompanies this distribution, is reproduced below, and is
+ * available at http://www.eclipse.org/org/documents/edl-v10.php
+ *
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or
+ * without modification, are permitted provided that the following
+ * conditions are met:
+ *
+ * - Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ *
+ * - Redistributions in binary form must reproduce the above
+ * copyright notice, this list of conditions and the following
+ * disclaimer in the documentation and/or other materials provided
+ * with the distribution.
+ *
+ * - Neither the name of the Eclipse Foundation, Inc. nor the
+ * names of its contributors may be used to endorse or promote
+ * products derived from this software without specific prior
+ * written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND
+ * CONTRIBUTORS "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES,
+ * INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
+ * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR
+ * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
+ * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT
+ * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
+ * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER
+ * CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT,
+ * STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
+ * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF
+ * ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ */
+
+package org.eclipse.jgit.storage.pack;
+
+/**
+ * Supports {@link DeltaIndex} by performing a partial scan of the content.
+ */
+class DeltaIndexScanner {
+ final int[] table;
+
+ // To save memory the buckets for hash chains are stored in correlated
+ // arrays. This permits us to get 3 values per entry, without paying
+ // the penalty for an object header on each entry.
+
+ final long[] entries;
+
+ final int[] next;
+
+ final int tableMask;
+
+ private int entryCnt;
+
+ DeltaIndexScanner(byte[] raw, int len) {
+ // Clip the length so it falls on a block boundary. We won't
+ // bother to scan the final partial block.
+ //
+ len -= (len % DeltaIndex.BLKSZ);
+
+ final int worstCaseBlockCnt = len / DeltaIndex.BLKSZ;
+ if (worstCaseBlockCnt < 1) {
+ table = new int[] {};
+ tableMask = 0;
+
+ entries = new long[] {};
+ next = new int[] {};
+
+ } else {
+ table = new int[tableSize(worstCaseBlockCnt)];
+ tableMask = table.length - 1;
+
+ // As we insert blocks we preincrement so that 0 is never a
+ // valid entry. Therefore we have to allocate one extra space.
+ //
+ entries = new long[1 + worstCaseBlockCnt];
+ next = new int[entries.length];
+
+ scan(raw, len);
+ }
+ }
+
+ private void scan(byte[] raw, final int end) {
+ // We scan the input backwards, and always insert onto the
+ // front of the chain. This ensures that chains will have lower
+ // offsets at the front of the chain, allowing us to prefer the
+ // earlier match rather than the later match.
+ //
+ int lastHash = 0;
+ int ptr = end - DeltaIndex.BLKSZ;
+ do {
+ final int key = DeltaIndex.hashBlock(raw, ptr);
+ final int tIdx = key & tableMask;
+
+ final int head = table[tIdx];
+ if (head != 0 && lastHash == key) {
+ // Two consecutive blocks have the same content hash,
+ // prefer the earlier block because we want to use the
+ // longest sequence we can during encoding.
+ //
+ entries[head] = (((long) key) << 32) | ptr;
+ } else {
+ final int eIdx = ++entryCnt;
+ entries[eIdx] = (((long) key) << 32) | ptr;
+ next[eIdx] = head;
+ table[tIdx] = eIdx;
+ }
+
+ lastHash = key;
+ ptr -= DeltaIndex.BLKSZ;
+ } while (0 <= ptr);
+ }
+
+ private static int tableSize(final int worstCaseBlockCnt) {
+ int shift = 32 - Integer.numberOfLeadingZeros(worstCaseBlockCnt);
+ int sz = 1 << (shift - 1);
+ if (sz < worstCaseBlockCnt)
+ sz <<= 1;
+ return sz;
+ }
+}
\ No newline at end of file
diff --git a/org.eclipse.jgit/src/org/eclipse/jgit/storage/pack/DeltaStream.java b/org.eclipse.jgit/src/org/eclipse/jgit/storage/pack/DeltaStream.java
new file mode 100644
index 0000000..6f479eb
--- /dev/null
+++ b/org.eclipse.jgit/src/org/eclipse/jgit/storage/pack/DeltaStream.java
@@ -0,0 +1,341 @@
+/*
+ * Copyright (C) 2007, Robin Rosenberg <robin.rosenberg@dewire.com>
+ * Copyright (C) 2006-2007, Shawn O. Pearce <spearce@spearce.org>
+ * Copyright (C) 2010, Google Inc.
+ * and other copyright owners as documented in the project's IP log.
+ *
+ * This program and the accompanying materials are made available
+ * under the terms of the Eclipse Distribution License v1.0 which
+ * accompanies this distribution, is reproduced below, and is
+ * available at http://www.eclipse.org/org/documents/edl-v10.php
+ *
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or
+ * without modification, are permitted provided that the following
+ * conditions are met:
+ *
+ * - Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ *
+ * - Redistributions in binary form must reproduce the above
+ * copyright notice, this list of conditions and the following
+ * disclaimer in the documentation and/or other materials provided
+ * with the distribution.
+ *
+ * - Neither the name of the Eclipse Foundation, Inc. nor the
+ * names of its contributors may be used to endorse or promote
+ * products derived from this software without specific prior
+ * written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND
+ * CONTRIBUTORS "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES,
+ * INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
+ * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR
+ * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
+ * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT
+ * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
+ * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER
+ * CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT,
+ * STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
+ * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF
+ * ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ */
+
+package org.eclipse.jgit.storage.pack;
+
+import java.io.EOFException;
+import java.io.IOException;
+import java.io.InputStream;
+
+import org.eclipse.jgit.JGitText;
+import org.eclipse.jgit.errors.CorruptObjectException;
+import org.eclipse.jgit.util.IO;
+
+/**
+ * Inflates a delta in an incremental way.
+ * <p>
+ * Implementations must provide a means to access a stream for the base object.
+ * This stream may be accessed multiple times, in order to randomly position it
+ * to match the copy instructions. A {@code DeltaStream} performs an efficient
+ * skip by only moving through the delta stream, making restarts of stacked
+ * deltas reasonably efficient.
+ */
+public abstract class DeltaStream extends InputStream {
+ private static final int CMD_COPY = 0;
+
+ private static final int CMD_INSERT = 1;
+
+ private static final int CMD_EOF = 2;
+
+ private final InputStream deltaStream;
+
+ private long baseSize;
+
+ private long resultSize;
+
+ private final byte[] cmdbuf = new byte[512];
+
+ private int cmdptr;
+
+ private int cmdcnt;
+
+ /** Stream to read from the base object. */
+ private InputStream baseStream;
+
+ /** Current position within {@link #baseStream}. */
+ private long baseOffset;
+
+ private int curcmd;
+
+ /** If {@code curcmd == CMD_COPY}, position the base has to be at. */
+ private long copyOffset;
+
+ /** Total number of bytes in this current command. */
+ private int copySize;
+
+ /**
+ * Construct a delta application stream, reading instructions.
+ *
+ * @param deltaStream
+ * the stream to read delta instructions from.
+ * @throws IOException
+ * the delta instruction stream cannot be read, or is
+ * inconsistent with the the base object information.
+ */
+ public DeltaStream(final InputStream deltaStream) throws IOException {
+ this.deltaStream = deltaStream;
+ if (!fill(cmdbuf.length))
+ throw new EOFException();
+
+ // Length of the base object.
+ //
+ int c, shift = 0;
+ do {
+ c = cmdbuf[cmdptr++] & 0xff;
+ baseSize |= (c & 0x7f) << shift;
+ shift += 7;
+ } while ((c & 0x80) != 0);
+
+ // Length of the resulting object.
+ //
+ shift = 0;
+ do {
+ c = cmdbuf[cmdptr++] & 0xff;
+ resultSize |= (c & 0x7f) << shift;
+ shift += 7;
+ } while ((c & 0x80) != 0);
+
+ curcmd = next();
+ }
+
+ /**
+ * Open the base stream.
+ * <p>
+ * The {@code DeltaStream} may close and reopen the base stream multiple
+ * times if copy instructions use offsets out of order. This can occur if a
+ * large block in the file was moved from near the top, to near the bottom.
+ * In such cases the reopened stream is skipped to the target offset, so
+ * {@code skip(long)} should be as efficient as possible.
+ *
+ * @return stream to read from the base object. This stream should not be
+ * buffered (or should be only minimally buffered), and does not
+ * need to support mark/reset.
+ * @throws IOException
+ * the base object cannot be opened for reading.
+ */
+ protected abstract InputStream openBase() throws IOException;
+
+ /**
+ * @return length of the base object, in bytes.
+ * @throws IOException
+ * the length of the base cannot be determined.
+ */
+ protected abstract long getBaseSize() throws IOException;
+
+ /** @return total size of this stream, in bytes. */
+ public long getSize() {
+ return resultSize;
+ }
+
+ @Override
+ public int read() throws IOException {
+ byte[] buf = new byte[1];
+ int n = read(buf, 0, 1);
+ return n == 1 ? buf[0] & 0xff : -1;
+ }
+
+ @Override
+ public void close() throws IOException {
+ deltaStream.close();
+ if (baseStream != null)
+ baseStream.close();
+ }
+
+ @Override
+ public long skip(long len) throws IOException {
+ long act = 0;
+ while (0 < len) {
+ long n = Math.min(len, copySize);
+ switch (curcmd) {
+ case CMD_COPY:
+ copyOffset += n;
+ break;
+
+ case CMD_INSERT:
+ cmdptr += n;
+ break;
+
+ case CMD_EOF:
+ return act;
+ default:
+ throw new CorruptObjectException(
+ JGitText.get().unsupportedCommand0);
+ }
+
+ act += n;
+ len -= n;
+ copySize -= n;
+ if (copySize == 0)
+ curcmd = next();
+ }
+ return act;
+ }
+
+ @Override
+ public int read(byte[] buf, int off, int len) throws IOException {
+ int act = 0;
+ while (0 < len) {
+ int n = Math.min(len, copySize);
+ switch (curcmd) {
+ case CMD_COPY:
+ seekBase();
+ n = baseStream.read(buf, off, n);
+ if (n < 0)
+ throw new CorruptObjectException(
+ JGitText.get().baseLengthIncorrect);
+ baseOffset += n;
+ break;
+
+ case CMD_INSERT:
+ System.arraycopy(cmdbuf, cmdptr, buf, off, n);
+ cmdptr += n;
+ break;
+
+ case CMD_EOF:
+ return 0 < act ? act : -1;
+ default:
+ throw new CorruptObjectException(
+ JGitText.get().unsupportedCommand0);
+ }
+
+ act += n;
+ off += n;
+ len -= n;
+ copySize -= n;
+ if (copySize == 0)
+ curcmd = next();
+ }
+ return act;
+ }
+
+ private boolean fill(final int need) throws IOException {
+ int n = have();
+ if (need < n)
+ return true;
+ if (n == 0) {
+ cmdptr = 0;
+ cmdcnt = 0;
+ } else if (cmdbuf.length - cmdptr < need) {
+ // There isn't room for the entire worst-case copy command,
+ // so shift the array down to make sure we can use the entire
+ // command without having it span across the end of the array.
+ //
+ System.arraycopy(cmdbuf, cmdptr, cmdbuf, 0, n);
+ cmdptr = 0;
+ cmdcnt = n;
+ }
+
+ do {
+ n = deltaStream.read(cmdbuf, cmdcnt, cmdbuf.length - cmdcnt);
+ if (n < 0)
+ return 0 < have();
+ cmdcnt += n;
+ } while (cmdcnt < cmdbuf.length);
+ return true;
+ }
+
+ private int next() throws IOException {
+ if (!fill(8))
+ return CMD_EOF;
+
+ final int cmd = cmdbuf[cmdptr++] & 0xff;
+ if ((cmd & 0x80) != 0) {
+ // Determine the segment of the base which should
+ // be copied into the output. The segment is given
+ // as an offset and a length.
+ //
+ copyOffset = 0;
+ if ((cmd & 0x01) != 0)
+ copyOffset = cmdbuf[cmdptr++] & 0xff;
+ if ((cmd & 0x02) != 0)
+ copyOffset |= (cmdbuf[cmdptr++] & 0xff) << 8;
+ if ((cmd & 0x04) != 0)
+ copyOffset |= (cmdbuf[cmdptr++] & 0xff) << 16;
+ if ((cmd & 0x08) != 0)
+ copyOffset |= (cmdbuf[cmdptr++] & 0xff) << 24;
+
+ copySize = 0;
+ if ((cmd & 0x10) != 0)
+ copySize = cmdbuf[cmdptr++] & 0xff;
+ if ((cmd & 0x20) != 0)
+ copySize |= (cmdbuf[cmdptr++] & 0xff) << 8;
+ if ((cmd & 0x40) != 0)
+ copySize |= (cmdbuf[cmdptr++] & 0xff) << 16;
+ if (copySize == 0)
+ copySize = 0x10000;
+ return CMD_COPY;
+
+ } else if (cmd != 0) {
+ // Anything else the data is literal within the delta
+ // itself. Page the entire thing into the cmdbuf, if
+ // its not already there.
+ //
+ fill(cmd);
+ copySize = cmd;
+ return CMD_INSERT;
+
+ } else {
+ // cmd == 0 has been reserved for future encoding but
+ // for now its not acceptable.
+ //
+ throw new CorruptObjectException(JGitText.get().unsupportedCommand0);
+ }
+ }
+
+ private int have() {
+ return cmdcnt - cmdptr;
+ }
+
+ private void seekBase() throws IOException {
+ if (baseStream == null) {
+ baseStream = openBase();
+ if (getBaseSize() != baseSize)
+ throw new CorruptObjectException(
+ JGitText.get().baseLengthIncorrect);
+ IO.skipFully(baseStream, copyOffset);
+ baseOffset = copyOffset;
+
+ } else if (baseOffset < copyOffset) {
+ IO.skipFully(baseStream, copyOffset - baseOffset);
+ baseOffset = copyOffset;
+
+ } else if (baseOffset > copyOffset) {
+ baseStream.close();
+ baseStream = openBase();
+ IO.skipFully(baseStream, copyOffset);
+ baseOffset = copyOffset;
+ }
+ }
+}
diff --git a/org.eclipse.jgit/src/org/eclipse/jgit/lib/RepositoryAdapter.java b/org.eclipse.jgit/src/org/eclipse/jgit/storage/pack/DeltaTask.java
similarity index 64%
copy from org.eclipse.jgit/src/org/eclipse/jgit/lib/RepositoryAdapter.java
copy to org.eclipse.jgit/src/org/eclipse/jgit/storage/pack/DeltaTask.java
index e43c33a..5e551e9 100644
--- a/org.eclipse.jgit/src/org/eclipse/jgit/lib/RepositoryAdapter.java
+++ b/org.eclipse.jgit/src/org/eclipse/jgit/storage/pack/DeltaTask.java
@@ -1,5 +1,5 @@
/*
- * Copyright (C) 2008, Robin Rosenberg <robin.rosenberg@dewire.com>
+ * Copyright (C) 2010, Google Inc.
* and other copyright owners as documented in the project's IP log.
*
* This program and the accompanying materials are made available
@@ -41,20 +41,48 @@
* ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
*/
-package org.eclipse.jgit.lib;
+package org.eclipse.jgit.storage.pack;
-/**
- * A default {@link RepositoryListener} that does nothing except invoke an
- * optional general method for any repository change.
- */
-public class RepositoryAdapter implements RepositoryListener {
+import java.util.concurrent.Callable;
- public void indexChanged(final IndexChangedEvent e) {
- // Empty
+import org.eclipse.jgit.lib.ObjectReader;
+import org.eclipse.jgit.lib.ProgressMonitor;
+
+final class DeltaTask implements Callable<Object> {
+ private final PackConfig config;
+
+ private final ObjectReader templateReader;
+
+ private final DeltaCache dc;
+
+ private final ProgressMonitor pm;
+
+ private final int batchSize;
+
+ private final int start;
+
+ private final ObjectToPack[] list;
+
+ DeltaTask(PackConfig config, ObjectReader reader, DeltaCache dc,
+ ProgressMonitor pm, int batchSize, int start, ObjectToPack[] list) {
+ this.config = config;
+ this.templateReader = reader;
+ this.dc = dc;
+ this.pm = pm;
+ this.batchSize = batchSize;
+ this.start = start;
+ this.list = list;
}
- public void refsChanged(final RefsChangedEvent e) {
- // Empty
+ public Object call() throws Exception {
+ final ObjectReader or = templateReader.newReader();
+ try {
+ DeltaWindow dw;
+ dw = new DeltaWindow(config, dc, or);
+ dw.search(pm, list, start, batchSize);
+ } finally {
+ or.release();
+ }
+ return null;
}
-
}
diff --git a/org.eclipse.jgit/src/org/eclipse/jgit/storage/pack/DeltaWindow.java b/org.eclipse.jgit/src/org/eclipse/jgit/storage/pack/DeltaWindow.java
new file mode 100644
index 0000000..c961056
--- /dev/null
+++ b/org.eclipse.jgit/src/org/eclipse/jgit/storage/pack/DeltaWindow.java
@@ -0,0 +1,511 @@
+/*
+ * Copyright (C) 2010, Google Inc.
+ * and other copyright owners as documented in the project's IP log.
+ *
+ * This program and the accompanying materials are made available
+ * under the terms of the Eclipse Distribution License v1.0 which
+ * accompanies this distribution, is reproduced below, and is
+ * available at http://www.eclipse.org/org/documents/edl-v10.php
+ *
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or
+ * without modification, are permitted provided that the following
+ * conditions are met:
+ *
+ * - Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ *
+ * - Redistributions in binary form must reproduce the above
+ * copyright notice, this list of conditions and the following
+ * disclaimer in the documentation and/or other materials provided
+ * with the distribution.
+ *
+ * - Neither the name of the Eclipse Foundation, Inc. nor the
+ * names of its contributors may be used to endorse or promote
+ * products derived from this software without specific prior
+ * written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND
+ * CONTRIBUTORS "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES,
+ * INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
+ * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR
+ * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
+ * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT
+ * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
+ * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER
+ * CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT,
+ * STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
+ * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF
+ * ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ */
+
+package org.eclipse.jgit.storage.pack;
+
+import java.io.EOFException;
+import java.io.IOException;
+import java.io.OutputStream;
+import java.util.zip.Deflater;
+
+import org.eclipse.jgit.errors.IncorrectObjectTypeException;
+import org.eclipse.jgit.errors.LargeObjectException;
+import org.eclipse.jgit.errors.MissingObjectException;
+import org.eclipse.jgit.lib.ObjectReader;
+import org.eclipse.jgit.lib.ProgressMonitor;
+import org.eclipse.jgit.util.TemporaryBuffer;
+
+class DeltaWindow {
+ private static final int NEXT_RES = 0;
+
+ private static final int NEXT_SRC = 1;
+
+ private final PackConfig config;
+
+ private final DeltaCache deltaCache;
+
+ private final ObjectReader reader;
+
+ private final DeltaWindowEntry[] window;
+
+ /** Maximum number of bytes to admit to the window at once. */
+ private final long maxMemory;
+
+ /** Maximum depth we should create for any delta chain. */
+ private final int maxDepth;
+
+ /** Amount of memory we have loaded right now. */
+ private long loaded;
+
+ // The object we are currently considering needs a lot of state:
+
+ /** Position of {@link #res} within {@link #window} array. */
+ private int resSlot;
+
+ /**
+ * Maximum delta chain depth the current object can have.
+ * <p>
+ * This can be smaller than {@link #maxDepth}.
+ */
+ private int resMaxDepth;
+
+ /** Window entry of the object we are currently considering. */
+ private DeltaWindowEntry res;
+
+ /** If we have a delta for {@link #res}, this is the shortest found yet. */
+ private TemporaryBuffer.Heap bestDelta;
+
+ /** If we have {@link #bestDelta}, the window position it was created by. */
+ private int bestSlot;
+
+ /** Used to compress cached deltas. */
+ private Deflater deflater;
+
+ DeltaWindow(PackConfig pc, DeltaCache dc, ObjectReader or) {
+ config = pc;
+ deltaCache = dc;
+ reader = or;
+
+ // C Git increases the window size supplied by the user by 1.
+ // We don't know why it does this, but if the user asks for
+ // window=10, it actually processes with window=11. Because
+ // the window size has the largest direct impact on the final
+ // pack file size, we match this odd behavior here to give us
+ // a better chance of producing a similar sized pack as C Git.
+ //
+ // We would prefer to directly honor the user's request since
+ // PackWriter has a minimum of 2 for the window size, but then
+ // users might complain that JGit is creating a bigger pack file.
+ //
+ window = new DeltaWindowEntry[config.getDeltaSearchWindowSize() + 1];
+ for (int i = 0; i < window.length; i++)
+ window[i] = new DeltaWindowEntry();
+
+ maxMemory = config.getDeltaSearchMemoryLimit();
+ maxDepth = config.getMaxDeltaDepth();
+ }
+
+ void search(ProgressMonitor monitor, ObjectToPack[] toSearch, int off,
+ int cnt) throws IOException {
+ try {
+ for (int end = off + cnt; off < end; off++) {
+ monitor.update(1);
+
+ res = window[resSlot];
+ if (0 < maxMemory) {
+ clear(res);
+ int tail = next(resSlot);
+ final long need = estimateSize(toSearch[off]);
+ while (maxMemory < loaded + need && tail != resSlot) {
+ clear(window[tail]);
+ tail = next(tail);
+ }
+ }
+ res.set(toSearch[off]);
+
+ if (res.object.isDoNotDelta()) {
+ // PackWriter marked edge objects with the
+ // do-not-delta flag. They are the only ones
+ // that appear in toSearch with it set, but
+ // we don't actually want to make a delta for
+ // them, just need to push them into the window
+ // so they can be read by other objects.
+ //
+ keepInWindow();
+ } else {
+ // Search for a delta for the current window slot.
+ //
+ search();
+ }
+ }
+ } finally {
+ if (deflater != null)
+ deflater.end();
+ }
+ }
+
+ private static long estimateSize(ObjectToPack ent) {
+ return DeltaIndex.estimateIndexSize(ent.getWeight());
+ }
+
+ private void clear(DeltaWindowEntry ent) {
+ if (ent.index != null)
+ loaded -= ent.index.getIndexSize();
+ else if (res.buffer != null)
+ loaded -= ent.buffer.length;
+ ent.set(null);
+ }
+
+ private void search() throws IOException {
+ // TODO(spearce) If the object is used as a base for other
+ // objects in this pack we should limit the depth we create
+ // for ourselves to be the remainder of our longest dependent
+ // chain and the configured maximum depth. This can happen
+ // when the dependents are being reused out a pack, but we
+ // cannot be because we are near the edge of a thin pack.
+ //
+ resMaxDepth = maxDepth;
+
+ // Loop through the window backwards, considering every entry.
+ // This lets us look at the bigger objects that came before.
+ //
+ for (int srcSlot = prior(resSlot); srcSlot != resSlot; srcSlot = prior(srcSlot)) {
+ DeltaWindowEntry src = window[srcSlot];
+ if (src.empty())
+ break;
+ if (delta(src, srcSlot) == NEXT_RES) {
+ bestDelta = null;
+ return;
+ }
+ }
+
+ // We couldn't find a suitable delta for this object, but it may
+ // still be able to act as a base for another one.
+ //
+ if (bestDelta == null) {
+ keepInWindow();
+ return;
+ }
+
+ // Select this best matching delta as the base for the object.
+ //
+ ObjectToPack srcObj = window[bestSlot].object;
+ ObjectToPack resObj = res.object;
+ if (srcObj.isDoNotDelta()) {
+ // The source (the delta base) is an edge object outside of the
+ // pack. Its part of the common base set that the peer already
+ // has on hand, so we don't want to send it. We have to store
+ // an ObjectId and *NOT* an ObjectToPack for the base to ensure
+ // the base isn't included in the outgoing pack file.
+ //
+ resObj.setDeltaBase(srcObj.copy());
+ } else {
+ // The base is part of the pack we are sending, so it should be
+ // a direct pointer to the base.
+ //
+ resObj.setDeltaBase(srcObj);
+ }
+ resObj.setDeltaDepth(srcObj.getDeltaDepth() + 1);
+ resObj.clearReuseAsIs();
+ cacheDelta(srcObj, resObj);
+
+ // Discard the cached best result, otherwise it leaks.
+ //
+ bestDelta = null;
+
+ // If this should be the end of a chain, don't keep
+ // it in the window. Just move on to the next object.
+ //
+ if (resObj.getDeltaDepth() == maxDepth)
+ return;
+
+ shuffleBaseUpInPriority();
+ keepInWindow();
+ }
+
+ private int delta(final DeltaWindowEntry src, final int srcSlot)
+ throws IOException {
+ // Objects must use only the same type as their delta base.
+ // If we are looking at something where that isn't true we
+ // have exhausted everything of the correct type and should
+ // move on to the next thing to examine.
+ //
+ if (src.type() != res.type()) {
+ keepInWindow();
+ return NEXT_RES;
+ }
+
+ // Only consider a source with a short enough delta chain.
+ if (src.depth() > resMaxDepth)
+ return NEXT_SRC;
+
+ // Estimate a reasonable upper limit on delta size.
+ int msz = deltaSizeLimit(res, resMaxDepth, src);
+ if (msz <= 8)
+ return NEXT_SRC;
+
+ // If we have to insert a lot to make this work, find another.
+ if (res.size() - src.size() > msz)
+ return NEXT_SRC;
+
+ // If the sizes are radically different, this is a bad pairing.
+ if (res.size() < src.size() / 16)
+ return NEXT_SRC;
+
+ DeltaIndex srcIndex;
+ try {
+ srcIndex = index(src);
+ } catch (LargeObjectException tooBig) {
+ // If the source is too big to work on, skip it.
+ dropFromWindow(srcSlot);
+ return NEXT_SRC;
+ } catch (IOException notAvailable) {
+ if (src.object.isDoNotDelta()) {
+ // This is an edge that is suddenly not available.
+ dropFromWindow(srcSlot);
+ return NEXT_SRC;
+ } else {
+ throw notAvailable;
+ }
+ }
+
+ byte[] resBuf;
+ try {
+ resBuf = buffer(res);
+ } catch (LargeObjectException tooBig) {
+ // If its too big, move on to another item.
+ return NEXT_RES;
+ }
+
+ // If we already have a delta for the current object, abort
+ // encoding early if this new pairing produces a larger delta.
+ if (bestDelta != null && bestDelta.length() < msz)
+ msz = (int) bestDelta.length();
+
+ TemporaryBuffer.Heap delta = new TemporaryBuffer.Heap(msz);
+ try {
+ if (!srcIndex.encode(delta, resBuf, msz))
+ return NEXT_SRC;
+ } catch (IOException deltaTooBig) {
+ // This only happens when the heap overflows our limit.
+ return NEXT_SRC;
+ }
+
+ if (isBetterDelta(src, delta)) {
+ bestDelta = delta;
+ bestSlot = srcSlot;
+ }
+
+ return NEXT_SRC;
+ }
+
+ private void cacheDelta(ObjectToPack srcObj, ObjectToPack resObj) {
+ if (Integer.MAX_VALUE < bestDelta.length())
+ return;
+
+ int rawsz = (int) bestDelta.length();
+ if (deltaCache.canCache(rawsz, srcObj, resObj)) {
+ try {
+ byte[] zbuf = new byte[deflateBound(rawsz)];
+
+ ZipStream zs = new ZipStream(deflater(), zbuf);
+ bestDelta.writeTo(zs, null);
+ bestDelta = null;
+ int len = zs.finish();
+
+ resObj.setCachedDelta(deltaCache.cache(zbuf, len, rawsz));
+ resObj.setCachedSize(rawsz);
+ } catch (IOException err) {
+ deltaCache.credit(rawsz);
+ } catch (OutOfMemoryError err) {
+ deltaCache.credit(rawsz);
+ }
+ }
+ }
+
+ private static int deflateBound(int insz) {
+ return insz + ((insz + 7) >> 3) + ((insz + 63) >> 6) + 11;
+ }
+
+ private void shuffleBaseUpInPriority() {
+ // Shuffle the entire window so that the best match we just used
+ // is at our current index, and our current object is at the index
+ // before it. Slide any entries in between to make space.
+ //
+ window[resSlot] = window[bestSlot];
+
+ DeltaWindowEntry next = res;
+ int slot = prior(resSlot);
+ for (; slot != bestSlot; slot = prior(slot)) {
+ DeltaWindowEntry e = window[slot];
+ window[slot] = next;
+ next = e;
+ }
+ window[slot] = next;
+ }
+
+ private void keepInWindow() {
+ resSlot = next(resSlot);
+ }
+
+ private int next(int slot) {
+ if (++slot == window.length)
+ return 0;
+ return slot;
+ }
+
+ private int prior(int slot) {
+ if (slot == 0)
+ return window.length - 1;
+ return slot - 1;
+ }
+
+ private void dropFromWindow(@SuppressWarnings("unused") int srcSlot) {
+ // We should drop the current source entry from the window,
+ // it is somehow invalid for us to work with.
+ }
+
+ private boolean isBetterDelta(DeltaWindowEntry src,
+ TemporaryBuffer.Heap resDelta) {
+ if (bestDelta == null)
+ return true;
+
+ // If both delta sequences are the same length, use the one
+ // that has a shorter delta chain since it would be faster
+ // to access during reads.
+ //
+ if (resDelta.length() == bestDelta.length())
+ return src.depth() < window[bestSlot].depth();
+
+ return resDelta.length() < bestDelta.length();
+ }
+
+ private static int deltaSizeLimit(DeltaWindowEntry res, int maxDepth,
+ DeltaWindowEntry src) {
+ // Ideally the delta is at least 50% of the original size,
+ // but we also want to account for delta header overhead in
+ // the pack file (to point to the delta base) so subtract off
+ // some of those header bytes from the limit.
+ //
+ final int limit = res.size() / 2 - 20;
+
+ // Distribute the delta limit over the entire chain length.
+ // This is weighted such that deeper items in the chain must
+ // be even smaller than if they were earlier in the chain, as
+ // they cost significantly more to unpack due to the increased
+ // number of recursive unpack calls.
+ //
+ final int remainingDepth = maxDepth - src.depth();
+ return (limit * remainingDepth) / maxDepth;
+ }
+
+ private DeltaIndex index(DeltaWindowEntry ent)
+ throws MissingObjectException, IncorrectObjectTypeException,
+ IOException, LargeObjectException {
+ DeltaIndex idx = ent.index;
+ if (idx == null) {
+ try {
+ idx = new DeltaIndex(buffer(ent));
+ } catch (OutOfMemoryError noMemory) {
+ LargeObjectException e = new LargeObjectException(ent.object);
+ e.initCause(noMemory);
+ throw e;
+ }
+ if (0 < maxMemory)
+ loaded += idx.getIndexSize() - idx.getSourceSize();
+ ent.index = idx;
+ }
+ return idx;
+ }
+
+ private byte[] buffer(DeltaWindowEntry ent) throws MissingObjectException,
+ IncorrectObjectTypeException, IOException, LargeObjectException {
+ byte[] buf = ent.buffer;
+ if (buf == null) {
+ buf = PackWriter.buffer(config, reader, ent.object);
+ if (0 < maxMemory)
+ loaded += buf.length;
+ ent.buffer = buf;
+ }
+ return buf;
+ }
+
+ private Deflater deflater() {
+ if (deflater == null)
+ deflater = new Deflater(config.getCompressionLevel());
+ else
+ deflater.reset();
+ return deflater;
+ }
+
+ static final class ZipStream extends OutputStream {
+ private final Deflater deflater;
+
+ private final byte[] zbuf;
+
+ private int outPtr;
+
+ ZipStream(Deflater deflater, byte[] zbuf) {
+ this.deflater = deflater;
+ this.zbuf = zbuf;
+ }
+
+ int finish() throws IOException {
+ deflater.finish();
+ for (;;) {
+ if (outPtr == zbuf.length)
+ throw new EOFException();
+
+ int n = deflater.deflate(zbuf, outPtr, zbuf.length - outPtr);
+ if (n == 0) {
+ if (deflater.finished())
+ return outPtr;
+ throw new IOException();
+ }
+ outPtr += n;
+ }
+ }
+
+ @Override
+ public void write(byte[] b, int off, int len) throws IOException {
+ deflater.setInput(b, off, len);
+ for (;;) {
+ if (outPtr == zbuf.length)
+ throw new EOFException();
+
+ int n = deflater.deflate(zbuf, outPtr, zbuf.length - outPtr);
+ if (n == 0) {
+ if (deflater.needsInput())
+ break;
+ throw new IOException();
+ }
+ outPtr += n;
+ }
+ }
+
+ @Override
+ public void write(int b) throws IOException {
+ throw new UnsupportedOperationException();
+ }
+ }
+}
diff --git a/org.eclipse.jgit/src/org/eclipse/jgit/lib/RepositoryAdapter.java b/org.eclipse.jgit/src/org/eclipse/jgit/storage/pack/DeltaWindowEntry.java
similarity index 69%
copy from org.eclipse.jgit/src/org/eclipse/jgit/lib/RepositoryAdapter.java
copy to org.eclipse.jgit/src/org/eclipse/jgit/storage/pack/DeltaWindowEntry.java
index e43c33a..0f1e632 100644
--- a/org.eclipse.jgit/src/org/eclipse/jgit/lib/RepositoryAdapter.java
+++ b/org.eclipse.jgit/src/org/eclipse/jgit/storage/pack/DeltaWindowEntry.java
@@ -1,5 +1,5 @@
/*
- * Copyright (C) 2008, Robin Rosenberg <robin.rosenberg@dewire.com>
+ * Copyright (C) 2010, Google Inc.
* and other copyright owners as documented in the project's IP log.
*
* This program and the accompanying materials are made available
@@ -41,20 +41,40 @@
* ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
*/
-package org.eclipse.jgit.lib;
+package org.eclipse.jgit.storage.pack;
-/**
- * A default {@link RepositoryListener} that does nothing except invoke an
- * optional general method for any repository change.
- */
-public class RepositoryAdapter implements RepositoryListener {
+class DeltaWindowEntry {
+ ObjectToPack object;
- public void indexChanged(final IndexChangedEvent e) {
- // Empty
+ /** Complete contents of this object. Lazily loaded. */
+ byte[] buffer;
+
+ /** Index of this object's content, to encode other deltas. Lazily loaded. */
+ DeltaIndex index;
+
+ void set(ObjectToPack object) {
+ this.object = object;
+ this.index = null;
+ this.buffer = null;
}
- public void refsChanged(final RefsChangedEvent e) {
- // Empty
+ /** @return current delta chain depth of this object. */
+ int depth() {
+ return object.getDeltaDepth();
}
+ /** @return type of the object in this window entry. */
+ int type() {
+ return object.getType();
+ }
+
+ /** @return estimated unpacked size of the object, in bytes . */
+ int size() {
+ return object.getWeight();
+ }
+
+ /** @return true if there is no object stored in this entry. */
+ boolean empty() {
+ return object == null;
+ }
}
diff --git a/org.eclipse.jgit/src/org/eclipse/jgit/storage/pack/ObjectReuseAsIs.java b/org.eclipse.jgit/src/org/eclipse/jgit/storage/pack/ObjectReuseAsIs.java
new file mode 100644
index 0000000..a815e93
--- /dev/null
+++ b/org.eclipse.jgit/src/org/eclipse/jgit/storage/pack/ObjectReuseAsIs.java
@@ -0,0 +1,136 @@
+/*
+ * Copyright (C) 2010, Google Inc.
+ * and other copyright owners as documented in the project's IP log.
+ *
+ * This program and the accompanying materials are made available
+ * under the terms of the Eclipse Distribution License v1.0 which
+ * accompanies this distribution, is reproduced below, and is
+ * available at http://www.eclipse.org/org/documents/edl-v10.php
+ *
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or
+ * without modification, are permitted provided that the following
+ * conditions are met:
+ *
+ * - Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ *
+ * - Redistributions in binary form must reproduce the above
+ * copyright notice, this list of conditions and the following
+ * disclaimer in the documentation and/or other materials provided
+ * with the distribution.
+ *
+ * - Neither the name of the Eclipse Foundation, Inc. nor the
+ * names of its contributors may be used to endorse or promote
+ * products derived from this software without specific prior
+ * written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND
+ * CONTRIBUTORS "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES,
+ * INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
+ * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR
+ * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
+ * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT
+ * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
+ * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER
+ * CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT,
+ * STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
+ * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF
+ * ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ */
+
+package org.eclipse.jgit.storage.pack;
+
+import java.io.IOException;
+
+import org.eclipse.jgit.errors.MissingObjectException;
+import org.eclipse.jgit.errors.StoredObjectRepresentationNotAvailableException;
+import org.eclipse.jgit.lib.ObjectReader;
+import org.eclipse.jgit.revwalk.RevObject;
+
+/**
+ * Extension of {@link ObjectReader} that supports reusing objects in packs.
+ * <p>
+ * {@code ObjectReader} implementations may also optionally implement this
+ * interface to support {@link PackWriter} with a means of copying an object
+ * that is already in pack encoding format directly into the output stream,
+ * without incurring decompression and recompression overheads.
+ */
+public interface ObjectReuseAsIs {
+ /**
+ * Allocate a new {@code PackWriter} state structure for an object.
+ * <p>
+ * {@link PackWriter} allocates these objects to keep track of the
+ * per-object state, and how to load the objects efficiently into the
+ * generated stream. Implementers may subclass this type with additional
+ * object state, such as to remember what file and offset contains the
+ * object's pack encoded data.
+ *
+ * @param obj
+ * identity of the object that will be packed. The object's
+ * parsed status is undefined here. Implementers must not rely on
+ * the object being parsed.
+ * @return a new instance for this object.
+ */
+ public ObjectToPack newObjectToPack(RevObject obj);
+
+ /**
+ * Select the best object representation for a packer.
+ * <p>
+ * Implementations should iterate through all available representations of
+ * an object, and pass them in turn to the PackWriter though
+ * {@link PackWriter#select(ObjectToPack, StoredObjectRepresentation)} so
+ * the writer can select the most suitable representation to reuse into the
+ * output stream.
+ *
+ * @param packer
+ * the packer that will write the object in the near future.
+ * @param otp
+ * the object to pack.
+ * @throws MissingObjectException
+ * there is no representation available for the object, as it is
+ * no longer in the repository. Packing will abort.
+ * @throws IOException
+ * the repository cannot be accessed. Packing will abort.
+ */
+ public void selectObjectRepresentation(PackWriter packer, ObjectToPack otp)
+ throws IOException, MissingObjectException;
+
+ /**
+ * Output a previously selected representation.
+ * <p>
+ * {@code PackWriter} invokes this method only if a representation
+ * previously given to it by {@code selectObjectRepresentation} was chosen
+ * for reuse into the output stream. The {@code otp} argument is an instance
+ * created by this reader's own {@code newObjectToPack}, and the
+ * representation data saved within it also originated from this reader.
+ * <p>
+ * Implementors must write the object header before copying the raw data to
+ * the output stream. The typical implementation is like:
+ *
+ * <pre>
+ * MyToPack mtp = (MyToPack) otp;
+ * byte[] raw = validate(mtp); // throw SORNAE here, if at all
+ * out.writeHeader(mtp, mtp.inflatedSize);
+ * out.write(raw);
+ * </pre>
+ *
+ * @param out
+ * stream the object should be written to.
+ * @param otp
+ * the object's saved representation information.
+ * @throws StoredObjectRepresentationNotAvailableException
+ * the previously selected representation is no longer
+ * available. If thrown before {@code out.writeHeader} the pack
+ * writer will try to find another representation, and write
+ * that one instead. If throw after {@code out.writeHeader},
+ * packing will abort.
+ * @throws IOException
+ * the stream's write method threw an exception. Packing will
+ * abort.
+ */
+ public void copyObjectAsIs(PackOutputStream out, ObjectToPack otp)
+ throws IOException, StoredObjectRepresentationNotAvailableException;
+}
diff --git a/org.eclipse.jgit/src/org/eclipse/jgit/storage/pack/ObjectToPack.java b/org.eclipse.jgit/src/org/eclipse/jgit/storage/pack/ObjectToPack.java
new file mode 100644
index 0000000..70188a3
--- /dev/null
+++ b/org.eclipse.jgit/src/org/eclipse/jgit/storage/pack/ObjectToPack.java
@@ -0,0 +1,320 @@
+/*
+ * Copyright (C) 2008-2010, Google Inc.
+ * Copyright (C) 2008, Marek Zawirski <marek.zawirski@gmail.com>
+ * and other copyright owners as documented in the project's IP log.
+ *
+ * This program and the accompanying materials are made available
+ * under the terms of the Eclipse Distribution License v1.0 which
+ * accompanies this distribution, is reproduced below, and is
+ * available at http://www.eclipse.org/org/documents/edl-v10.php
+ *
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or
+ * without modification, are permitted provided that the following
+ * conditions are met:
+ *
+ * - Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ *
+ * - Redistributions in binary form must reproduce the above
+ * copyright notice, this list of conditions and the following
+ * disclaimer in the documentation and/or other materials provided
+ * with the distribution.
+ *
+ * - Neither the name of the Eclipse Foundation, Inc. nor the
+ * names of its contributors may be used to endorse or promote
+ * products derived from this software without specific prior
+ * written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND
+ * CONTRIBUTORS "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES,
+ * INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
+ * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR
+ * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
+ * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT
+ * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
+ * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER
+ * CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT,
+ * STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
+ * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF
+ * ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ */
+
+package org.eclipse.jgit.storage.pack;
+
+import org.eclipse.jgit.lib.AnyObjectId;
+import org.eclipse.jgit.lib.Constants;
+import org.eclipse.jgit.lib.ObjectId;
+import org.eclipse.jgit.revwalk.RevObject;
+import org.eclipse.jgit.transport.PackedObjectInfo;
+
+/**
+ * Per-object state used by {@link PackWriter}.
+ * <p>
+ * {@code PackWriter} uses this class to track the things it needs to include in
+ * the newly generated pack file, and how to efficiently obtain the raw data for
+ * each object as they are written to the output stream.
+ */
+public class ObjectToPack extends PackedObjectInfo {
+ private static final int WANT_WRITE = 1 << 0;
+
+ private static final int REUSE_AS_IS = 1 << 1;
+
+ private static final int DO_NOT_DELTA = 1 << 2;
+
+ private static final int TYPE_SHIFT = 5;
+
+ private static final int DELTA_SHIFT = 8;
+
+ private static final int NON_DELTA_MASK = 0xff;
+
+ /** Other object being packed that this will delta against. */
+ private ObjectId deltaBase;
+
+ /**
+ * Bit field, from bit 0 to bit 31:
+ * <ul>
+ * <li>1 bit: wantWrite</li>
+ * <li>1 bit: canReuseAsIs</li>
+ * <li>1 bit: doNotDelta</li>
+ * <li>2 bits: unused</li>
+ * <li>3 bits: type</li>
+ * <li>--</li>
+ * <li>24 bits: deltaDepth</li>
+ * </ul>
+ */
+ private int flags;
+
+ /** Hash of the object's tree path. */
+ private int pathHash;
+
+ /** If present, deflated delta instruction stream for this object. */
+ private DeltaCache.Ref cachedDelta;
+
+ /**
+ * Construct for the specified object id.
+ *
+ * @param src
+ * object id of object for packing
+ * @param type
+ * real type code of the object, not its in-pack type.
+ */
+ public ObjectToPack(AnyObjectId src, final int type) {
+ super(src);
+ flags = type << TYPE_SHIFT;
+ }
+
+ /**
+ * Construct for the specified object.
+ *
+ * @param obj
+ * identity of the object that will be packed. The object's
+ * parsed status is undefined here. Implementers must not rely on
+ * the object being parsed.
+ */
+ public ObjectToPack(RevObject obj) {
+ this(obj, obj.getType());
+ }
+
+ /**
+ * @return delta base object id if object is going to be packed in delta
+ * representation; null otherwise - if going to be packed as a
+ * whole object.
+ */
+ ObjectId getDeltaBaseId() {
+ return deltaBase;
+ }
+
+ /**
+ * @return delta base object to pack if object is going to be packed in
+ * delta representation and delta is specified as object to
+ * pack; null otherwise - if going to be packed as a whole
+ * object or delta base is specified only as id.
+ */
+ ObjectToPack getDeltaBase() {
+ if (deltaBase instanceof ObjectToPack)
+ return (ObjectToPack) deltaBase;
+ return null;
+ }
+
+ /**
+ * Set delta base for the object. Delta base set by this method is used
+ * by {@link PackWriter} to write object - determines its representation
+ * in a created pack.
+ *
+ * @param deltaBase
+ * delta base object or null if object should be packed as a
+ * whole object.
+ *
+ */
+ void setDeltaBase(ObjectId deltaBase) {
+ this.deltaBase = deltaBase;
+ }
+
+ void setCachedDelta(DeltaCache.Ref data){
+ cachedDelta = data;
+ }
+
+ DeltaCache.Ref popCachedDelta() {
+ DeltaCache.Ref r = cachedDelta;
+ if (r != null)
+ cachedDelta = null;
+ return r;
+ }
+
+ void clearDeltaBase() {
+ this.deltaBase = null;
+
+ if (cachedDelta != null) {
+ cachedDelta.clear();
+ cachedDelta.enqueue();
+ cachedDelta = null;
+ }
+ }
+
+ /**
+ * @return true if object is going to be written as delta; false
+ * otherwise.
+ */
+ boolean isDeltaRepresentation() {
+ return deltaBase != null;
+ }
+
+ /**
+ * Check if object is already written in a pack. This information is
+ * used to achieve delta-base precedence in a pack file.
+ *
+ * @return true if object is already written; false otherwise.
+ */
+ boolean isWritten() {
+ return getOffset() != 0;
+ }
+
+ int getType() {
+ return (flags >> TYPE_SHIFT) & 0x7;
+ }
+
+ int getDeltaDepth() {
+ return flags >>> DELTA_SHIFT;
+ }
+
+ void setDeltaDepth(int d) {
+ flags = (d << DELTA_SHIFT) | (flags & NON_DELTA_MASK);
+ }
+
+ boolean wantWrite() {
+ return (flags & WANT_WRITE) != 0;
+ }
+
+ void markWantWrite() {
+ flags |= WANT_WRITE;
+ }
+
+ boolean isReuseAsIs() {
+ return (flags & REUSE_AS_IS) != 0;
+ }
+
+ void setReuseAsIs() {
+ flags |= REUSE_AS_IS;
+ }
+
+ /**
+ * Forget the reuse information previously stored.
+ * <p>
+ * Implementations may subclass this method, but they must also invoke the
+ * super version with {@code super.clearReuseAsIs()} to ensure the flag is
+ * properly cleared for the writer.
+ */
+ protected void clearReuseAsIs() {
+ flags &= ~REUSE_AS_IS;
+ }
+
+ boolean isDoNotDelta() {
+ return (flags & DO_NOT_DELTA) != 0;
+ }
+
+ void setDoNotDelta(boolean noDelta) {
+ if (noDelta)
+ flags |= DO_NOT_DELTA;
+ else
+ flags &= ~DO_NOT_DELTA;
+ }
+
+ int getFormat() {
+ if (isReuseAsIs()) {
+ if (isDeltaRepresentation())
+ return StoredObjectRepresentation.PACK_DELTA;
+ return StoredObjectRepresentation.PACK_WHOLE;
+ }
+ return StoredObjectRepresentation.FORMAT_OTHER;
+ }
+
+ // Overload weight into CRC since we don't need them at the same time.
+ int getWeight() {
+ return getCRC();
+ }
+
+ void setWeight(int weight) {
+ setCRC(weight);
+ }
+
+ int getPathHash() {
+ return pathHash;
+ }
+
+ void setPathHash(int hc) {
+ pathHash = hc;
+ }
+
+ int getCachedSize() {
+ return pathHash;
+ }
+
+ void setCachedSize(int sz) {
+ pathHash = sz;
+ }
+
+ /**
+ * Remember a specific representation for reuse at a later time.
+ * <p>
+ * Implementers should remember the representation chosen, so it can be
+ * reused at a later time. {@link PackWriter} may invoke this method
+ * multiple times for the same object, each time saving the current best
+ * representation found.
+ *
+ * @param ref
+ * the object representation.
+ */
+ public void select(StoredObjectRepresentation ref) {
+ // Empty by default.
+ }
+
+ @Override
+ public String toString() {
+ StringBuilder buf = new StringBuilder();
+ buf.append("ObjectToPack[");
+ buf.append(Constants.typeString(getType()));
+ buf.append(" ");
+ buf.append(name());
+ if (wantWrite())
+ buf.append(" wantWrite");
+ if (isReuseAsIs())
+ buf.append(" reuseAsIs");
+ if (isDoNotDelta())
+ buf.append(" doNotDelta");
+ if (getDeltaDepth() > 0)
+ buf.append(" depth=" + getDeltaDepth());
+ if (isDeltaRepresentation()) {
+ if (getDeltaBase() != null)
+ buf.append(" base=inpack:" + getDeltaBase().name());
+ else
+ buf.append(" base=edge:" + getDeltaBaseId().name());
+ }
+ if (isWritten())
+ buf.append(" offset=" + getOffset());
+ buf.append("]");
+ return buf.toString();
+ }
+}
diff --git a/org.eclipse.jgit/src/org/eclipse/jgit/storage/pack/PackConfig.java b/org.eclipse.jgit/src/org/eclipse/jgit/storage/pack/PackConfig.java
new file mode 100644
index 0000000..9bda76d
--- /dev/null
+++ b/org.eclipse.jgit/src/org/eclipse/jgit/storage/pack/PackConfig.java
@@ -0,0 +1,621 @@
+/*
+ * Copyright (C) 2008-2010, Google Inc.
+ * Copyright (C) 2008, Marek Zawirski <marek.zawirski@gmail.com>
+ * and other copyright owners as documented in the project's IP log.
+ *
+ * This program and the accompanying materials are made available
+ * under the terms of the Eclipse Distribution License v1.0 which
+ * accompanies this distribution, is reproduced below, and is
+ * available at http://www.eclipse.org/org/documents/edl-v10.php
+ *
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or
+ * without modification, are permitted provided that the following
+ * conditions are met:
+ *
+ * - Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ *
+ * - Redistributions in binary form must reproduce the above
+ * copyright notice, this list of conditions and the following
+ * disclaimer in the documentation and/or other materials provided
+ * with the distribution.
+ *
+ * - Neither the name of the Eclipse Foundation, Inc. nor the
+ * names of its contributors may be used to endorse or promote
+ * products derived from this software without specific prior
+ * written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND
+ * CONTRIBUTORS "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES,
+ * INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
+ * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR
+ * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
+ * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT
+ * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
+ * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER
+ * CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT,
+ * STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
+ * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF
+ * ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ */
+
+package org.eclipse.jgit.storage.pack;
+
+import java.util.concurrent.Executor;
+import java.util.zip.Deflater;
+
+import org.eclipse.jgit.lib.Config;
+import org.eclipse.jgit.lib.Repository;
+import org.eclipse.jgit.storage.file.PackIndexWriter;
+
+/**
+ * Configuration used by a {@link PackWriter} when constructing the stream.
+ *
+ * A configuration may be modified once created, but should not be modified
+ * while it is being used by a PackWriter. If a configuration is not modified it
+ * is safe to share the same configuration instance between multiple concurrent
+ * threads executing different PackWriters.
+ */
+public class PackConfig {
+ /**
+ * Default value of deltas reuse option: {@value}
+ *
+ * @see #setReuseDeltas(boolean)
+ */
+ public static final boolean DEFAULT_REUSE_DELTAS = true;
+
+ /**
+ * Default value of objects reuse option: {@value}
+ *
+ * @see #setReuseObjects(boolean)
+ */
+ public static final boolean DEFAULT_REUSE_OBJECTS = true;
+
+ /**
+ * Default value of delta compress option: {@value}
+ *
+ * @see #setDeltaCompress(boolean)
+ */
+ public static final boolean DEFAULT_DELTA_COMPRESS = true;
+
+ /**
+ * Default value of delta base as offset option: {@value}
+ *
+ * @see #setDeltaBaseAsOffset(boolean)
+ */
+ public static final boolean DEFAULT_DELTA_BASE_AS_OFFSET = false;
+
+ /**
+ * Default value of maximum delta chain depth: {@value}
+ *
+ * @see #setMaxDeltaDepth(int)
+ */
+ public static final int DEFAULT_MAX_DELTA_DEPTH = 50;
+
+ /**
+ * Default window size during packing: {@value}
+ *
+ * @see #setDeltaSearchWindowSize(int)
+ */
+ public static final int DEFAULT_DELTA_SEARCH_WINDOW_SIZE = 10;
+
+ /**
+ * Default big file threshold: {@value}
+ *
+ * @see #setBigFileThreshold(long)
+ */
+ public static final long DEFAULT_BIG_FILE_THRESHOLD = 50 * 1024 * 1024;
+
+ /**
+ * Default delta cache size: {@value}
+ *
+ * @see #setDeltaCacheSize(long)
+ */
+ public static final long DEFAULT_DELTA_CACHE_SIZE = 50 * 1024 * 1024;
+
+ /**
+ * Default delta cache limit: {@value}
+ *
+ * @see #setDeltaCacheLimit(int)
+ */
+ public static final int DEFAULT_DELTA_CACHE_LIMIT = 100;
+
+ /**
+ * Default index version: {@value}
+ *
+ * @see #setIndexVersion(int)
+ */
+ public static final int DEFAULT_INDEX_VERSION = 2;
+
+
+ private int compressionLevel = Deflater.DEFAULT_COMPRESSION;
+
+ private boolean reuseDeltas = DEFAULT_REUSE_DELTAS;
+
+ private boolean reuseObjects = DEFAULT_REUSE_OBJECTS;
+
+ private boolean deltaBaseAsOffset = DEFAULT_DELTA_BASE_AS_OFFSET;
+
+ private boolean deltaCompress = DEFAULT_DELTA_COMPRESS;
+
+ private int maxDeltaDepth = DEFAULT_MAX_DELTA_DEPTH;
+
+ private int deltaSearchWindowSize = DEFAULT_DELTA_SEARCH_WINDOW_SIZE;
+
+ private long deltaSearchMemoryLimit;
+
+ private long deltaCacheSize = DEFAULT_DELTA_CACHE_SIZE;
+
+ private int deltaCacheLimit = DEFAULT_DELTA_CACHE_LIMIT;
+
+ private long bigFileThreshold = DEFAULT_BIG_FILE_THRESHOLD;
+
+ private int threads;
+
+ private Executor executor;
+
+ private int indexVersion = DEFAULT_INDEX_VERSION;
+
+
+ /** Create a default configuration. */
+ public PackConfig() {
+ // Fields are initialized to defaults.
+ }
+
+ /**
+ * Create a configuration honoring the repository's settings.
+ *
+ * @param db
+ * the repository to read settings from. The repository is not
+ * retained by the new configuration, instead its settings are
+ * copied during the constructor.
+ */
+ public PackConfig(Repository db) {
+ fromConfig(db.getConfig());
+ }
+
+ /**
+ * Create a configuration honoring settings in a {@link Config}.
+ *
+ * @param cfg
+ * the source to read settings from. The source is not retained
+ * by the new configuration, instead its settings are copied
+ * during the constructor.
+ */
+ public PackConfig(Config cfg) {
+ fromConfig(cfg);
+ }
+
+ /**
+ * Check whether to reuse deltas existing in repository.
+ *
+ * Default setting: {@value #DEFAULT_REUSE_DELTAS}
+ *
+ * @return true if object is configured to reuse deltas; false otherwise.
+ */
+ public boolean isReuseDeltas() {
+ return reuseDeltas;
+ }
+
+ /**
+ * Set reuse deltas configuration option for the writer.
+ *
+ * When enabled, writer will search for delta representation of object in
+ * repository and use it if possible. Normally, only deltas with base to
+ * another object existing in set of objects to pack will be used. The
+ * exception however is thin-packs where the base object may exist on the
+ * other side.
+ *
+ * When raw delta data is directly copied from a pack file, its checksum is
+ * computed to verify the data is not corrupt.
+ *
+ * Default setting: {@value #DEFAULT_REUSE_DELTAS}
+ *
+ * @param reuseDeltas
+ * boolean indicating whether or not try to reuse deltas.
+ */
+ public void setReuseDeltas(boolean reuseDeltas) {
+ this.reuseDeltas = reuseDeltas;
+ }
+
+ /**
+ * Checks whether to reuse existing objects representation in repository.
+ *
+ * Default setting: {@value #DEFAULT_REUSE_OBJECTS}
+ *
+ * @return true if writer is configured to reuse objects representation from
+ * pack; false otherwise.
+ */
+ public boolean isReuseObjects() {
+ return reuseObjects;
+ }
+
+ /**
+ * Set reuse objects configuration option for the writer.
+ *
+ * If enabled, writer searches for compressed representation in a pack file.
+ * If possible, compressed data is directly copied from such a pack file.
+ * Data checksum is verified.
+ *
+ * Default setting: {@value #DEFAULT_REUSE_OBJECTS}
+ *
+ * @param reuseObjects
+ * boolean indicating whether or not writer should reuse existing
+ * objects representation.
+ */
+ public void setReuseObjects(boolean reuseObjects) {
+ this.reuseObjects = reuseObjects;
+ }
+
+ /**
+ * True if writer can use offsets to point to a delta base.
+ *
+ * If true the writer may choose to use an offset to point to a delta base
+ * in the same pack, this is a newer style of reference that saves space.
+ * False if the writer has to use the older (and more compatible style) of
+ * storing the full ObjectId of the delta base.
+ *
+ * Default setting: {@value #DEFAULT_DELTA_BASE_AS_OFFSET}
+ *
+ * @return true if delta base is stored as an offset; false if it is stored
+ * as an ObjectId.
+ */
+ public boolean isDeltaBaseAsOffset() {
+ return deltaBaseAsOffset;
+ }
+
+ /**
+ * Set writer delta base format.
+ *
+ * Delta base can be written as an offset in a pack file (new approach
+ * reducing file size) or as an object id (legacy approach, compatible with
+ * old readers).
+ *
+ * Default setting: {@value #DEFAULT_DELTA_BASE_AS_OFFSET}
+ *
+ * @param deltaBaseAsOffset
+ * boolean indicating whether delta base can be stored as an
+ * offset.
+ */
+ public void setDeltaBaseAsOffset(boolean deltaBaseAsOffset) {
+ this.deltaBaseAsOffset = deltaBaseAsOffset;
+ }
+
+ /**
+ * Check whether the writer will create new deltas on the fly.
+ *
+ * Default setting: {@value #DEFAULT_DELTA_COMPRESS}
+ *
+ * @return true if the writer will create a new delta when either
+ * {@link #isReuseDeltas()} is false, or no suitable delta is
+ * available for reuse.
+ */
+ public boolean isDeltaCompress() {
+ return deltaCompress;
+ }
+
+ /**
+ * Set whether or not the writer will create new deltas on the fly.
+ *
+ * Default setting: {@value #DEFAULT_DELTA_COMPRESS}
+ *
+ * @param deltaCompress
+ * true to create deltas when {@link #isReuseDeltas()} is false,
+ * or when a suitable delta isn't available for reuse. Set to
+ * false to write whole objects instead.
+ */
+ public void setDeltaCompress(boolean deltaCompress) {
+ this.deltaCompress = deltaCompress;
+ }
+
+ /**
+ * Get maximum depth of delta chain set up for the writer.
+ *
+ * Generated chains are not longer than this value.
+ *
+ * Default setting: {@value #DEFAULT_MAX_DELTA_DEPTH}
+ *
+ * @return maximum delta chain depth.
+ */
+ public int getMaxDeltaDepth() {
+ return maxDeltaDepth;
+ }
+
+ /**
+ * Set up maximum depth of delta chain for the writer.
+ *
+ * Generated chains are not longer than this value. Too low value causes low
+ * compression level, while too big makes unpacking (reading) longer.
+ *
+ * Default setting: {@value #DEFAULT_MAX_DELTA_DEPTH}
+ *
+ * @param maxDeltaDepth
+ * maximum delta chain depth.
+ */
+ public void setMaxDeltaDepth(int maxDeltaDepth) {
+ this.maxDeltaDepth = maxDeltaDepth;
+ }
+
+ /**
+ * Get the number of objects to try when looking for a delta base.
+ *
+ * This limit is per thread, if 4 threads are used the actual memory used
+ * will be 4 times this value.
+ *
+ * Default setting: {@value #DEFAULT_DELTA_SEARCH_WINDOW_SIZE}
+ *
+ * @return the object count to be searched.
+ */
+ public int getDeltaSearchWindowSize() {
+ return deltaSearchWindowSize;
+ }
+
+ /**
+ * Set the number of objects considered when searching for a delta base.
+ *
+ * Default setting: {@value #DEFAULT_DELTA_SEARCH_WINDOW_SIZE}
+ *
+ * @param objectCount
+ * number of objects to search at once. Must be at least 2.
+ */
+ public void setDeltaSearchWindowSize(int objectCount) {
+ if (objectCount <= 2)
+ setDeltaCompress(false);
+ else
+ deltaSearchWindowSize = objectCount;
+ }
+
+ /**
+ * Get maximum number of bytes to put into the delta search window.
+ *
+ * Default setting is 0, for an unlimited amount of memory usage. Actual
+ * memory used is the lower limit of either this setting, or the sum of
+ * space used by at most {@link #getDeltaSearchWindowSize()} objects.
+ *
+ * This limit is per thread, if 4 threads are used the actual memory limit
+ * will be 4 times this value.
+ *
+ * @return the memory limit.
+ */
+ public long getDeltaSearchMemoryLimit() {
+ return deltaSearchMemoryLimit;
+ }
+
+ /**
+ * Set the maximum number of bytes to put into the delta search window.
+ *
+ * Default setting is 0, for an unlimited amount of memory usage. If the
+ * memory limit is reached before {@link #getDeltaSearchWindowSize()} the
+ * window size is temporarily lowered.
+ *
+ * @param memoryLimit
+ * Maximum number of bytes to load at once, 0 for unlimited.
+ */
+ public void setDeltaSearchMemoryLimit(long memoryLimit) {
+ deltaSearchMemoryLimit = memoryLimit;
+ }
+
+ /**
+ * Get the size of the in-memory delta cache.
+ *
+ * This limit is for the entire writer, even if multiple threads are used.
+ *
+ * Default setting: {@value #DEFAULT_DELTA_CACHE_SIZE}
+ *
+ * @return maximum number of bytes worth of delta data to cache in memory.
+ * If 0 the cache is infinite in size (up to the JVM heap limit
+ * anyway). A very tiny size such as 1 indicates the cache is
+ * effectively disabled.
+ */
+ public long getDeltaCacheSize() {
+ return deltaCacheSize;
+ }
+
+ /**
+ * Set the maximum number of bytes of delta data to cache.
+ *
+ * During delta search, up to this many bytes worth of small or hard to
+ * compute deltas will be stored in memory. This cache speeds up writing by
+ * allowing the cached entry to simply be dumped to the output stream.
+ *
+ * Default setting: {@value #DEFAULT_DELTA_CACHE_SIZE}
+ *
+ * @param size
+ * number of bytes to cache. Set to 0 to enable an infinite
+ * cache, set to 1 (an impossible size for any delta) to disable
+ * the cache.
+ */
+ public void setDeltaCacheSize(long size) {
+ deltaCacheSize = size;
+ }
+
+ /**
+ * Maximum size in bytes of a delta to cache.
+ *
+ * Default setting: {@value #DEFAULT_DELTA_CACHE_LIMIT}
+ *
+ * @return maximum size (in bytes) of a delta that should be cached.
+ */
+ public int getDeltaCacheLimit() {
+ return deltaCacheLimit;
+ }
+
+ /**
+ * Set the maximum size of a delta that should be cached.
+ *
+ * During delta search, any delta smaller than this size will be cached, up
+ * to the {@link #getDeltaCacheSize()} maximum limit. This speeds up writing
+ * by allowing these cached deltas to be output as-is.
+ *
+ * Default setting: {@value #DEFAULT_DELTA_CACHE_LIMIT}
+ *
+ * @param size
+ * maximum size (in bytes) of a delta to be cached.
+ */
+ public void setDeltaCacheLimit(int size) {
+ deltaCacheLimit = size;
+ }
+
+ /**
+ * Get the maximum file size that will be delta compressed.
+ *
+ * Files bigger than this setting will not be delta compressed, as they are
+ * more than likely already highly compressed binary data files that do not
+ * delta compress well, such as MPEG videos.
+ *
+ * Default setting: {@value #DEFAULT_BIG_FILE_THRESHOLD}
+ *
+ * @return the configured big file threshold.
+ */
+ public long getBigFileThreshold() {
+ return bigFileThreshold;
+ }
+
+ /**
+ * Set the maximum file size that should be considered for deltas.
+ *
+ * Default setting: {@value #DEFAULT_BIG_FILE_THRESHOLD}
+ *
+ * @param bigFileThreshold
+ * the limit, in bytes.
+ */
+ public void setBigFileThreshold(long bigFileThreshold) {
+ this.bigFileThreshold = bigFileThreshold;
+ }
+
+ /**
+ * Get the compression level applied to objects in the pack.
+ *
+ * Default setting: {@value java.util.zip.Deflater#DEFAULT_COMPRESSION}
+ *
+ * @return current compression level, see {@link java.util.zip.Deflater}.
+ */
+ public int getCompressionLevel() {
+ return compressionLevel;
+ }
+
+ /**
+ * Set the compression level applied to objects in the pack.
+ *
+ * Default setting: {@value java.util.zip.Deflater#DEFAULT_COMPRESSION}
+ *
+ * @param level
+ * compression level, must be a valid level recognized by the
+ * {@link java.util.zip.Deflater} class.
+ */
+ public void setCompressionLevel(int level) {
+ compressionLevel = level;
+ }
+
+ /**
+ * Get the number of threads used during delta compression.
+ *
+ * Default setting: 0 (auto-detect processors)
+ *
+ * @return number of threads used for delta compression. 0 will auto-detect
+ * the threads to the number of available processors.
+ */
+ public int getThreads() {
+ return threads;
+ }
+
+ /**
+ * Set the number of threads to use for delta compression.
+ *
+ * During delta compression, if there are enough objects to be considered
+ * the writer will start up concurrent threads and allow them to compress
+ * different sections of the repository concurrently.
+ *
+ * An application thread pool can be set by {@link #setExecutor(Executor)}.
+ * If not set a temporary pool will be created by the writer, and torn down
+ * automatically when compression is over.
+ *
+ * Default setting: 0 (auto-detect processors)
+ *
+ * @param threads
+ * number of threads to use. If <= 0 the number of available
+ * processors for this JVM is used.
+ */
+ public void setThreads(int threads) {
+ this.threads = threads;
+ }
+
+ /** @return the preferred thread pool to execute delta search on. */
+ public Executor getExecutor() {
+ return executor;
+ }
+
+ /**
+ * Set the executor to use when using threads.
+ *
+ * During delta compression if the executor is non-null jobs will be queued
+ * up on it to perform delta compression in parallel. Aside from setting the
+ * executor, the caller must set {@link #setThreads(int)} to enable threaded
+ * delta search.
+ *
+ * @param executor
+ * executor to use for threads. Set to null to create a temporary
+ * executor just for the writer.
+ */
+ public void setExecutor(Executor executor) {
+ this.executor = executor;
+ }
+
+ /**
+ * Get the pack index file format version this instance creates.
+ *
+ * Default setting: {@value #DEFAULT_INDEX_VERSION}
+ *
+ * @return the index version, the special version 0 designates the oldest
+ * (most compatible) format available for the objects.
+ * @see PackIndexWriter
+ */
+ public int getIndexVersion() {
+ return indexVersion;
+ }
+
+ /**
+ * Set the pack index file format version this instance will create.
+ *
+ * Default setting: {@value #DEFAULT_INDEX_VERSION}
+ *
+ * @param version
+ * the version to write. The special version 0 designates the
+ * oldest (most compatible) format available for the objects.
+ * @see PackIndexWriter
+ */
+ public void setIndexVersion(final int version) {
+ indexVersion = version;
+ }
+
+ /**
+ * Update properties by setting fields from the configuration.
+ *
+ * If a property's corresponding variable is not defined in the supplied
+ * configuration, then it is left unmodified.
+ *
+ * @param rc
+ * configuration to read properties from.
+ */
+ public void fromConfig(final Config rc) {
+ setMaxDeltaDepth(rc.getInt("pack", "depth", getMaxDeltaDepth()));
+ setDeltaSearchWindowSize(rc.getInt("pack", "window", getDeltaSearchWindowSize()));
+ setDeltaSearchMemoryLimit(rc.getLong("pack", "windowmemory", getDeltaSearchMemoryLimit()));
+ setDeltaCacheSize(rc.getLong("pack", "deltacachesize", getDeltaCacheSize()));
+ setDeltaCacheLimit(rc.getInt("pack", "deltacachelimit", getDeltaCacheLimit()));
+ setCompressionLevel(rc.getInt("pack", "compression",
+ rc.getInt("core", "compression", getCompressionLevel())));
+ setIndexVersion(rc.getInt("pack", "indexversion", getIndexVersion()));
+ setBigFileThreshold(rc.getLong("core", "bigfilethreshold", getBigFileThreshold()));
+ setThreads(rc.getInt("pack", "threads", getThreads()));
+
+ // These variables aren't standardized
+ //
+ setReuseDeltas(rc.getBoolean("pack", "reusedeltas", isReuseDeltas()));
+ setReuseObjects(rc.getBoolean("pack", "reuseobjects", isReuseObjects()));
+ setDeltaCompress(rc.getBoolean("pack", "deltacompression", isDeltaCompress()));
+ }
+}
diff --git a/org.eclipse.jgit/src/org/eclipse/jgit/storage/pack/PackOutputStream.java b/org.eclipse.jgit/src/org/eclipse/jgit/storage/pack/PackOutputStream.java
new file mode 100644
index 0000000..92e1a19
--- /dev/null
+++ b/org.eclipse.jgit/src/org/eclipse/jgit/storage/pack/PackOutputStream.java
@@ -0,0 +1,213 @@
+/*
+ * Copyright (C) 2008-2009, Google Inc.
+ * Copyright (C) 2008, Marek Zawirski <marek.zawirski@gmail.com>
+ * and other copyright owners as documented in the project's IP log.
+ *
+ * This program and the accompanying materials are made available
+ * under the terms of the Eclipse Distribution License v1.0 which
+ * accompanies this distribution, is reproduced below, and is
+ * available at http://www.eclipse.org/org/documents/edl-v10.php
+ *
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or
+ * without modification, are permitted provided that the following
+ * conditions are met:
+ *
+ * - Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ *
+ * - Redistributions in binary form must reproduce the above
+ * copyright notice, this list of conditions and the following
+ * disclaimer in the documentation and/or other materials provided
+ * with the distribution.
+ *
+ * - Neither the name of the Eclipse Foundation, Inc. nor the
+ * names of its contributors may be used to endorse or promote
+ * products derived from this software without specific prior
+ * written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND
+ * CONTRIBUTORS "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES,
+ * INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
+ * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR
+ * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
+ * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT
+ * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
+ * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER
+ * CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT,
+ * STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
+ * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF
+ * ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ */
+
+package org.eclipse.jgit.storage.pack;
+
+import java.io.IOException;
+import java.io.OutputStream;
+import java.security.MessageDigest;
+import java.util.zip.CRC32;
+
+import org.eclipse.jgit.lib.Constants;
+import org.eclipse.jgit.lib.ProgressMonitor;
+import org.eclipse.jgit.util.NB;
+
+/** Custom output stream to support {@link PackWriter}. */
+public final class PackOutputStream extends OutputStream {
+ private final ProgressMonitor writeMonitor;
+
+ private final OutputStream out;
+
+ private final boolean ofsDelta;
+
+ private final CRC32 crc = new CRC32();
+
+ private final MessageDigest md = Constants.newMessageDigest();
+
+ private long count;
+
+ private byte[] headerBuffer = new byte[32];
+
+ private byte[] copyBuffer;
+
+ /**
+ * Initialize a pack output stream.
+ * <p>
+ * This constructor is exposed to support debugging the JGit library only.
+ * Application or storage level code should not create a PackOutputStream,
+ * instead use {@link PackWriter}, and let the writer create the stream.
+ *
+ * @param writeMonitor
+ * monitor to update on object output progress.
+ * @param out
+ * target stream to receive all object contents.
+ * @param pw
+ * packer that is going to perform the output.
+ */
+ public PackOutputStream(final ProgressMonitor writeMonitor,
+ final OutputStream out, final PackWriter pw) {
+ this.writeMonitor = writeMonitor;
+ this.out = out;
+ this.ofsDelta = pw.isDeltaBaseAsOffset();
+ }
+
+ @Override
+ public void write(final int b) throws IOException {
+ count++;
+ out.write(b);
+ crc.update(b);
+ md.update((byte) b);
+ }
+
+ @Override
+ public void write(final byte[] b, final int off, final int len)
+ throws IOException {
+ count += len;
+ out.write(b, off, len);
+ crc.update(b, off, len);
+ md.update(b, off, len);
+ }
+
+ @Override
+ public void flush() throws IOException {
+ out.flush();
+ }
+
+ void writeFileHeader(int version, int objectCount) throws IOException {
+ System.arraycopy(Constants.PACK_SIGNATURE, 0, headerBuffer, 0, 4);
+ NB.encodeInt32(headerBuffer, 4, version);
+ NB.encodeInt32(headerBuffer, 8, objectCount);
+ write(headerBuffer, 0, 12);
+ }
+
+ /**
+ * Commits the object header onto the stream.
+ * <p>
+ * Once the header has been written, the object representation must be fully
+ * output, or packing must abort abnormally.
+ *
+ * @param otp
+ * the object to pack. Header information is obtained.
+ * @param rawLength
+ * number of bytes of the inflated content. For an object that is
+ * in whole object format, this is the same as the object size.
+ * For an object that is in a delta format, this is the size of
+ * the inflated delta instruction stream.
+ * @throws IOException
+ * the underlying stream refused to accept the header.
+ */
+ public void writeHeader(ObjectToPack otp, long rawLength)
+ throws IOException {
+ if (otp.isDeltaRepresentation()) {
+ if (ofsDelta) {
+ ObjectToPack baseInPack = otp.getDeltaBase();
+ if (baseInPack != null && baseInPack.isWritten()) {
+ final long start = count;
+ int n = encodeTypeSize(Constants.OBJ_OFS_DELTA, rawLength);
+ write(headerBuffer, 0, n);
+
+ long offsetDiff = start - baseInPack.getOffset();
+ n = headerBuffer.length - 1;
+ headerBuffer[n] = (byte) (offsetDiff & 0x7F);
+ while ((offsetDiff >>= 7) > 0)
+ headerBuffer[--n] = (byte) (0x80 | (--offsetDiff & 0x7F));
+ write(headerBuffer, n, headerBuffer.length - n);
+ return;
+ }
+ }
+
+ int n = encodeTypeSize(Constants.OBJ_REF_DELTA, rawLength);
+ otp.getDeltaBaseId().copyRawTo(headerBuffer, n);
+ write(headerBuffer, 0, n + Constants.OBJECT_ID_LENGTH);
+ } else {
+ int n = encodeTypeSize(otp.getType(), rawLength);
+ write(headerBuffer, 0, n);
+ }
+ }
+
+ private int encodeTypeSize(int type, long rawLength) {
+ long nextLength = rawLength >>> 4;
+ headerBuffer[0] = (byte) ((nextLength > 0 ? 0x80 : 0x00)
+ | (type << 4) | (rawLength & 0x0F));
+ rawLength = nextLength;
+ int n = 1;
+ while (rawLength > 0) {
+ nextLength >>>= 7;
+ headerBuffer[n++] = (byte) ((nextLength > 0 ? 0x80 : 0x00) | (rawLength & 0x7F));
+ rawLength = nextLength;
+ }
+ return n;
+ }
+
+ /** @return a temporary buffer writers can use to copy data with. */
+ public byte[] getCopyBuffer() {
+ if (copyBuffer == null)
+ copyBuffer = new byte[16 * 1024];
+ return copyBuffer;
+ }
+
+ void endObject() {
+ writeMonitor.update(1);
+ }
+
+ /** @return total number of bytes written since stream start. */
+ long length() {
+ return count;
+ }
+
+ /** @return obtain the current CRC32 register. */
+ int getCRC32() {
+ return (int) crc.getValue();
+ }
+
+ /** Reinitialize the CRC32 register for a new region. */
+ void resetCRC32() {
+ crc.reset();
+ }
+
+ /** @return obtain the current SHA-1 digest. */
+ byte[] getDigest() {
+ return md.digest();
+ }
+}
diff --git a/org.eclipse.jgit/src/org/eclipse/jgit/storage/pack/PackWriter.java b/org.eclipse.jgit/src/org/eclipse/jgit/storage/pack/PackWriter.java
new file mode 100644
index 0000000..df5594c
--- /dev/null
+++ b/org.eclipse.jgit/src/org/eclipse/jgit/storage/pack/PackWriter.java
@@ -0,0 +1,1106 @@
+/*
+ * Copyright (C) 2008-2010, Google Inc.
+ * Copyright (C) 2008, Marek Zawirski <marek.zawirski@gmail.com>
+ * and other copyright owners as documented in the project's IP log.
+ *
+ * This program and the accompanying materials are made available
+ * under the terms of the Eclipse Distribution License v1.0 which
+ * accompanies this distribution, is reproduced below, and is
+ * available at http://www.eclipse.org/org/documents/edl-v10.php
+ *
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or
+ * without modification, are permitted provided that the following
+ * conditions are met:
+ *
+ * - Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ *
+ * - Redistributions in binary form must reproduce the above
+ * copyright notice, this list of conditions and the following
+ * disclaimer in the documentation and/or other materials provided
+ * with the distribution.
+ *
+ * - Neither the name of the Eclipse Foundation, Inc. nor the
+ * names of its contributors may be used to endorse or promote
+ * products derived from this software without specific prior
+ * written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND
+ * CONTRIBUTORS "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES,
+ * INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
+ * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR
+ * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
+ * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT
+ * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
+ * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER
+ * CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT,
+ * STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
+ * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF
+ * ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ */
+
+package org.eclipse.jgit.storage.pack;
+
+import static org.eclipse.jgit.storage.pack.StoredObjectRepresentation.PACK_DELTA;
+import static org.eclipse.jgit.storage.pack.StoredObjectRepresentation.PACK_WHOLE;
+
+import java.io.IOException;
+import java.io.InputStream;
+import java.io.OutputStream;
+import java.security.MessageDigest;
+import java.util.ArrayList;
+import java.util.Arrays;
+import java.util.Collection;
+import java.util.Collections;
+import java.util.Comparator;
+import java.util.Iterator;
+import java.util.List;
+import java.util.concurrent.CountDownLatch;
+import java.util.concurrent.ExecutionException;
+import java.util.concurrent.Executor;
+import java.util.concurrent.ExecutorService;
+import java.util.concurrent.Executors;
+import java.util.concurrent.Future;
+import java.util.concurrent.TimeUnit;
+import java.util.zip.Deflater;
+import java.util.zip.DeflaterOutputStream;
+
+import org.eclipse.jgit.JGitText;
+import org.eclipse.jgit.errors.CorruptObjectException;
+import org.eclipse.jgit.errors.IncorrectObjectTypeException;
+import org.eclipse.jgit.errors.LargeObjectException;
+import org.eclipse.jgit.errors.MissingObjectException;
+import org.eclipse.jgit.errors.StoredObjectRepresentationNotAvailableException;
+import org.eclipse.jgit.lib.AnyObjectId;
+import org.eclipse.jgit.lib.Constants;
+import org.eclipse.jgit.lib.NullProgressMonitor;
+import org.eclipse.jgit.lib.ObjectId;
+import org.eclipse.jgit.lib.ObjectIdSubclassMap;
+import org.eclipse.jgit.lib.ObjectLoader;
+import org.eclipse.jgit.lib.ObjectReader;
+import org.eclipse.jgit.lib.ProgressMonitor;
+import org.eclipse.jgit.lib.Repository;
+import org.eclipse.jgit.lib.ThreadSafeProgressMonitor;
+import org.eclipse.jgit.revwalk.ObjectWalk;
+import org.eclipse.jgit.revwalk.RevFlag;
+import org.eclipse.jgit.revwalk.RevObject;
+import org.eclipse.jgit.revwalk.RevSort;
+import org.eclipse.jgit.storage.file.PackIndexWriter;
+import org.eclipse.jgit.util.IO;
+import org.eclipse.jgit.util.TemporaryBuffer;
+
+/**
+ * <p>
+ * PackWriter class is responsible for generating pack files from specified set
+ * of objects from repository. This implementation produce pack files in format
+ * version 2.
+ * </p>
+ * <p>
+ * Source of objects may be specified in two ways:
+ * <ul>
+ * <li>(usually) by providing sets of interesting and uninteresting objects in
+ * repository - all interesting objects and their ancestors except uninteresting
+ * objects and their ancestors will be included in pack, or</li>
+ * <li>by providing iterator of {@link RevObject} specifying exact list and
+ * order of objects in pack</li>
+ * </ul>
+ * Typical usage consists of creating instance intended for some pack,
+ * configuring options, preparing the list of objects by calling
+ * {@link #preparePack(Iterator)} or
+ * {@link #preparePack(ProgressMonitor, Collection, Collection)}, and finally
+ * producing the stream with {@link #writePack(ProgressMonitor, ProgressMonitor, OutputStream)}.
+ * </p>
+ * <p>
+ * Class provide set of configurable options and {@link ProgressMonitor}
+ * support, as operations may take a long time for big repositories. Deltas
+ * searching algorithm is <b>NOT IMPLEMENTED</b> yet - this implementation
+ * relies only on deltas and objects reuse.
+ * </p>
+ * <p>
+ * This class is not thread safe, it is intended to be used in one thread, with
+ * one instance per created pack. Subsequent calls to writePack result in
+ * undefined behavior.
+ * </p>
+ */
+public class PackWriter {
+ private static final int PACK_VERSION_GENERATED = 2;
+
+ @SuppressWarnings("unchecked")
+ private final List<ObjectToPack> objectsLists[] = new List[Constants.OBJ_TAG + 1];
+ {
+ objectsLists[0] = Collections.<ObjectToPack> emptyList();
+ objectsLists[Constants.OBJ_COMMIT] = new ArrayList<ObjectToPack>();
+ objectsLists[Constants.OBJ_TREE] = new ArrayList<ObjectToPack>();
+ objectsLists[Constants.OBJ_BLOB] = new ArrayList<ObjectToPack>();
+ objectsLists[Constants.OBJ_TAG] = new ArrayList<ObjectToPack>();
+ }
+
+ private final ObjectIdSubclassMap<ObjectToPack> objectsMap = new ObjectIdSubclassMap<ObjectToPack>();
+
+ // edge objects for thin packs
+ private final ObjectIdSubclassMap<ObjectToPack> edgeObjects = new ObjectIdSubclassMap<ObjectToPack>();
+
+ private Deflater myDeflater;
+
+ private final ObjectReader reader;
+
+ /** {@link #reader} recast to the reuse interface, if it supports it. */
+ private final ObjectReuseAsIs reuseSupport;
+
+ private final PackConfig config;
+
+ private List<ObjectToPack> sortedByName;
+
+ private byte packcsum[];
+
+ private boolean deltaBaseAsOffset;
+
+ private boolean reuseDeltas;
+
+ private boolean thin;
+
+ private boolean ignoreMissingUninteresting = true;
+
+ /**
+ * Create writer for specified repository.
+ * <p>
+ * Objects for packing are specified in {@link #preparePack(Iterator)} or
+ * {@link #preparePack(ProgressMonitor, Collection, Collection)}.
+ *
+ * @param repo
+ * repository where objects are stored.
+ */
+ public PackWriter(final Repository repo) {
+ this(repo, repo.newObjectReader());
+ }
+
+ /**
+ * Create a writer to load objects from the specified reader.
+ * <p>
+ * Objects for packing are specified in {@link #preparePack(Iterator)} or
+ * {@link #preparePack(ProgressMonitor, Collection, Collection)}.
+ *
+ * @param reader
+ * reader to read from the repository with.
+ */
+ public PackWriter(final ObjectReader reader) {
+ this(new PackConfig(), reader);
+ }
+
+ /**
+ * Create writer for specified repository.
+ * <p>
+ * Objects for packing are specified in {@link #preparePack(Iterator)} or
+ * {@link #preparePack(ProgressMonitor, Collection, Collection)}.
+ *
+ * @param repo
+ * repository where objects are stored.
+ * @param reader
+ * reader to read from the repository with.
+ */
+ public PackWriter(final Repository repo, final ObjectReader reader) {
+ this(new PackConfig(repo), reader);
+ }
+
+ /**
+ * Create writer with a specified configuration.
+ * <p>
+ * Objects for packing are specified in {@link #preparePack(Iterator)} or
+ * {@link #preparePack(ProgressMonitor, Collection, Collection)}.
+ *
+ * @param config
+ * configuration for the pack writer.
+ * @param reader
+ * reader to read from the repository with.
+ */
+ public PackWriter(final PackConfig config, final ObjectReader reader) {
+ this.config = config;
+ this.reader = reader;
+ if (reader instanceof ObjectReuseAsIs)
+ reuseSupport = ((ObjectReuseAsIs) reader);
+ else
+ reuseSupport = null;
+
+ deltaBaseAsOffset = config.isDeltaBaseAsOffset();
+ reuseDeltas = config.isReuseDeltas();
+ }
+
+ /**
+ * Check whether writer can store delta base as an offset (new style
+ * reducing pack size) or should store it as an object id (legacy style,
+ * compatible with old readers).
+ *
+ * Default setting: {@value PackConfig#DEFAULT_DELTA_BASE_AS_OFFSET}
+ *
+ * @return true if delta base is stored as an offset; false if it is stored
+ * as an object id.
+ */
+ public boolean isDeltaBaseAsOffset() {
+ return deltaBaseAsOffset;
+ }
+
+ /**
+ * Set writer delta base format. Delta base can be written as an offset in a
+ * pack file (new approach reducing file size) or as an object id (legacy
+ * approach, compatible with old readers).
+ *
+ * Default setting: {@value PackConfig#DEFAULT_DELTA_BASE_AS_OFFSET}
+ *
+ * @param deltaBaseAsOffset
+ * boolean indicating whether delta base can be stored as an
+ * offset.
+ */
+ public void setDeltaBaseAsOffset(boolean deltaBaseAsOffset) {
+ this.deltaBaseAsOffset = deltaBaseAsOffset;
+ }
+
+ /** @return true if this writer is producing a thin pack. */
+ public boolean isThin() {
+ return thin;
+ }
+
+ /**
+ * @param packthin
+ * a boolean indicating whether writer may pack objects with
+ * delta base object not within set of objects to pack, but
+ * belonging to party repository (uninteresting/boundary) as
+ * determined by set; this kind of pack is used only for
+ * transport; true - to produce thin pack, false - otherwise.
+ */
+ public void setThin(final boolean packthin) {
+ thin = packthin;
+ }
+
+ /**
+ * @return true to ignore objects that are uninteresting and also not found
+ * on local disk; false to throw a {@link MissingObjectException}
+ * out of {@link #preparePack(ProgressMonitor, Collection, Collection)} if an
+ * uninteresting object is not in the source repository. By default,
+ * true, permitting gracefully ignoring of uninteresting objects.
+ */
+ public boolean isIgnoreMissingUninteresting() {
+ return ignoreMissingUninteresting;
+ }
+
+ /**
+ * @param ignore
+ * true if writer should ignore non existing uninteresting
+ * objects during construction set of objects to pack; false
+ * otherwise - non existing uninteresting objects may cause
+ * {@link MissingObjectException}
+ */
+ public void setIgnoreMissingUninteresting(final boolean ignore) {
+ ignoreMissingUninteresting = ignore;
+ }
+
+ /**
+ * Returns objects number in a pack file that was created by this writer.
+ *
+ * @return number of objects in pack.
+ */
+ public int getObjectsNumber() {
+ return objectsMap.size();
+ }
+
+ /**
+ * Prepare the list of objects to be written to the pack stream.
+ * <p>
+ * Iterator <b>exactly</b> determines which objects are included in a pack
+ * and order they appear in pack (except that objects order by type is not
+ * needed at input). This order should conform general rules of ordering
+ * objects in git - by recency and path (type and delta-base first is
+ * internally secured) and responsibility for guaranteeing this order is on
+ * a caller side. Iterator must return each id of object to write exactly
+ * once.
+ * </p>
+ * <p>
+ * When iterator returns object that has {@link RevFlag#UNINTERESTING} flag,
+ * this object won't be included in an output pack. Instead, it is recorded
+ * as edge-object (known to remote repository) for thin-pack. In such a case
+ * writer may pack objects with delta base object not within set of objects
+ * to pack, but belonging to party repository - those marked with
+ * {@link RevFlag#UNINTERESTING} flag. This type of pack is used only for
+ * transport.
+ * </p>
+ *
+ * @param objectsSource
+ * iterator of object to store in a pack; order of objects within
+ * each type is important, ordering by type is not needed;
+ * allowed types for objects are {@link Constants#OBJ_COMMIT},
+ * {@link Constants#OBJ_TREE}, {@link Constants#OBJ_BLOB} and
+ * {@link Constants#OBJ_TAG}; objects returned by iterator may
+ * be later reused by caller as object id and type are internally
+ * copied in each iteration; if object returned by iterator has
+ * {@link RevFlag#UNINTERESTING} flag set, it won't be included
+ * in a pack, but is considered as edge-object for thin-pack.
+ * @throws IOException
+ * when some I/O problem occur during reading objects.
+ */
+ public void preparePack(final Iterator<RevObject> objectsSource)
+ throws IOException {
+ while (objectsSource.hasNext()) {
+ addObject(objectsSource.next());
+ }
+ }
+
+ /**
+ * Prepare the list of objects to be written to the pack stream.
+ * <p>
+ * Basing on these 2 sets, another set of objects to put in a pack file is
+ * created: this set consists of all objects reachable (ancestors) from
+ * interesting objects, except uninteresting objects and their ancestors.
+ * This method uses class {@link ObjectWalk} extensively to find out that
+ * appropriate set of output objects and their optimal order in output pack.
+ * Order is consistent with general git in-pack rules: sort by object type,
+ * recency, path and delta-base first.
+ * </p>
+ *
+ * @param countingMonitor
+ * progress during object enumeration.
+ * @param interestingObjects
+ * collection of objects to be marked as interesting (start
+ * points of graph traversal).
+ * @param uninterestingObjects
+ * collection of objects to be marked as uninteresting (end
+ * points of graph traversal).
+ * @throws IOException
+ * when some I/O problem occur during reading objects.
+ */
+ public void preparePack(ProgressMonitor countingMonitor,
+ final Collection<? extends ObjectId> interestingObjects,
+ final Collection<? extends ObjectId> uninterestingObjects)
+ throws IOException {
+ if (countingMonitor == null)
+ countingMonitor = NullProgressMonitor.INSTANCE;
+ ObjectWalk walker = setUpWalker(interestingObjects,
+ uninterestingObjects);
+ findObjectsToPack(countingMonitor, walker);
+ }
+
+ /**
+ * Determine if the pack file will contain the requested object.
+ *
+ * @param id
+ * the object to test the existence of.
+ * @return true if the object will appear in the output pack file.
+ */
+ public boolean willInclude(final AnyObjectId id) {
+ return objectsMap.get(id) != null;
+ }
+
+ /**
+ * Computes SHA-1 of lexicographically sorted objects ids written in this
+ * pack, as used to name a pack file in repository.
+ *
+ * @return ObjectId representing SHA-1 name of a pack that was created.
+ */
+ public ObjectId computeName() {
+ final byte[] buf = new byte[Constants.OBJECT_ID_LENGTH];
+ final MessageDigest md = Constants.newMessageDigest();
+ for (ObjectToPack otp : sortByName()) {
+ otp.copyRawTo(buf, 0);
+ md.update(buf, 0, Constants.OBJECT_ID_LENGTH);
+ }
+ return ObjectId.fromRaw(md.digest());
+ }
+
+ /**
+ * Create an index file to match the pack file just written.
+ * <p>
+ * This method can only be invoked after {@link #preparePack(Iterator)} or
+ * {@link #preparePack(ProgressMonitor, Collection, Collection)} has been
+ * invoked and completed successfully. Writing a corresponding index is an
+ * optional feature that not all pack users may require.
+ *
+ * @param indexStream
+ * output for the index data. Caller is responsible for closing
+ * this stream.
+ * @throws IOException
+ * the index data could not be written to the supplied stream.
+ */
+ public void writeIndex(final OutputStream indexStream) throws IOException {
+ final List<ObjectToPack> list = sortByName();
+ final PackIndexWriter iw;
+ int indexVersion = config.getIndexVersion();
+ if (indexVersion <= 0)
+ iw = PackIndexWriter.createOldestPossible(indexStream, list);
+ else
+ iw = PackIndexWriter.createVersion(indexStream, indexVersion);
+ iw.write(list, packcsum);
+ }
+
+ private List<ObjectToPack> sortByName() {
+ if (sortedByName == null) {
+ sortedByName = new ArrayList<ObjectToPack>(objectsMap.size());
+ for (List<ObjectToPack> list : objectsLists) {
+ for (ObjectToPack otp : list)
+ sortedByName.add(otp);
+ }
+ Collections.sort(sortedByName);
+ }
+ return sortedByName;
+ }
+
+ /**
+ * Write the prepared pack to the supplied stream.
+ * <p>
+ * At first, this method collects and sorts objects to pack, then deltas
+ * search is performed if set up accordingly, finally pack stream is
+ * written.
+ * </p>
+ * <p>
+ * All reused objects data checksum (Adler32/CRC32) is computed and
+ * validated against existing checksum.
+ * </p>
+ *
+ * @param compressMonitor
+ * progress monitor to report object compression work.
+ * @param writeMonitor
+ * progress monitor to report the number of objects written.
+ * @param packStream
+ * output stream of pack data. The stream should be buffered by
+ * the caller. The caller is responsible for closing the stream.
+ * @throws IOException
+ * an error occurred reading a local object's data to include in
+ * the pack, or writing compressed object data to the output
+ * stream.
+ */
+ public void writePack(ProgressMonitor compressMonitor,
+ ProgressMonitor writeMonitor, OutputStream packStream)
+ throws IOException {
+ if (compressMonitor == null)
+ compressMonitor = NullProgressMonitor.INSTANCE;
+ if (writeMonitor == null)
+ writeMonitor = NullProgressMonitor.INSTANCE;
+
+ if ((reuseDeltas || config.isReuseObjects()) && reuseSupport != null)
+ searchForReuse();
+ if (config.isDeltaCompress())
+ searchForDeltas(compressMonitor);
+
+ final PackOutputStream out = new PackOutputStream(writeMonitor,
+ packStream, this);
+
+ int objCnt = getObjectsNumber();
+ writeMonitor.beginTask(JGitText.get().writingObjects, objCnt);
+ out.writeFileHeader(PACK_VERSION_GENERATED, objCnt);
+ writeObjects(writeMonitor, out);
+ writeChecksum(out);
+
+ reader.release();
+ writeMonitor.endTask();
+ }
+
+ /** Release all resources used by this writer. */
+ public void release() {
+ reader.release();
+ if (myDeflater != null) {
+ myDeflater.end();
+ myDeflater = null;
+ }
+ }
+
+ private void searchForReuse() throws IOException {
+ for (List<ObjectToPack> list : objectsLists) {
+ for (ObjectToPack otp : list)
+ reuseSupport.selectObjectRepresentation(this, otp);
+ }
+ }
+
+ private void searchForDeltas(ProgressMonitor monitor)
+ throws MissingObjectException, IncorrectObjectTypeException,
+ IOException {
+ // Commits and annotated tags tend to have too many differences to
+ // really benefit from delta compression. Consequently just don't
+ // bother examining those types here.
+ //
+ ObjectToPack[] list = new ObjectToPack[
+ objectsLists[Constants.OBJ_TREE].size()
+ + objectsLists[Constants.OBJ_BLOB].size()
+ + edgeObjects.size()];
+ int cnt = 0;
+ cnt = findObjectsNeedingDelta(list, cnt, Constants.OBJ_TREE);
+ cnt = findObjectsNeedingDelta(list, cnt, Constants.OBJ_BLOB);
+ if (cnt == 0)
+ return;
+
+ // Queue up any edge objects that we might delta against. We won't
+ // be sending these as we assume the other side has them, but we need
+ // them in the search phase below.
+ //
+ for (ObjectToPack eo : edgeObjects) {
+ try {
+ if (loadSize(eo))
+ list[cnt++] = eo;
+ } catch (IOException notAvailable) {
+ // Skip this object. Since we aren't going to write it out
+ // the only consequence of it being unavailable to us is we
+ // may produce a larger data stream than we could have.
+ //
+ if (!ignoreMissingUninteresting)
+ throw notAvailable;
+ }
+ }
+
+ monitor.beginTask(JGitText.get().compressingObjects, cnt);
+
+ // Sort the objects by path hash so like files are near each other,
+ // and then by size descending so that bigger files are first. This
+ // applies "Linus' Law" which states that newer files tend to be the
+ // bigger ones, because source files grow and hardly ever shrink.
+ //
+ Arrays.sort(list, 0, cnt, new Comparator<ObjectToPack>() {
+ public int compare(ObjectToPack a, ObjectToPack b) {
+ int cmp = a.getType() - b.getType();
+ if (cmp == 0)
+ cmp = (a.getPathHash() >>> 1) - (b.getPathHash() >>> 1);
+ if (cmp == 0)
+ cmp = (a.getPathHash() & 1) - (b.getPathHash() & 1);
+ if (cmp == 0)
+ cmp = b.getWeight() - a.getWeight();
+ return cmp;
+ }
+ });
+ searchForDeltas(monitor, list, cnt);
+ monitor.endTask();
+ }
+
+ private int findObjectsNeedingDelta(ObjectToPack[] list, int cnt, int type)
+ throws MissingObjectException, IncorrectObjectTypeException,
+ IOException {
+ for (ObjectToPack otp : objectsLists[type]) {
+ if (otp.isDoNotDelta()) // delta is disabled for this path
+ continue;
+ if (otp.isDeltaRepresentation()) // already reusing a delta
+ continue;
+ if (loadSize(otp))
+ list[cnt++] = otp;
+ }
+ return cnt;
+ }
+
+ private boolean loadSize(ObjectToPack e) throws MissingObjectException,
+ IncorrectObjectTypeException, IOException {
+ long sz = reader.getObjectSize(e, e.getType());
+
+ // If its too big for us to handle, skip over it.
+ //
+ if (config.getBigFileThreshold() <= sz || Integer.MAX_VALUE <= sz)
+ return false;
+
+ // If its too tiny for the delta compression to work, skip it.
+ //
+ if (sz <= DeltaIndex.BLKSZ)
+ return false;
+
+ e.setWeight((int) sz);
+ return true;
+ }
+
+ private void searchForDeltas(final ProgressMonitor monitor,
+ final ObjectToPack[] list, final int cnt)
+ throws MissingObjectException, IncorrectObjectTypeException,
+ LargeObjectException, IOException {
+ int threads = config.getThreads();
+ if (threads == 0)
+ threads = Runtime.getRuntime().availableProcessors();
+
+ if (threads <= 1 || cnt <= 2 * config.getDeltaSearchWindowSize()) {
+ DeltaCache dc = new DeltaCache(config);
+ DeltaWindow dw = new DeltaWindow(config, dc, reader);
+ dw.search(monitor, list, 0, cnt);
+ return;
+ }
+
+ final DeltaCache dc = new ThreadSafeDeltaCache(config);
+ final ProgressMonitor pm = new ThreadSafeProgressMonitor(monitor);
+
+ // Guess at the size of batch we want. Because we don't really
+ // have a way for a thread to steal work from another thread if
+ // it ends early, we over partition slightly so the work units
+ // are a bit smaller.
+ //
+ int estSize = cnt / (threads * 2);
+ if (estSize < 2 * config.getDeltaSearchWindowSize())
+ estSize = 2 * config.getDeltaSearchWindowSize();
+
+ final List<DeltaTask> myTasks = new ArrayList<DeltaTask>(threads * 2);
+ for (int i = 0; i < cnt;) {
+ final int start = i;
+ final int batchSize;
+
+ if (cnt - i < estSize) {
+ // If we don't have enough to fill the remaining block,
+ // schedule what is left over as a single block.
+ //
+ batchSize = cnt - i;
+ } else {
+ // Try to split the block at the end of a path.
+ //
+ int end = start + estSize;
+ while (end < cnt) {
+ ObjectToPack a = list[end - 1];
+ ObjectToPack b = list[end];
+ if (a.getPathHash() == b.getPathHash())
+ end++;
+ else
+ break;
+ }
+ batchSize = end - start;
+ }
+ i += batchSize;
+ myTasks.add(new DeltaTask(config, reader, dc, pm, batchSize, start, list));
+ }
+
+ final Executor executor = config.getExecutor();
+ final List<Throwable> errors = Collections
+ .synchronizedList(new ArrayList<Throwable>());
+ if (executor instanceof ExecutorService) {
+ // Caller supplied us a service, use it directly.
+ //
+ runTasks((ExecutorService) executor, myTasks, errors);
+
+ } else if (executor == null) {
+ // Caller didn't give us a way to run the tasks, spawn up a
+ // temporary thread pool and make sure it tears down cleanly.
+ //
+ ExecutorService pool = Executors.newFixedThreadPool(threads);
+ try {
+ runTasks(pool, myTasks, errors);
+ } finally {
+ pool.shutdown();
+ for (;;) {
+ try {
+ if (pool.awaitTermination(60, TimeUnit.SECONDS))
+ break;
+ } catch (InterruptedException e) {
+ throw new IOException(
+ JGitText.get().packingCancelledDuringObjectsWriting);
+ }
+ }
+ }
+ } else {
+ // The caller gave us an executor, but it might not do
+ // asynchronous execution. Wrap everything and hope it
+ // can schedule these for us.
+ //
+ final CountDownLatch done = new CountDownLatch(myTasks.size());
+ for (final DeltaTask task : myTasks) {
+ executor.execute(new Runnable() {
+ public void run() {
+ try {
+ task.call();
+ } catch (Throwable failure) {
+ errors.add(failure);
+ } finally {
+ done.countDown();
+ }
+ }
+ });
+ }
+ try {
+ done.await();
+ } catch (InterruptedException ie) {
+ // We can't abort the other tasks as we have no handle.
+ // Cross our fingers and just break out anyway.
+ //
+ throw new IOException(
+ JGitText.get().packingCancelledDuringObjectsWriting);
+ }
+ }
+
+ // If any task threw an error, try to report it back as
+ // though we weren't using a threaded search algorithm.
+ //
+ if (!errors.isEmpty()) {
+ Throwable err = errors.get(0);
+ if (err instanceof Error)
+ throw (Error) err;
+ if (err instanceof RuntimeException)
+ throw (RuntimeException) err;
+ if (err instanceof IOException)
+ throw (IOException) err;
+
+ IOException fail = new IOException(err.getMessage());
+ fail.initCause(err);
+ throw fail;
+ }
+ }
+
+ private void runTasks(ExecutorService pool, List<DeltaTask> tasks,
+ List<Throwable> errors) throws IOException {
+ List<Future<?>> futures = new ArrayList<Future<?>>(tasks.size());
+ for (DeltaTask task : tasks)
+ futures.add(pool.submit(task));
+
+ try {
+ for (Future<?> f : futures) {
+ try {
+ f.get();
+ } catch (ExecutionException failed) {
+ errors.add(failed.getCause());
+ }
+ }
+ } catch (InterruptedException ie) {
+ for (Future<?> f : futures)
+ f.cancel(true);
+ throw new IOException(
+ JGitText.get().packingCancelledDuringObjectsWriting);
+ }
+ }
+
+ private void writeObjects(ProgressMonitor writeMonitor, PackOutputStream out)
+ throws IOException {
+ for (List<ObjectToPack> list : objectsLists) {
+ for (ObjectToPack otp : list) {
+ if (writeMonitor.isCancelled())
+ throw new IOException(
+ JGitText.get().packingCancelledDuringObjectsWriting);
+ if (!otp.isWritten())
+ writeObject(out, otp);
+ }
+ }
+ }
+
+ private void writeObject(PackOutputStream out, final ObjectToPack otp)
+ throws IOException {
+ if (otp.isWritten())
+ return; // We shouldn't be here.
+
+ otp.markWantWrite();
+ if (otp.isDeltaRepresentation())
+ writeBaseFirst(out, otp);
+
+ out.resetCRC32();
+ otp.setOffset(out.length());
+
+ while (otp.isReuseAsIs()) {
+ try {
+ reuseSupport.copyObjectAsIs(out, otp);
+ out.endObject();
+ otp.setCRC(out.getCRC32());
+ return;
+ } catch (StoredObjectRepresentationNotAvailableException gone) {
+ if (otp.getOffset() == out.length()) {
+ redoSearchForReuse(otp);
+ continue;
+ } else {
+ // Object writing already started, we cannot recover.
+ //
+ CorruptObjectException coe;
+ coe = new CorruptObjectException(otp, "");
+ coe.initCause(gone);
+ throw coe;
+ }
+ }
+ }
+
+ // If we reached here, reuse wasn't possible.
+ //
+ if (otp.isDeltaRepresentation())
+ writeDeltaObjectDeflate(out, otp);
+ else
+ writeWholeObjectDeflate(out, otp);
+ out.endObject();
+ otp.setCRC(out.getCRC32());
+ }
+
+ private void writeBaseFirst(PackOutputStream out, final ObjectToPack otp)
+ throws IOException {
+ ObjectToPack baseInPack = otp.getDeltaBase();
+ if (baseInPack != null) {
+ if (!baseInPack.isWritten()) {
+ if (baseInPack.wantWrite()) {
+ // There is a cycle. Our caller is trying to write the
+ // object we want as a base, and called us. Turn off
+ // delta reuse so we can find another form.
+ //
+ reuseDeltas = false;
+ redoSearchForReuse(otp);
+ reuseDeltas = true;
+ } else {
+ writeObject(out, baseInPack);
+ }
+ }
+ } else if (!thin) {
+ // This should never occur, the base isn't in the pack and
+ // the pack isn't allowed to reference base outside objects.
+ // Write the object as a whole form, even if that is slow.
+ //
+ otp.clearDeltaBase();
+ otp.clearReuseAsIs();
+ }
+ }
+
+ private void redoSearchForReuse(final ObjectToPack otp) throws IOException,
+ MissingObjectException {
+ otp.clearDeltaBase();
+ otp.clearReuseAsIs();
+ reuseSupport.selectObjectRepresentation(this, otp);
+ }
+
+ private void writeWholeObjectDeflate(PackOutputStream out,
+ final ObjectToPack otp) throws IOException {
+ final Deflater deflater = deflater();
+ final ObjectLoader ldr = reader.open(otp, otp.getType());
+
+ out.writeHeader(otp, ldr.getSize());
+
+ deflater.reset();
+ DeflaterOutputStream dst = new DeflaterOutputStream(out, deflater);
+ ldr.copyTo(dst);
+ dst.finish();
+ }
+
+ private void writeDeltaObjectDeflate(PackOutputStream out,
+ final ObjectToPack otp) throws IOException {
+ DeltaCache.Ref ref = otp.popCachedDelta();
+ if (ref != null) {
+ byte[] zbuf = ref.get();
+ if (zbuf != null) {
+ out.writeHeader(otp, otp.getCachedSize());
+ out.write(zbuf);
+ return;
+ }
+ }
+
+ TemporaryBuffer.Heap delta = delta(otp);
+ out.writeHeader(otp, delta.length());
+
+ Deflater deflater = deflater();
+ deflater.reset();
+ DeflaterOutputStream dst = new DeflaterOutputStream(out, deflater);
+ delta.writeTo(dst, null);
+ dst.finish();
+ }
+
+ private TemporaryBuffer.Heap delta(final ObjectToPack otp)
+ throws IOException {
+ DeltaIndex index = new DeltaIndex(buffer(otp.getDeltaBaseId()));
+ byte[] res = buffer(otp);
+
+ // We never would have proposed this pair if the delta would be
+ // larger than the unpacked version of the object. So using it
+ // as our buffer limit is valid: we will never reach it.
+ //
+ TemporaryBuffer.Heap delta = new TemporaryBuffer.Heap(res.length);
+ index.encode(delta, res);
+ return delta;
+ }
+
+ private byte[] buffer(AnyObjectId objId) throws IOException {
+ return buffer(config, reader, objId);
+ }
+
+ static byte[] buffer(PackConfig config, ObjectReader or, AnyObjectId objId)
+ throws IOException {
+ ObjectLoader ldr = or.open(objId);
+ if (!ldr.isLarge())
+ return ldr.getCachedBytes();
+
+ // PackWriter should have already pruned objects that
+ // are above the big file threshold, so our chances of
+ // the object being below it are very good. We really
+ // shouldn't be here, unless the implementation is odd.
+
+ // If it really is too big to work with, abort out now.
+ //
+ long sz = ldr.getSize();
+ if (config.getBigFileThreshold() <= sz || Integer.MAX_VALUE < sz)
+ throw new LargeObjectException(objId.copy());
+
+ // Its considered to be large by the loader, but we really
+ // want it in byte array format. Try to make it happen.
+ //
+ byte[] buf;
+ try {
+ buf = new byte[(int) sz];
+ } catch (OutOfMemoryError noMemory) {
+ LargeObjectException e;
+
+ e = new LargeObjectException(objId.copy());
+ e.initCause(noMemory);
+ throw e;
+ }
+ InputStream in = ldr.openStream();
+ try {
+ IO.readFully(in, buf, 0, buf.length);
+ } finally {
+ in.close();
+ }
+ return buf;
+ }
+
+ private Deflater deflater() {
+ if (myDeflater == null)
+ myDeflater = new Deflater(config.getCompressionLevel());
+ return myDeflater;
+ }
+
+ private void writeChecksum(PackOutputStream out) throws IOException {
+ packcsum = out.getDigest();
+ out.write(packcsum);
+ }
+
+ private ObjectWalk setUpWalker(
+ final Collection<? extends ObjectId> interestingObjects,
+ final Collection<? extends ObjectId> uninterestingObjects)
+ throws MissingObjectException, IOException,
+ IncorrectObjectTypeException {
+ final ObjectWalk walker = new ObjectWalk(reader);
+ walker.setRetainBody(false);
+ walker.sort(RevSort.COMMIT_TIME_DESC);
+ if (thin)
+ walker.sort(RevSort.BOUNDARY, true);
+
+ for (ObjectId id : interestingObjects) {
+ RevObject o = walker.parseAny(id);
+ walker.markStart(o);
+ }
+ if (uninterestingObjects != null) {
+ for (ObjectId id : uninterestingObjects) {
+ final RevObject o;
+ try {
+ o = walker.parseAny(id);
+ } catch (MissingObjectException x) {
+ if (ignoreMissingUninteresting)
+ continue;
+ throw x;
+ }
+ walker.markUninteresting(o);
+ }
+ }
+ return walker;
+ }
+
+ private void findObjectsToPack(final ProgressMonitor countingMonitor,
+ final ObjectWalk walker) throws MissingObjectException,
+ IncorrectObjectTypeException, IOException {
+ countingMonitor.beginTask(JGitText.get().countingObjects,
+ ProgressMonitor.UNKNOWN);
+ RevObject o;
+
+ while ((o = walker.next()) != null) {
+ addObject(o, 0);
+ countingMonitor.update(1);
+ }
+ while ((o = walker.nextObject()) != null) {
+ addObject(o, walker.getPathHashCode());
+ countingMonitor.update(1);
+ }
+ countingMonitor.endTask();
+ }
+
+ /**
+ * Include one object to the output file.
+ * <p>
+ * Objects are written in the order they are added. If the same object is
+ * added twice, it may be written twice, creating a larger than necessary
+ * file.
+ *
+ * @param object
+ * the object to add.
+ * @throws IncorrectObjectTypeException
+ * the object is an unsupported type.
+ */
+ public void addObject(final RevObject object)
+ throws IncorrectObjectTypeException {
+ addObject(object, 0);
+ }
+
+ private void addObject(final RevObject object, final int pathHashCode)
+ throws IncorrectObjectTypeException {
+ if (object.has(RevFlag.UNINTERESTING)) {
+ switch (object.getType()) {
+ case Constants.OBJ_TREE:
+ case Constants.OBJ_BLOB:
+ ObjectToPack otp = new ObjectToPack(object);
+ otp.setPathHash(pathHashCode);
+ otp.setDoNotDelta(true);
+ edgeObjects.add(otp);
+ thin = true;
+ break;
+ }
+ return;
+ }
+
+ final ObjectToPack otp;
+ if (reuseSupport != null)
+ otp = reuseSupport.newObjectToPack(object);
+ else
+ otp = new ObjectToPack(object);
+ otp.setPathHash(pathHashCode);
+
+ try {
+ objectsLists[object.getType()].add(otp);
+ } catch (ArrayIndexOutOfBoundsException x) {
+ throw new IncorrectObjectTypeException(object,
+ JGitText.get().incorrectObjectType_COMMITnorTREEnorBLOBnorTAG);
+ } catch (UnsupportedOperationException x) {
+ // index pointing to "dummy" empty list
+ throw new IncorrectObjectTypeException(object,
+ JGitText.get().incorrectObjectType_COMMITnorTREEnorBLOBnorTAG);
+ }
+ objectsMap.add(otp);
+ }
+
+ /**
+ * Select an object representation for this writer.
+ * <p>
+ * An {@link ObjectReader} implementation should invoke this method once for
+ * each representation available for an object, to allow the writer to find
+ * the most suitable one for the output.
+ *
+ * @param otp
+ * the object being packed.
+ * @param next
+ * the next available representation from the repository.
+ */
+ public void select(ObjectToPack otp, StoredObjectRepresentation next) {
+ int nFmt = next.getFormat();
+ int nWeight;
+ if (otp.isReuseAsIs()) {
+ // We've already chosen to reuse a packed form, if next
+ // cannot beat that break out early.
+ //
+ if (PACK_WHOLE < nFmt)
+ return; // next isn't packed
+ else if (PACK_DELTA < nFmt && otp.isDeltaRepresentation())
+ return; // next isn't a delta, but we are
+
+ nWeight = next.getWeight();
+ if (otp.getWeight() <= nWeight)
+ return; // next would be bigger
+ } else
+ nWeight = next.getWeight();
+
+ if (nFmt == PACK_DELTA && reuseDeltas) {
+ ObjectId baseId = next.getDeltaBase();
+ ObjectToPack ptr = objectsMap.get(baseId);
+ if (ptr != null) {
+ otp.setDeltaBase(ptr);
+ otp.setReuseAsIs();
+ otp.setWeight(nWeight);
+ } else if (thin && edgeObjects.contains(baseId)) {
+ otp.setDeltaBase(baseId);
+ otp.setReuseAsIs();
+ otp.setWeight(nWeight);
+ } else {
+ otp.clearDeltaBase();
+ otp.clearReuseAsIs();
+ }
+ } else if (nFmt == PACK_WHOLE && config.isReuseObjects()) {
+ otp.clearDeltaBase();
+ otp.setReuseAsIs();
+ otp.setWeight(nWeight);
+ } else {
+ otp.clearDeltaBase();
+ otp.clearReuseAsIs();
+ }
+
+ otp.select(next);
+ }
+}
diff --git a/org.eclipse.jgit/src/org/eclipse/jgit/storage/pack/StoredObjectRepresentation.java b/org.eclipse.jgit/src/org/eclipse/jgit/storage/pack/StoredObjectRepresentation.java
new file mode 100644
index 0000000..334ea5e
--- /dev/null
+++ b/org.eclipse.jgit/src/org/eclipse/jgit/storage/pack/StoredObjectRepresentation.java
@@ -0,0 +1,90 @@
+/*
+ * Copyright (C) 2010, Google Inc.
+ * and other copyright owners as documented in the project's IP log.
+ *
+ * This program and the accompanying materials are made available
+ * under the terms of the Eclipse Distribution License v1.0 which
+ * accompanies this distribution, is reproduced below, and is
+ * available at http://www.eclipse.org/org/documents/edl-v10.php
+ *
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or
+ * without modification, are permitted provided that the following
+ * conditions are met:
+ *
+ * - Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ *
+ * - Redistributions in binary form must reproduce the above
+ * copyright notice, this list of conditions and the following
+ * disclaimer in the documentation and/or other materials provided
+ * with the distribution.
+ *
+ * - Neither the name of the Eclipse Foundation, Inc. nor the
+ * names of its contributors may be used to endorse or promote
+ * products derived from this software without specific prior
+ * written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND
+ * CONTRIBUTORS "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES,
+ * INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
+ * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR
+ * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
+ * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT
+ * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
+ * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER
+ * CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT,
+ * STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
+ * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF
+ * ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ */
+
+package org.eclipse.jgit.storage.pack;
+
+import org.eclipse.jgit.lib.ObjectId;
+
+/**
+ * An object representation {@link PackWriter} can consider for packing.
+ */
+public class StoredObjectRepresentation {
+ /** Special unknown value for {@link #getWeight()}. */
+ public static final int WEIGHT_UNKNOWN = Integer.MAX_VALUE;
+
+ /** Stored in pack format, as a delta to another object. */
+ public static final int PACK_DELTA = 0;
+
+ /** Stored in pack format, without delta. */
+ public static final int PACK_WHOLE = 1;
+
+ /** Only available after inflating to canonical format. */
+ public static final int FORMAT_OTHER = 2;
+
+ /**
+ * @return relative size of this object's packed form. The special value
+ * {@link #WEIGHT_UNKNOWN} can be returned to indicate the
+ * implementation doesn't know, or cannot supply the weight up
+ * front.
+ */
+ public int getWeight() {
+ return WEIGHT_UNKNOWN;
+ }
+
+ /**
+ * @return true if this is a delta against another object and this is stored
+ * in pack delta format.
+ */
+ public int getFormat() {
+ return FORMAT_OTHER;
+ }
+
+ /**
+ * @return identity of the object this delta applies to in order to recover
+ * the original object content. This method should only be called if
+ * {@link #getFormat()} returned {@link #PACK_DELTA}.
+ */
+ public ObjectId getDeltaBase() {
+ return null;
+ }
+}
diff --git a/org.eclipse.jgit/src/org/eclipse/jgit/lib/RepositoryChangedEvent.java b/org.eclipse.jgit/src/org/eclipse/jgit/storage/pack/ThreadSafeDeltaCache.java
similarity index 70%
copy from org.eclipse.jgit/src/org/eclipse/jgit/lib/RepositoryChangedEvent.java
copy to org.eclipse.jgit/src/org/eclipse/jgit/storage/pack/ThreadSafeDeltaCache.java
index 495049c..2492a05 100644
--- a/org.eclipse.jgit/src/org/eclipse/jgit/lib/RepositoryChangedEvent.java
+++ b/org.eclipse.jgit/src/org/eclipse/jgit/storage/pack/ThreadSafeDeltaCache.java
@@ -1,5 +1,5 @@
/*
- * Copyright (C) 2008, Robin Rosenberg <robin.rosenberg@dewire.com>
+ * Copyright (C) 2010, Google Inc.
* and other copyright owners as documented in the project's IP log.
*
* This program and the accompanying materials are made available
@@ -41,30 +41,46 @@
* ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
*/
-package org.eclipse.jgit.lib;
+package org.eclipse.jgit.storage.pack;
-/**
- * This class passes information about changed refs to a
- * {@link RepositoryListener}
- *
- * Currently only a reference to the repository is passed.
- */
-public class RepositoryChangedEvent {
- private final Repository repository;
+import java.util.concurrent.locks.ReentrantLock;
- RepositoryChangedEvent(final Repository repository) {
- this.repository = repository;
- }
+class ThreadSafeDeltaCache extends DeltaCache {
+ private final ReentrantLock lock;
- /**
- * @return the repository that was changed
- */
- public Repository getRepository() {
- return repository;
+ ThreadSafeDeltaCache(PackConfig pc) {
+ super(pc);
+ lock = new ReentrantLock();
}
@Override
- public String toString() {
- return "RepositoryChangedEvent[" + repository + "]";
+ boolean canCache(int length, ObjectToPack src, ObjectToPack res) {
+ lock.lock();
+ try {
+ return super.canCache(length, src, res);
+ } finally {
+ lock.unlock();
+ }
+ }
+
+ @Override
+ void credit(int reservedSize) {
+ lock.lock();
+ try {
+ super.credit(reservedSize);
+ } finally {
+ lock.unlock();
+ }
+ }
+
+ @Override
+ Ref cache(byte[] data, int actLen, int reservedSize) {
+ data = resize(data, actLen);
+ lock.lock();
+ try {
+ return super.cache(data, actLen, reservedSize);
+ } finally {
+ lock.unlock();
+ }
}
}
diff --git a/org.eclipse.jgit/src/org/eclipse/jgit/transport/BasePackFetchConnection.java b/org.eclipse.jgit/src/org/eclipse/jgit/transport/BasePackFetchConnection.java
index 2819ae2..af18f18 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.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.RevWalk;
import org.eclipse.jgit.revwalk.filter.CommitTimeRevFilter;
import org.eclipse.jgit.revwalk.filter.RevFilter;
+import org.eclipse.jgit.storage.file.PackLock;
import org.eclipse.jgit.transport.PacketLineIn.AckNackResult;
import org.eclipse.jgit.util.TemporaryBuffer;
@@ -270,6 +270,12 @@ protected void doFetch(final ProgressMonitor monitor,
}
}
+ @Override
+ public void close() {
+ walk.release();
+ super.close();
+ }
+
private int maxTimeWanted(final Collection<Ref> wants) {
int maxTime = 0;
for (final Ref r : wants) {
diff --git a/org.eclipse.jgit/src/org/eclipse/jgit/transport/BasePackPushConnection.java b/org.eclipse.jgit/src/org/eclipse/jgit/transport/BasePackPushConnection.java
index 44ccd2d..0838f29 100644
--- a/org.eclipse.jgit/src/org/eclipse/jgit/transport/BasePackPushConnection.java
+++ b/org.eclipse.jgit/src/org/eclipse/jgit/transport/BasePackPushConnection.java
@@ -48,6 +48,7 @@
import java.text.MessageFormat;
import java.util.ArrayList;
import java.util.Collection;
+import java.util.List;
import java.util.Map;
import org.eclipse.jgit.JGitText;
@@ -56,9 +57,9 @@
import org.eclipse.jgit.errors.PackProtocolException;
import org.eclipse.jgit.errors.TransportException;
import org.eclipse.jgit.lib.ObjectId;
-import org.eclipse.jgit.lib.PackWriter;
import org.eclipse.jgit.lib.ProgressMonitor;
import org.eclipse.jgit.lib.Ref;
+import org.eclipse.jgit.storage.pack.PackWriter;
import org.eclipse.jgit.transport.RemoteRefUpdate.Status;
/**
@@ -226,25 +227,30 @@ private String enableCapabilities(final ProgressMonitor monitor) {
private void writePack(final Map<String, RemoteRefUpdate> refUpdates,
final ProgressMonitor monitor) throws IOException {
- final PackWriter writer = new PackWriter(local, monitor);
- final ArrayList<ObjectId> remoteObjects = new ArrayList<ObjectId>(
- getRefs().size());
- final ArrayList<ObjectId> newObjects = new ArrayList<ObjectId>(
- refUpdates.size());
+ List<ObjectId> remoteObjects = new ArrayList<ObjectId>(getRefs().size());
+ List<ObjectId> newObjects = new ArrayList<ObjectId>(refUpdates.size());
- for (final Ref r : getRefs())
- remoteObjects.add(r.getObjectId());
- remoteObjects.addAll(additionalHaves);
- for (final RemoteRefUpdate r : refUpdates.values()) {
- if (!ObjectId.zeroId().equals(r.getNewObjectId()))
- newObjects.add(r.getNewObjectId());
+ final long start;
+ final PackWriter writer = new PackWriter(transport.getPackConfig(),
+ local.newObjectReader());
+ try {
+
+ for (final Ref r : getRefs())
+ remoteObjects.add(r.getObjectId());
+ remoteObjects.addAll(additionalHaves);
+ for (final RemoteRefUpdate r : refUpdates.values()) {
+ if (!ObjectId.zeroId().equals(r.getNewObjectId()))
+ newObjects.add(r.getNewObjectId());
+ }
+
+ writer.setThin(thinPack);
+ writer.setDeltaBaseAsOffset(capableOfsDelta);
+ writer.preparePack(monitor, newObjects, remoteObjects);
+ start = System.currentTimeMillis();
+ writer.writePack(monitor, monitor, out);
+ } finally {
+ writer.release();
}
-
- writer.setThin(thinPack);
- writer.setDeltaBaseAsOffset(capableOfsDelta);
- writer.preparePack(newObjects, remoteObjects);
- final long start = System.currentTimeMillis();
- writer.writePack(out);
out.flush();
packTransferTime = System.currentTimeMillis() - start;
}
diff --git a/org.eclipse.jgit/src/org/eclipse/jgit/transport/BundleFetchConnection.java b/org.eclipse.jgit/src/org/eclipse/jgit/transport/BundleFetchConnection.java
index 3b97dfc..126acab 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.lib.Constants;
import org.eclipse.jgit.lib.ObjectId;
import org.eclipse.jgit.lib.ObjectIdRef;
-import org.eclipse.jgit.lib.PackLock;
import org.eclipse.jgit.lib.ProgressMonitor;
import org.eclipse.jgit.lib.Ref;
import org.eclipse.jgit.revwalk.RevCommit;
import org.eclipse.jgit.revwalk.RevFlag;
import org.eclipse.jgit.revwalk.RevObject;
import org.eclipse.jgit.revwalk.RevWalk;
+import org.eclipse.jgit.storage.file.PackLock;
import org.eclipse.jgit.util.IO;
import org.eclipse.jgit.util.RawParseUtils;
@@ -213,57 +213,65 @@ private void verifyPrerequisites() throws TransportException {
return;
final RevWalk rw = new RevWalk(transport.local);
- final RevFlag PREREQ = rw.newFlag("PREREQ");
- final RevFlag SEEN = rw.newFlag("SEEN");
-
- final Map<ObjectId, String> missing = new HashMap<ObjectId, String>();
- final List<RevObject> commits = new ArrayList<RevObject>();
- for (final Map.Entry<ObjectId, String> e : prereqs.entrySet()) {
- ObjectId p = e.getKey();
- try {
- final RevCommit c = rw.parseCommit(p);
- if (!c.has(PREREQ)) {
- c.add(PREREQ);
- commits.add(c);
- }
- } 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);
-
- 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;
+ final RevFlag PREREQ = rw.newFlag("PREREQ");
+ final RevFlag SEEN = rw.newFlag("SEEN");
+
+ final Map<ObjectId, String> missing = new HashMap<ObjectId, String>();
+ final List<RevObject> commits = new ArrayList<RevObject>();
+ for (final Map.Entry<ObjectId, String> e : prereqs.entrySet()) {
+ ObjectId p = e.getKey();
+ try {
+ final RevCommit c = rw.parseCommit(p);
+ if (!c.has(PREREQ)) {
+ c.add(PREREQ);
+ commits.add(c);
+ }
+ } 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 (IOException err) {
- throw new TransportException(transport.uri, JGitText.get().cannotReadObject, err);
- }
+ if (!missing.isEmpty())
+ throw new MissingBundlePrerequisiteException(transport.uri,
+ missing);
- if (remaining > 0) {
- for (final RevObject o : commits) {
- if (!o.has(SEEN))
- missing.put(o, prereqs.get(o));
+ 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.
+ }
}
- throw new MissingBundlePrerequisiteException(transport.uri, missing);
+
+ 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);
+ }
+
+ if (remaining > 0) {
+ for (final RevObject o : commits) {
+ if (!o.has(SEEN))
+ missing.put(o, prereqs.get(o));
+ }
+ throw new MissingBundlePrerequisiteException(transport.uri,
+ missing);
+ }
+ } finally {
+ rw.release();
}
}
diff --git a/org.eclipse.jgit/src/org/eclipse/jgit/transport/BundleWriter.java b/org.eclipse.jgit/src/org/eclipse/jgit/transport/BundleWriter.java
index 7e91557..b513412 100644
--- a/org.eclipse.jgit/src/org/eclipse/jgit/transport/BundleWriter.java
+++ b/org.eclipse.jgit/src/org/eclipse/jgit/transport/BundleWriter.java
@@ -57,11 +57,12 @@
import org.eclipse.jgit.lib.AnyObjectId;
import org.eclipse.jgit.lib.Constants;
import org.eclipse.jgit.lib.ObjectId;
-import org.eclipse.jgit.lib.PackWriter;
import org.eclipse.jgit.lib.ProgressMonitor;
import org.eclipse.jgit.lib.Ref;
import org.eclipse.jgit.lib.Repository;
import org.eclipse.jgit.revwalk.RevCommit;
+import org.eclipse.jgit.storage.pack.PackConfig;
+import org.eclipse.jgit.storage.pack.PackWriter;
/**
* Creates a Git bundle file, for sneaker-net transport to another system.
@@ -81,27 +82,38 @@
* overall bundle size.
*/
public class BundleWriter {
- private final PackWriter packWriter;
+ private final Repository db;
private final Map<String, ObjectId> include;
private final Set<RevCommit> assume;
+ private PackConfig packConfig;
+
/**
* Create a writer for a bundle.
*
* @param repo
* repository where objects are stored.
- * @param monitor
- * operations progress monitor.
*/
- public BundleWriter(final Repository repo, final ProgressMonitor monitor) {
- packWriter = new PackWriter(repo, monitor);
+ public BundleWriter(final Repository repo) {
+ db = repo;
include = new TreeMap<String, ObjectId>();
assume = new HashSet<RevCommit>();
}
/**
+ * Set the configuration used by the pack generator.
+ *
+ * @param pc
+ * configuration controlling packing parameters. If null the
+ * source repository's settings will be used.
+ */
+ public void setPackConfig(PackConfig pc) {
+ this.packConfig = pc;
+ }
+
+ /**
* Include an object (and everything reachable from it) in the bundle.
*
* @param name
@@ -155,6 +167,8 @@ public void assume(final RevCommit c) {
* <p>
* This method can only be called once per BundleWriter instance.
*
+ * @param monitor
+ * progress monitor to report bundle writing status to.
* @param os
* the stream the bundle is written to. The stream should be
* buffered by the caller. The caller is responsible for closing
@@ -164,38 +178,47 @@ public void assume(final RevCommit c) {
* the bundle, or writing compressed object data to the output
* stream.
*/
- public void writeBundle(OutputStream os) throws IOException {
- final HashSet<ObjectId> inc = new HashSet<ObjectId>();
- final HashSet<ObjectId> exc = new HashSet<ObjectId>();
- inc.addAll(include.values());
- for (final RevCommit r : assume)
- exc.add(r.getId());
- packWriter.setThin(exc.size() > 0);
- packWriter.preparePack(inc, exc);
+ public void writeBundle(ProgressMonitor monitor, OutputStream os)
+ throws IOException {
+ PackConfig pc = packConfig;
+ if (pc == null)
+ pc = new PackConfig(db);
+ PackWriter packWriter = new PackWriter(pc, db.newObjectReader());
+ try {
+ final HashSet<ObjectId> inc = new HashSet<ObjectId>();
+ final HashSet<ObjectId> exc = new HashSet<ObjectId>();
+ inc.addAll(include.values());
+ for (final RevCommit r : assume)
+ exc.add(r.getId());
+ packWriter.setThin(exc.size() > 0);
+ packWriter.preparePack(monitor, inc, exc);
- final Writer w = new OutputStreamWriter(os, Constants.CHARSET);
- w.write(TransportBundle.V2_BUNDLE_SIGNATURE);
- w.write('\n');
+ final Writer w = new OutputStreamWriter(os, Constants.CHARSET);
+ w.write(TransportBundle.V2_BUNDLE_SIGNATURE);
+ w.write('\n');
- final char[] tmp = new char[Constants.OBJECT_ID_STRING_LENGTH];
- for (final RevCommit a : assume) {
- w.write('-');
- a.copyTo(tmp, w);
- if (a.getRawBuffer() != null) {
- w.write(' ');
- w.write(a.getShortMessage());
+ final char[] tmp = new char[Constants.OBJECT_ID_STRING_LENGTH];
+ for (final RevCommit a : assume) {
+ w.write('-');
+ a.copyTo(tmp, w);
+ if (a.getRawBuffer() != null) {
+ w.write(' ');
+ w.write(a.getShortMessage());
+ }
+ w.write('\n');
}
- w.write('\n');
- }
- for (final Map.Entry<String, ObjectId> e : include.entrySet()) {
- e.getValue().copyTo(tmp, w);
- w.write(' ');
- w.write(e.getKey());
- w.write('\n');
- }
+ for (final Map.Entry<String, ObjectId> e : include.entrySet()) {
+ e.getValue().copyTo(tmp, w);
+ w.write(' ');
+ w.write(e.getKey());
+ w.write('\n');
+ }
- w.write('\n');
- w.flush();
- packWriter.writePack(os);
+ w.write('\n');
+ w.flush();
+ packWriter.writePack(monitor, monitor, os);
+ } finally {
+ packWriter.release();
+ }
}
}
diff --git a/org.eclipse.jgit/src/org/eclipse/jgit/transport/Daemon.java b/org.eclipse.jgit/src/org/eclipse/jgit/transport/Daemon.java
index aa2e252..0bc5fb3 100644
--- a/org.eclipse.jgit/src/org/eclipse/jgit/transport/Daemon.java
+++ b/org.eclipse.jgit/src/org/eclipse/jgit/transport/Daemon.java
@@ -63,6 +63,7 @@
import org.eclipse.jgit.lib.Repository;
import org.eclipse.jgit.lib.RepositoryCache;
import org.eclipse.jgit.lib.RepositoryCache.FileKey;
+import org.eclipse.jgit.storage.pack.PackConfig;
import org.eclipse.jgit.util.FS;
/** Basic daemon for the anonymous <code>git://</code> transport protocol. */
@@ -90,6 +91,8 @@ public class Daemon {
private int timeout;
+ private PackConfig packConfig;
+
/** Configure a daemon to listen on any available network port. */
public Daemon() {
this(null);
@@ -120,6 +123,7 @@ protected void execute(final DaemonClient dc,
final UploadPack rp = new UploadPack(db);
final InputStream in = dc.getInputStream();
rp.setTimeout(Daemon.this.getTimeout());
+ rp.setPackConfig(Daemon.this.packConfig);
rp.upload(in, dc.getOutputStream(), null);
}
}, new DaemonService("receive-pack", "receivepack") {
@@ -243,6 +247,17 @@ public void setTimeout(final int seconds) {
}
/**
+ * Set the configuration used by the pack generator.
+ *
+ * @param pc
+ * configuration controlling packing parameters. If null the
+ * source repository's settings will be used.
+ */
+ public void setPackConfig(PackConfig pc) {
+ this.packConfig = pc;
+ }
+
+ /**
* Start this daemon on a background thread.
*
* @throws IOException
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 50c0866..9dc54da 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 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 fc203f6..f747616 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.NotSupportedException;
import org.eclipse.jgit.errors.TransportException;
import org.eclipse.jgit.lib.Constants;
-import org.eclipse.jgit.lib.LockFile;
import org.eclipse.jgit.lib.ObjectId;
-import org.eclipse.jgit.lib.PackLock;
import org.eclipse.jgit.lib.ProgressMonitor;
import org.eclipse.jgit.lib.Ref;
import org.eclipse.jgit.lib.Repository;
import org.eclipse.jgit.revwalk.ObjectWalk;
import org.eclipse.jgit.revwalk.RevWalk;
+import org.eclipse.jgit.storage.file.LockFile;
+import org.eclipse.jgit.storage.file.PackLock;
class FetchProcess {
/** Transport we will fetch over. */
@@ -176,16 +176,21 @@ else if (tagopt == TagOpt.FETCH_TAGS)
}
final RevWalk walk = new RevWalk(transport.local);
- if (transport.isRemoveDeletedRefs())
- deleteStaleTrackingRefs(result, walk);
- for (TrackingRefUpdate u : localUpdates) {
- try {
- u.update(walk);
- result.add(u);
- } catch (IOException err) {
- throw new TransportException(MessageFormat.format(
- JGitText.get().failureUpdatingTrackingRef, u.getLocalName(), err.getMessage()), err);
+ try {
+ if (transport.isRemoveDeletedRefs())
+ deleteStaleTrackingRefs(result, walk);
+ for (TrackingRefUpdate u : localUpdates) {
+ try {
+ u.update(walk);
+ result.add(u);
+ } catch (IOException err) {
+ throw new TransportException(MessageFormat.format(JGitText
+ .get().failureUpdatingTrackingRef,
+ u.getLocalName(), err.getMessage()), err);
+ }
}
+ } finally {
+ walk.release();
}
if (!fetchHeadUpdates.isEmpty()) {
@@ -271,8 +276,11 @@ private void removeFetchHeadRecord(final ObjectId want) {
}
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"),
+ transport.local.getFS());
try {
if (lock.lock()) {
final Writer w = new OutputStreamWriter(lock.getOutputStream());
@@ -294,11 +302,15 @@ private void updateFETCH_HEAD(final FetchResult result) throws IOException {
private boolean askForIsComplete() throws TransportException {
try {
final ObjectWalk ow = new ObjectWalk(transport.local);
- for (final ObjectId want : askFor.keySet())
- ow.markStart(ow.parseAny(want));
- for (final Ref ref : transport.local.getAllRefs().values())
- ow.markUninteresting(ow.parseAny(ref.getObjectId()));
- ow.checkConnectivity();
+ try {
+ for (final ObjectId want : askFor.keySet())
+ ow.markStart(ow.parseAny(want));
+ for (final Ref ref : transport.local.getAllRefs().values())
+ ow.markUninteresting(ow.parseAny(ref.getObjectId()));
+ ow.checkConnectivity();
+ } finally {
+ ow.release();
+ }
return true;
} catch (MissingObjectException e) {
return false;
diff --git a/org.eclipse.jgit/src/org/eclipse/jgit/transport/IndexPack.java b/org.eclipse.jgit/src/org/eclipse/jgit/transport/IndexPack.java
index 491227d..2daa105 100644
--- a/org.eclipse.jgit/src/org/eclipse/jgit/transport/IndexPack.java
+++ b/org.eclipse.jgit/src/org/eclipse/jgit/transport/IndexPack.java
@@ -65,8 +65,8 @@
import org.eclipse.jgit.errors.CorruptObjectException;
import org.eclipse.jgit.errors.MissingObjectException;
import org.eclipse.jgit.lib.AnyObjectId;
-import org.eclipse.jgit.lib.BinaryDelta;
import org.eclipse.jgit.lib.Constants;
+import org.eclipse.jgit.lib.CoreConfig;
import org.eclipse.jgit.lib.InflaterCache;
import org.eclipse.jgit.lib.MutableObjectId;
import org.eclipse.jgit.lib.ObjectChecker;
@@ -74,11 +74,12 @@
import org.eclipse.jgit.lib.ObjectId;
import org.eclipse.jgit.lib.ObjectIdSubclassMap;
import org.eclipse.jgit.lib.ObjectLoader;
-import org.eclipse.jgit.lib.PackIndexWriter;
-import org.eclipse.jgit.lib.PackLock;
import org.eclipse.jgit.lib.ProgressMonitor;
import org.eclipse.jgit.lib.Repository;
-import org.eclipse.jgit.lib.WindowCursor;
+import org.eclipse.jgit.lib.ObjectReader;
+import org.eclipse.jgit.storage.file.PackIndexWriter;
+import org.eclipse.jgit.storage.file.PackLock;
+import org.eclipse.jgit.storage.pack.BinaryDelta;
import org.eclipse.jgit.util.NB;
/** Indexes Git pack files for local use. */
@@ -127,7 +128,8 @@ public static IndexPack create(final Repository db, final InputStream is)
base = new File(objdir, n.substring(0, n.length() - suffix.length()));
final IndexPack ip = new IndexPack(db, is, base);
- ip.setIndexVersion(db.getConfig().getCore().getPackIndexVersion());
+ ip.setIndexVersion(db.getConfig().get(CoreConfig.KEY)
+ .getPackIndexVersion());
return ip;
}
@@ -224,7 +226,7 @@ private static enum Source {
/** If {@link #fixThin} this is the last byte of the original checksum. */
private long originalEOF;
- private WindowCursor readCurs;
+ private ObjectReader readCurs;
/**
* Create a new pack indexer utility.
@@ -243,7 +245,7 @@ public IndexPack(final Repository db, final InputStream src,
objectDatabase = db.getObjectDatabase().newCachedDatabase();
in = src;
inflater = InflaterCache.get();
- readCurs = new WindowCursor();
+ readCurs = objectDatabase.newReader();
buf = new byte[BUFFER_SIZE];
skipBuffer = new byte[512];
objectDigest = Constants.newMessageDigest();
@@ -436,12 +438,18 @@ public void index(final ProgressMonitor progress) throws IOException {
} finally {
try {
+ if (readCurs != null)
+ readCurs.release();
+ } finally {
+ readCurs = null;
+ }
+
+ try {
InflaterCache.release(inflater);
} finally {
inflater = null;
objectDatabase.close();
}
- readCurs = WindowCursor.release(readCurs);
progress.endTask();
if (packOut != null)
@@ -598,8 +606,10 @@ private void fixThinPack(final ProgressMonitor progress) throws IOException {
continue;
if (needBaseObjectIds)
baseObjectIds.add(baseId);
- final ObjectLoader ldr = repo.openObject(readCurs, baseId);
- if (ldr == null) {
+ final ObjectLoader ldr;
+ try {
+ ldr = readCurs.open(baseId);
+ } catch (MissingObjectException notFound) {
missing.add(baseId);
continue;
}
@@ -858,12 +868,16 @@ private void verifySafeObject(final AnyObjectId id, final int type,
}
}
- final ObjectLoader ldr = objectDatabase.openObject(readCurs, id);
- if (ldr != null) {
+ try {
+ final ObjectLoader ldr = readCurs.open(id, type);
final byte[] existingData = ldr.getCachedBytes();
- if (ldr.getType() != type || !Arrays.equals(data, existingData)) {
+ if (!Arrays.equals(data, existingData)) {
throw new IOException(MessageFormat.format(JGitText.get().collisionOn, id.name()));
}
+ } catch (MissingObjectException notLocal) {
+ // This is OK, we don't have a copy of the object locally
+ // but the API throws when we try to read it as usually its
+ // an error to read something that doesn't exist.
}
}
@@ -1087,7 +1101,7 @@ public PackLock renameAndOpenPack(final String lockMessage)
final File packDir = new File(repo.getObjectsDirectory(), "pack");
final File finalPack = new File(packDir, "pack-" + name + ".pack");
final File finalIdx = new File(packDir, "pack-" + name + ".idx");
- final PackLock keep = new PackLock(finalPack);
+ final PackLock keep = new PackLock(finalPack, repo.getFS());
if (!packDir.exists() && !packDir.mkdir() && !packDir.exists()) {
// The objects/pack directory isn't present, and we are unable
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 02497cb..6cd796a 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();
+ monitor.beginTask(PROGRESS_OPENING_CONNECTION,
+ ProgressMonitor.UNKNOWN);
- final Map<String, RemoteRefUpdate> preprocessed = prepareRemoteUpdates();
- if (transport.isDryRun())
- modifyUpdatesForDryRun();
- else if (!preprocessed.isEmpty())
- connection.push(monitor, preprocessed);
+ final PushResult res = new PushResult();
+ connection = transport.openPush();
+ try {
+ res.setAdvertisedRefs(transport.getURI(), connection
+ .getRefsMap());
+ res.setRemoteUpdates(toPush);
+ monitor.endTask();
+
+ final Map<String, RemoteRefUpdate> preprocessed = prepareRemoteUpdates();
+ if (transport.isDryRun())
+ modifyUpdatesForDryRun();
+ else if (!preprocessed.isEmpty())
+ connection.push(monitor, preprocessed);
+ } finally {
+ connection.close();
+ res.addMessages(connection.getMessages());
+ }
+ if (!transport.isDryRun())
+ updateTrackingRefs();
+ for (final RemoteRefUpdate rru : toPush.values()) {
+ final TrackingRefUpdate tru = rru.getTrackingRefUpdate();
+ if (tru != null)
+ res.add(tru);
+ }
+ return res;
} finally {
- connection.close();
- res.addMessages(connection.getMessages());
+ walker.release();
}
- if (!transport.isDryRun())
- updateTrackingRefs();
- for (final RemoteRefUpdate rru : toPush.values()) {
- final TrackingRefUpdate tru = rru.getTrackingRefUpdate();
- if (tru != null)
- res.add(tru);
- }
- return res;
}
private Map<String, RemoteRefUpdate> prepareRemoteUpdates()
diff --git a/org.eclipse.jgit/src/org/eclipse/jgit/transport/ReceivePack.java b/org.eclipse.jgit/src/org/eclipse/jgit/transport/ReceivePack.java
index e42b7fe..6b0a9b6 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.NullProgressMonitor;
import org.eclipse.jgit.lib.ObjectId;
import org.eclipse.jgit.lib.ObjectIdSubclassMap;
-import org.eclipse.jgit.lib.PackLock;
import org.eclipse.jgit.lib.PersonIdent;
import org.eclipse.jgit.lib.Ref;
import org.eclipse.jgit.lib.RefUpdate;
@@ -84,9 +83,9 @@
import org.eclipse.jgit.revwalk.RevCommit;
import org.eclipse.jgit.revwalk.RevFlag;
import org.eclipse.jgit.revwalk.RevObject;
-import org.eclipse.jgit.revwalk.RevTag;
import org.eclipse.jgit.revwalk.RevTree;
import org.eclipse.jgit.revwalk.RevWalk;
+import org.eclipse.jgit.storage.file.PackLock;
import org.eclipse.jgit.transport.ReceiveCommand.Result;
import org.eclipse.jgit.transport.RefAdvertiser.PacketLineOutRefAdvertiser;
import org.eclipse.jgit.util.io.InterruptTimer;
@@ -575,6 +574,7 @@ public void receive(final InputStream input, final OutputStream output,
service();
} finally {
+ walk.release();
try {
if (pckOut != null)
pckOut.flush();
@@ -697,7 +697,7 @@ public void sendAdvertisedRefs(final RefAdvertiser adv) throws IOException {
adv.send(refs);
if (head != null && !head.isSymbolic())
adv.advertiseHave(head.getObjectId());
- adv.includeAdditionalHaves();
+ adv.includeAdditionalHaves(db);
if (adv.isEmpty())
adv.advertiseId(ObjectId.zeroId(), "capabilities^{}");
adv.end();
@@ -818,8 +818,7 @@ private void checkConnectivity() throws IOException {
ow.markUninteresting(o);
if (checkReferencedIsReachable && !baseObjects.isEmpty()) {
- while (o instanceof RevTag)
- o = ((RevTag) o).getObject();
+ o = ow.peel(o);
if (o instanceof RevCommit)
o = ((RevCommit) o).getTree();
if (o instanceof RevTree)
diff --git a/org.eclipse.jgit/src/org/eclipse/jgit/transport/RefAdvertiser.java b/org.eclipse.jgit/src/org/eclipse/jgit/transport/RefAdvertiser.java
index 694a2e0..df0afe7 100644
--- a/org.eclipse.jgit/src/org/eclipse/jgit/transport/RefAdvertiser.java
+++ b/org.eclipse.jgit/src/org/eclipse/jgit/transport/RefAdvertiser.java
@@ -49,10 +49,9 @@
import java.util.Set;
import java.util.SortedMap;
-import org.eclipse.jgit.lib.AlternateRepositoryDatabase;
import org.eclipse.jgit.lib.AnyObjectId;
import org.eclipse.jgit.lib.Constants;
-import org.eclipse.jgit.lib.ObjectDatabase;
+import org.eclipse.jgit.lib.ObjectId;
import org.eclipse.jgit.lib.Ref;
import org.eclipse.jgit.lib.RefComparator;
import org.eclipse.jgit.lib.Repository;
@@ -126,7 +125,7 @@ public void init(final RevWalk protoWalk, final RevFlag advertisedFlag) {
* <ul>
* <li>{@link #send(Map)}
* <li>{@link #advertiseHave(AnyObjectId)}
- * <li>{@link #includeAdditionalHaves()}
+ * <li>{@link #includeAdditionalHaves(Repository)}
* </ul>
*
* @param deref
@@ -144,7 +143,7 @@ public void setDerefTags(final boolean deref) {
* <ul>
* <li>{@link #send(Map)}
* <li>{@link #advertiseHave(AnyObjectId)}
- * <li>{@link #includeAdditionalHaves()}
+ * <li>{@link #includeAdditionalHaves(Repository)}
* </ul>
*
* @param name
@@ -212,24 +211,15 @@ public void advertiseHave(AnyObjectId id) throws IOException {
/**
* Include references of alternate repositories as {@code .have} lines.
*
+ * @param src
+ * repository to get the additional reachable objects from.
* @throws IOException
* the underlying output stream failed to write out an
* advertisement record.
*/
- public void includeAdditionalHaves() throws IOException {
- additionalHaves(walk.getRepository().getObjectDatabase());
- }
-
- private void additionalHaves(final ObjectDatabase db) throws IOException {
- if (db instanceof AlternateRepositoryDatabase)
- additionalHaves(((AlternateRepositoryDatabase) db).getRepository());
- for (ObjectDatabase alt : db.getAlternates())
- additionalHaves(alt);
- }
-
- private void additionalHaves(final Repository alt) throws IOException {
- for (final Ref r : alt.getAllRefs().values())
- advertiseHave(r.getObjectId());
+ public void includeAdditionalHaves(Repository src) throws IOException {
+ for (ObjectId id : src.getAdditionalHaves())
+ advertiseHave(id);
}
/** @return true if no advertisements have been sent yet. */
diff --git a/org.eclipse.jgit/src/org/eclipse/jgit/transport/RemoteRefUpdate.java b/org.eclipse.jgit/src/org/eclipse/jgit/transport/RemoteRefUpdate.java
index 1b17c9f..37e03fd 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 String toString() {
+ "..." + (newObjectId != null ? newObjectId.abbreviate(localDb).name() : "(null)")
+ (fastForward ? ", fastForward" : "")
+ ", srcRef=" + srcRef + (forceUpdate ? ", forceUpdate" : "") + ", message=" + (message != null ? "\""
- + message + "\"" : "null") + ", " + localDb.getDirectory() + "]";
+ + message + "\"" : "null") + "]";
}
}
diff --git a/org.eclipse.jgit/src/org/eclipse/jgit/transport/Transport.java b/org.eclipse.jgit/src/org/eclipse/jgit/transport/Transport.java
index e1988a6..500cf0c 100644
--- a/org.eclipse.jgit/src/org/eclipse/jgit/transport/Transport.java
+++ b/org.eclipse.jgit/src/org/eclipse/jgit/transport/Transport.java
@@ -66,6 +66,7 @@
import org.eclipse.jgit.lib.Ref;
import org.eclipse.jgit.lib.Repository;
import org.eclipse.jgit.lib.TransferConfig;
+import org.eclipse.jgit.storage.pack.PackConfig;
import org.eclipse.jgit.util.FS;
/**
@@ -554,6 +555,9 @@ private static String findTrackingRefName(final String remoteName,
/** Timeout in seconds to wait before aborting an IO read or write. */
private int timeout;
+ /** Pack configuration used by this transport to make pack file. */
+ private PackConfig packConfig;
+
/**
* Create a new transport instance.
*
@@ -566,7 +570,7 @@ private static String findTrackingRefName(final String remoteName,
* URI passed to {@link #open(Repository, URIish)}.
*/
protected Transport(final Repository local, final URIish uri) {
- final TransferConfig tc = local.getConfig().getTransfer();
+ final TransferConfig tc = local.getConfig().get(TransferConfig.KEY);
this.local = local;
this.uri = uri;
this.checkFetchedObjects = tc.isFsckObjects();
@@ -792,6 +796,32 @@ public void setTimeout(final int seconds) {
}
/**
+ * Get the configuration used by the pack generator to make packs.
+ *
+ * If {@link #setPackConfig(PackConfig)} was previously given null a new
+ * PackConfig is created on demand by this method using the source
+ * repository's settings.
+ *
+ * @return the pack configuration. Never null.
+ */
+ public PackConfig getPackConfig() {
+ if (packConfig == null)
+ packConfig = new PackConfig(local);
+ return packConfig;
+ }
+
+ /**
+ * Set the configuration used by the pack generator.
+ *
+ * @param pc
+ * configuration controlling packing parameters. If null the
+ * source repository's settings will be used.
+ */
+ public void setPackConfig(PackConfig pc) {
+ packConfig = pc;
+ }
+
+ /**
* Fetch objects and refs from the remote repository to the local one.
* <p>
* This is a utility function providing standard fetch behavior. Local
diff --git a/org.eclipse.jgit/src/org/eclipse/jgit/transport/TransportAmazonS3.java b/org.eclipse.jgit/src/org/eclipse/jgit/transport/TransportAmazonS3.java
index 56a5c97..79b88b6 100644
--- a/org.eclipse.jgit/src/org/eclipse/jgit/transport/TransportAmazonS3.java
+++ b/org.eclipse.jgit/src/org/eclipse/jgit/transport/TransportAmazonS3.java
@@ -126,23 +126,7 @@ static boolean canHandle(final URIish uri) {
throws NotSupportedException {
super(local, uri);
- Properties props = null;
- File propsFile = new File(local.getDirectory(), uri.getUser());
- if (!propsFile.isFile())
- propsFile = new File(local.getFS().userHome(), uri.getUser());
- if (propsFile.isFile()) {
- try {
- props = AmazonS3.properties(propsFile);
- } catch (IOException e) {
- throw new NotSupportedException(MessageFormat.format(JGitText.get().cannotReadFile, propsFile), e);
- }
- } else {
- props = new Properties();
- props.setProperty("accesskey", uri.getUser());
- props.setProperty("secretkey", uri.getPass());
- }
-
- s3 = new AmazonS3(props);
+ s3 = new AmazonS3(loadProperties());
bucket = uri.getHost();
String p = uri.getPath();
@@ -153,6 +137,33 @@ static boolean canHandle(final URIish uri) {
keyPrefix = p;
}
+ private Properties loadProperties() throws NotSupportedException {
+ if (local.getDirectory() != null) {
+ File propsFile = new File(local.getDirectory(), uri.getUser());
+ if (propsFile.isFile())
+ return loadPropertiesFile(propsFile);
+ }
+
+ File propsFile = new File(local.getFS().userHome(), uri.getUser());
+ if (propsFile.isFile())
+ return loadPropertiesFile(propsFile);
+
+ Properties props = new Properties();
+ props.setProperty("accesskey", uri.getUser());
+ props.setProperty("secretkey", uri.getPass());
+ return props;
+ }
+
+ private static Properties loadPropertiesFile(File propsFile)
+ throws NotSupportedException {
+ try {
+ return AmazonS3.properties(propsFile);
+ } catch (IOException e) {
+ throw new NotSupportedException(MessageFormat.format(
+ JGitText.get().cannotReadFile, propsFile), e);
+ }
+ }
+
@Override
public FetchConnection openFetch() throws TransportException {
final DatabaseS3 c = new DatabaseS3(bucket, keyPrefix + "/objects");
diff --git a/org.eclipse.jgit/src/org/eclipse/jgit/transport/TransportHttp.java b/org.eclipse.jgit/src/org/eclipse/jgit/transport/TransportHttp.java
index 71e7bf2..0f4c131 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.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 08fd890..c9b18be 100644
--- a/org.eclipse.jgit/src/org/eclipse/jgit/transport/TransportLocal.java
+++ b/org.eclipse.jgit/src/org/eclipse/jgit/transport/TransportLocal.java
@@ -61,6 +61,7 @@
import org.eclipse.jgit.errors.TransportException;
import org.eclipse.jgit.lib.Constants;
import org.eclipse.jgit.lib.Repository;
+import org.eclipse.jgit.storage.file.FileRepository;
import org.eclipse.jgit.util.FS;
import org.eclipse.jgit.util.io.MessageWriter;
import org.eclipse.jgit.util.io.StreamCopyThread;
@@ -174,7 +175,7 @@ class InternalLocalFetchConnection extends BasePackFetchConnection {
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 InternalLocalPushConnection extends BasePackPushConnection {
final Repository dst;
try {
- dst = new Repository(remoteGitDir);
+ dst = new FileRepository(remoteGitDir);
} catch (IOException err) {
throw new TransportException(uri, JGitText.get().notAGitDirectory);
}
diff --git a/org.eclipse.jgit/src/org/eclipse/jgit/transport/UploadPack.java b/org.eclipse.jgit/src/org/eclipse/jgit/transport/UploadPack.java
index 77cc1a6..16d56df 100644
--- a/org.eclipse.jgit/src/org/eclipse/jgit/transport/UploadPack.java
+++ b/org.eclipse.jgit/src/org/eclipse/jgit/transport/UploadPack.java
@@ -56,10 +56,10 @@
import java.util.Set;
import org.eclipse.jgit.JGitText;
+import org.eclipse.jgit.errors.MissingObjectException;
import org.eclipse.jgit.errors.PackProtocolException;
import org.eclipse.jgit.lib.NullProgressMonitor;
import org.eclipse.jgit.lib.ObjectId;
-import org.eclipse.jgit.lib.PackWriter;
import org.eclipse.jgit.lib.ProgressMonitor;
import org.eclipse.jgit.lib.Ref;
import org.eclipse.jgit.lib.Repository;
@@ -69,6 +69,8 @@
import org.eclipse.jgit.revwalk.RevObject;
import org.eclipse.jgit.revwalk.RevTag;
import org.eclipse.jgit.revwalk.RevWalk;
+import org.eclipse.jgit.storage.pack.PackConfig;
+import org.eclipse.jgit.storage.pack.PackWriter;
import org.eclipse.jgit.transport.BasePackFetchConnection.MultiAck;
import org.eclipse.jgit.transport.RefAdvertiser.PacketLineOutRefAdvertiser;
import org.eclipse.jgit.util.io.InterruptTimer;
@@ -101,6 +103,9 @@ public class UploadPack {
/** Revision traversal support over {@link #db}. */
private final RevWalk walk;
+ /** Configuration to pass into the PackWriter. */
+ private PackConfig packConfig;
+
/** Timeout in seconds to wait for client interaction. */
private int timeout;
@@ -258,6 +263,17 @@ public void setRefFilter(final RefFilter refFilter) {
}
/**
+ * Set the configuration used by the pack generator.
+ *
+ * @param pc
+ * configuration controlling packing parameters. If null the
+ * source repository's settings will be used.
+ */
+ public void setPackConfig(PackConfig pc) {
+ this.packConfig = pc;
+ }
+
+ /**
* Execute the upload task on the socket.
*
* @param input
@@ -295,6 +311,7 @@ public void upload(final InputStream input, final OutputStream output,
pckOut = new PacketLineOut(rawOut);
service();
} finally {
+ walk.release();
if (timer != null) {
try {
timer.terminate();
@@ -393,11 +410,15 @@ private void recvWants() throws IOException {
}
if (!o.has(ADVERTISED))
throw new PackProtocolException(MessageFormat.format(JGitText.get().notValid, id.name()));
- want(o);
+ try {
+ want(o);
+ } catch (IOException e) {
+ throw new PackProtocolException(MessageFormat.format(JGitText.get().notValid, id.name()), e);
+ }
}
}
- private void want(RevObject o) {
+ private void want(RevObject o) throws MissingObjectException, IOException {
if (!o.has(WANT)) {
o.add(WANT);
wantAll.add(o);
@@ -406,9 +427,7 @@ private void want(RevObject o) {
wantCommits.add((RevCommit) o);
else if (o instanceof RevTag) {
- do {
- o = ((RevTag) o).getObject();
- } while (o instanceof RevTag);
+ o = walk.peel(o);
if (o instanceof RevCommit)
want(o);
}
@@ -544,8 +563,6 @@ private boolean wantSatisfied(final RevCommit want) throws IOException {
}
private void sendPack() throws IOException {
- final boolean thin = options.contains(OPTION_THIN_PACK);
- final boolean progress = !options.contains(OPTION_NO_PROGRESS);
final boolean sideband = options.contains(OPTION_SIDE_BAND)
|| options.contains(OPTION_SIDE_BAND_64K);
@@ -559,32 +576,38 @@ private void sendPack() throws IOException {
packOut = new SideBandOutputStream(SideBandOutputStream.CH_DATA,
bufsz, rawOut);
- if (progress)
+ if (!options.contains(OPTION_NO_PROGRESS))
pm = new SideBandProgressMonitor(new SideBandOutputStream(
SideBandOutputStream.CH_PROGRESS, bufsz, rawOut));
}
- final PackWriter pw;
- pw = new PackWriter(db, pm, NullProgressMonitor.INSTANCE);
- pw.setDeltaBaseAsOffset(options.contains(OPTION_OFS_DELTA));
- pw.setThin(thin);
- pw.preparePack(wantAll, commonBase);
- if (options.contains(OPTION_INCLUDE_TAG)) {
- for (final Ref r : refs.values()) {
- final RevObject o;
- try {
- o = walk.parseAny(r.getObjectId());
- } catch (IOException e) {
- continue;
+ PackConfig cfg = packConfig;
+ if (cfg == null)
+ cfg = new PackConfig(db);
+ final PackWriter pw = new PackWriter(cfg, walk.getObjectReader());
+ try {
+ pw.setDeltaBaseAsOffset(options.contains(OPTION_OFS_DELTA));
+ pw.setThin(options.contains(OPTION_THIN_PACK));
+ pw.preparePack(pm, wantAll, commonBase);
+ if (options.contains(OPTION_INCLUDE_TAG)) {
+ for (final Ref r : refs.values()) {
+ final RevObject o;
+ try {
+ o = walk.parseAny(r.getObjectId());
+ } catch (IOException e) {
+ continue;
+ }
+ if (o.has(WANT) || !(o instanceof RevTag))
+ continue;
+ final RevTag t = (RevTag) o;
+ if (!pw.willInclude(t) && pw.willInclude(t.getObject()))
+ pw.addObject(t);
}
- if (o.has(WANT) || !(o instanceof RevTag))
- continue;
- final RevTag t = (RevTag) o;
- if (!pw.willInclude(t) && pw.willInclude(t.getObject()))
- pw.addObject(t);
}
+ pw.writePack(pm, NullProgressMonitor.INSTANCE, packOut);
+ } finally {
+ pw.release();
}
- pw.writePack(packOut);
packOut.flush();
if (sideband)
diff --git a/org.eclipse.jgit/src/org/eclipse/jgit/transport/WalkFetchConnection.java b/org.eclipse.jgit/src/org/eclipse/jgit/transport/WalkFetchConnection.java
index 6254745..237b431 100644
--- a/org.eclipse.jgit/src/org/eclipse/jgit/transport/WalkFetchConnection.java
+++ b/org.eclipse.jgit/src/org/eclipse/jgit/transport/WalkFetchConnection.java
@@ -48,7 +48,6 @@
import java.io.FileNotFoundException;
import java.io.FileOutputStream;
import java.io.IOException;
-import java.security.MessageDigest;
import java.text.MessageFormat;
import java.util.ArrayList;
import java.util.Collection;
@@ -63,7 +62,6 @@
import org.eclipse.jgit.errors.CompoundException;
import org.eclipse.jgit.errors.CorruptObjectException;
import org.eclipse.jgit.errors.MissingObjectException;
-import org.eclipse.jgit.errors.ObjectWritingException;
import org.eclipse.jgit.errors.TransportException;
import org.eclipse.jgit.lib.AnyObjectId;
import org.eclipse.jgit.lib.Constants;
@@ -71,12 +69,12 @@
import org.eclipse.jgit.lib.MutableObjectId;
import org.eclipse.jgit.lib.ObjectChecker;
import org.eclipse.jgit.lib.ObjectId;
-import org.eclipse.jgit.lib.PackIndex;
-import org.eclipse.jgit.lib.PackLock;
+import org.eclipse.jgit.lib.ObjectInserter;
+import org.eclipse.jgit.lib.ObjectLoader;
+import org.eclipse.jgit.lib.ObjectReader;
import org.eclipse.jgit.lib.ProgressMonitor;
import org.eclipse.jgit.lib.Ref;
import org.eclipse.jgit.lib.Repository;
-import org.eclipse.jgit.lib.UnpackedObjectLoader;
import org.eclipse.jgit.revwalk.DateRevQueue;
import org.eclipse.jgit.revwalk.RevCommit;
import org.eclipse.jgit.revwalk.RevFlag;
@@ -84,6 +82,10 @@
import org.eclipse.jgit.revwalk.RevTag;
import org.eclipse.jgit.revwalk.RevTree;
import org.eclipse.jgit.revwalk.RevWalk;
+import org.eclipse.jgit.storage.file.ObjectDirectory;
+import org.eclipse.jgit.storage.file.PackIndex;
+import org.eclipse.jgit.storage.file.PackLock;
+import org.eclipse.jgit.storage.file.UnpackedObject;
import org.eclipse.jgit.treewalk.TreeWalk;
/**
@@ -165,8 +167,6 @@ class WalkFetchConnection extends BaseFetchConnection {
private final MutableObjectId idBuffer = new MutableObjectId();
- private final MessageDigest objectDigest = Constants.newMessageDigest();
-
/**
* Errors received while trying to obtain an object.
* <p>
@@ -180,10 +180,18 @@ class WalkFetchConnection extends BaseFetchConnection {
private final List<PackLock> packLocks;
+ /** Inserter to write objects onto {@link #local}. */
+ private final ObjectInserter inserter;
+
+ /** Inserter to read objects from {@link #local}. */
+ private final ObjectReader reader;
+
WalkFetchConnection(final WalkTransport t, final WalkRemoteObjectDatabase w) {
Transport wt = (Transport)t;
local = wt.local;
objCheck = wt.isCheckFetchedObjects() ? new ObjectChecker() : null;
+ inserter = local.newObjectInserter();
+ reader = local.newObjectReader();
remotes = new ArrayList<WalkRemoteObjectDatabase>();
remotes.add(w);
@@ -200,9 +208,9 @@ class WalkFetchConnection extends BaseFetchConnection {
fetchErrors = new HashMap<ObjectId, List<Throwable>>();
packLocks = new ArrayList<PackLock>(4);
- revWalk = new RevWalk(local);
+ revWalk = new RevWalk(reader);
revWalk.setRetainBody(false);
- treeWalk = new TreeWalk(local);
+ treeWalk = new TreeWalk(reader);
COMPLETE = revWalk.newFlag("COMPLETE");
IN_WORK_QUEUE = revWalk.newFlag("IN_WORK_QUEUE");
LOCALLY_SEEN = revWalk.newFlag("LOCALLY_SEEN");
@@ -240,8 +248,12 @@ public void setPackLockMessage(final String message) {
@Override
public void close() {
- for (final RemotePack p : unfetchedPacks)
- p.tmpIdx.delete();
+ inserter.release();
+ reader.release();
+ for (final RemotePack p : unfetchedPacks) {
+ if (p.tmpIdx != null)
+ p.tmpIdx.delete();
+ }
for (final WalkRemoteObjectDatabase r : remotes)
r.close();
}
@@ -309,10 +321,17 @@ private void process(final ObjectId id) throws TransportException {
}
private void processBlob(final RevObject obj) throws TransportException {
- if (!local.hasObject(obj))
- throw new TransportException(MessageFormat.format(JGitText.get().cannotReadBlob, obj.name()),
- new MissingObjectException(obj, Constants.TYPE_BLOB));
- obj.add(COMPLETE);
+ try {
+ if (reader.has(obj, Constants.OBJ_BLOB))
+ obj.add(COMPLETE);
+ else
+ throw new TransportException(MessageFormat.format(JGitText
+ .get().cannotReadBlob, obj.name()),
+ new MissingObjectException(obj, Constants.TYPE_BLOB));
+ } catch (IOException error) {
+ throw new TransportException(MessageFormat.format(
+ JGitText.get().cannotReadBlob, obj.name()), error);
+ }
}
private void processTree(final RevObject obj) throws TransportException {
@@ -369,7 +388,7 @@ private void needs(final RevObject obj) {
private void downloadObject(final ProgressMonitor pm, final AnyObjectId id)
throws TransportException {
- if (local.hasObject(id))
+ if (alreadyHave(id))
return;
for (;;) {
@@ -456,6 +475,15 @@ private void downloadObject(final ProgressMonitor pm, final AnyObjectId id)
}
}
+ private boolean alreadyHave(final AnyObjectId id) throws TransportException {
+ try {
+ return reader.has(id);
+ } catch (IOException error) {
+ throw new TransportException(MessageFormat.format(
+ JGitText.get().cannotReadObject, id.name()), error);
+ }
+ }
+
private boolean downloadPackedObject(final ProgressMonitor monitor,
final AnyObjectId id) throws TransportException {
// Search for the object in a remote pack whose index we have,
@@ -512,11 +540,12 @@ private boolean downloadPackedObject(final ProgressMonitor monitor,
// it failed the index and pack are unusable and we
// shouldn't consult them again.
//
- pack.tmpIdx.delete();
+ if (pack.tmpIdx != null)
+ pack.tmpIdx.delete();
packItr.remove();
}
- if (!local.hasObject(id)) {
+ if (!alreadyHave(id)) {
// What the hell? This pack claimed to have
// the object, but after indexing we didn't
// actually find it in the pack.
@@ -555,8 +584,7 @@ private boolean downloadLooseObject(final AnyObjectId id,
throws TransportException {
try {
final byte[] compressed = remote.open(looseName).toArray();
- verifyLooseObject(id, compressed);
- saveLooseObject(id, compressed);
+ verifyAndInsertLooseObject(id, compressed);
return true;
} catch (FileNotFoundException e) {
// Not available in a loose format from this alternate?
@@ -569,11 +597,11 @@ private boolean downloadLooseObject(final AnyObjectId id,
}
}
- private void verifyLooseObject(final AnyObjectId id, final byte[] compressed)
- throws IOException {
- final UnpackedObjectLoader uol;
+ private void verifyAndInsertLooseObject(final AnyObjectId id,
+ final byte[] compressed) throws IOException {
+ final ObjectLoader uol;
try {
- uol = new UnpackedObjectLoader(compressed);
+ uol = UnpackedObject.parse(compressed, id);
} catch (CorruptObjectException parsingError) {
// Some HTTP servers send back a "200 OK" status with an HTML
// page that explains the requested file could not be found.
@@ -592,62 +620,23 @@ private void verifyLooseObject(final AnyObjectId id, final byte[] compressed)
throw e;
}
- objectDigest.reset();
- objectDigest.update(Constants.encodedTypeString(uol.getType()));
- objectDigest.update((byte) ' ');
- objectDigest.update(Constants.encodeASCII(uol.getSize()));
- objectDigest.update((byte) 0);
- objectDigest.update(uol.getCachedBytes());
- idBuffer.fromRaw(objectDigest.digest(), 0);
-
- if (!AnyObjectId.equals(id, idBuffer)) {
- throw new TransportException(MessageFormat.format(JGitText.get().incorrectHashFor
- , id.name(), idBuffer.name(), Constants.typeString(uol.getType()), compressed.length));
- }
+ final int type = uol.getType();
+ final byte[] raw = uol.getCachedBytes();
if (objCheck != null) {
try {
- objCheck.check(uol.getType(), uol.getCachedBytes());
+ objCheck.check(type, raw);
} catch (CorruptObjectException e) {
throw new TransportException(MessageFormat.format(JGitText.get().transportExceptionInvalid
- , Constants.typeString(uol.getType()), id.name(), e.getMessage()));
+ , Constants.typeString(type), id.name(), e.getMessage()));
}
}
- }
- private void saveLooseObject(final AnyObjectId id, final byte[] compressed)
- throws IOException, ObjectWritingException {
- final File tmp;
-
- tmp = File.createTempFile("noz", null, local.getObjectsDirectory());
- try {
- final FileOutputStream out = new FileOutputStream(tmp);
- try {
- out.write(compressed);
- } finally {
- out.close();
- }
- tmp.setReadOnly();
- } catch (IOException e) {
- tmp.delete();
- throw e;
+ ObjectId act = inserter.insert(type, raw);
+ if (!AnyObjectId.equals(id, act)) {
+ throw new TransportException(MessageFormat.format(JGitText.get().incorrectHashFor
+ , id.name(), act.name(), Constants.typeString(type), compressed.length));
}
-
- final File o = local.toFile(id);
- if (tmp.renameTo(o))
- return;
-
- // Maybe the directory doesn't exist yet as the object
- // directories are always lazily created. Note that we
- // try the rename first as the directory likely does exist.
- //
- o.getParentFile().mkdir();
- if (tmp.renameTo(o))
- return;
-
- tmp.delete();
- if (local.hasObject(id))
- return;
- throw new ObjectWritingException(MessageFormat.format(JGitText.get().unableToStore, id.name()));
+ inserter.flush();
}
private Collection<WalkRemoteObjectDatabase> expandOneAlternate(
@@ -788,12 +777,11 @@ private class RemotePack {
final String idxName;
- final File tmpIdx;
+ File tmpIdx;
PackIndex index;
RemotePack(final WalkRemoteObjectDatabase c, final String pn) {
- final File objdir = local.getObjectsDirectory();
connection = c;
packName = pn;
idxName = packName.substring(0, packName.length() - 5) + ".idx";
@@ -803,13 +791,19 @@ private class RemotePack {
tn = tn.substring(5);
if (tn.endsWith(".idx"))
tn = tn.substring(0, tn.length() - 4);
- tmpIdx = new File(objdir, "walk-" + tn + ".walkidx");
+
+ if (local.getObjectDatabase() instanceof ObjectDirectory) {
+ tmpIdx = new File(((ObjectDirectory) local.getObjectDatabase())
+ .getDirectory(), "walk-" + tn + ".walkidx");
+ }
}
void openIndex(final ProgressMonitor pm) throws IOException {
if (index != null)
return;
- if (tmpIdx.isFile()) {
+ if (tmpIdx == null)
+ tmpIdx = File.createTempFile("jgit-walk-", ".idx");
+ else if (tmpIdx.isFile()) {
try {
index = PackIndex.open(tmpIdx);
return;
diff --git a/org.eclipse.jgit/src/org/eclipse/jgit/transport/WalkPushConnection.java b/org.eclipse.jgit/src/org/eclipse/jgit/transport/WalkPushConnection.java
index 0edf967..9ce0ec1 100644
--- a/org.eclipse.jgit/src/org/eclipse/jgit/transport/WalkPushConnection.java
+++ b/org.eclipse.jgit/src/org/eclipse/jgit/transport/WalkPushConnection.java
@@ -61,12 +61,12 @@
import org.eclipse.jgit.lib.Constants;
import org.eclipse.jgit.lib.ObjectId;
import org.eclipse.jgit.lib.ObjectIdRef;
-import org.eclipse.jgit.lib.PackWriter;
import org.eclipse.jgit.lib.ProgressMonitor;
import org.eclipse.jgit.lib.Ref;
import org.eclipse.jgit.lib.RefWriter;
import org.eclipse.jgit.lib.Repository;
import org.eclipse.jgit.lib.Ref.Storage;
+import org.eclipse.jgit.storage.pack.PackWriter;
import org.eclipse.jgit.transport.RemoteRefUpdate.Status;
/**
@@ -103,6 +103,9 @@ class WalkPushConnection extends BaseConnection implements PushConnection {
/** Database connection to the remote repository. */
private final WalkRemoteObjectDatabase dest;
+ /** The configured transport we were constructed by. */
+ private final Transport transport;
+
/**
* Packs already known to reside in the remote repository.
* <p>
@@ -123,9 +126,9 @@ class WalkPushConnection extends BaseConnection implements PushConnection {
WalkPushConnection(final WalkTransport walkTransport,
final WalkRemoteObjectDatabase w) {
- Transport t = (Transport)walkTransport;
- local = t.local;
- uri = t.getURI();
+ transport = (Transport) walkTransport;
+ local = transport.local;
+ uri = transport.getURI();
dest = w;
}
@@ -209,8 +212,9 @@ private void sendpack(final List<RemoteRefUpdate> updates,
String pathPack = null;
String pathIdx = null;
+ final PackWriter writer = new PackWriter(transport.getPackConfig(),
+ local.newObjectReader());
try {
- final PackWriter pw = new PackWriter(local, monitor);
final List<ObjectId> need = new ArrayList<ObjectId>();
final List<ObjectId> have = new ArrayList<ObjectId>();
for (final RemoteRefUpdate r : updates)
@@ -220,20 +224,20 @@ private void sendpack(final List<RemoteRefUpdate> updates,
if (r.getPeeledObjectId() != null)
have.add(r.getPeeledObjectId());
}
- pw.preparePack(need, have);
+ writer.preparePack(monitor, need, have);
// We don't have to continue further if the pack will
// be an empty pack, as the remote has all objects it
// needs to complete this change.
//
- if (pw.getObjectsNumber() == 0)
+ if (writer.getObjectsNumber() == 0)
return;
packNames = new LinkedHashMap<String, String>();
for (final String n : dest.getPackNames())
packNames.put(n, n);
- final String base = "pack-" + pw.computeName().name();
+ final String base = "pack-" + writer.computeName().name();
final String packName = base + ".pack";
pathPack = "pack/" + packName;
pathIdx = "pack/" + base + ".idx";
@@ -254,7 +258,7 @@ private void sendpack(final List<RemoteRefUpdate> updates,
OutputStream os = dest.writeFile(pathPack, monitor, wt + "..pack");
try {
os = new BufferedOutputStream(os);
- pw.writePack(os);
+ writer.writePack(monitor, monitor, os);
} finally {
os.close();
}
@@ -262,7 +266,7 @@ private void sendpack(final List<RemoteRefUpdate> updates,
os = dest.writeFile(pathIdx, monitor, wt + "..idx");
try {
os = new BufferedOutputStream(os);
- pw.writeIndex(os);
+ writer.writeIndex(os);
} finally {
os.close();
}
@@ -281,6 +285,8 @@ private void sendpack(final List<RemoteRefUpdate> updates,
safeDelete(pathPack);
throw new TransportException(uri, JGitText.get().cannotStoreObjects, err);
+ } finally {
+ writer.release();
}
}
diff --git a/org.eclipse.jgit/src/org/eclipse/jgit/transport/WalkRemoteObjectDatabase.java b/org.eclipse.jgit/src/org/eclipse/jgit/transport/WalkRemoteObjectDatabase.java
index f1743b3..0e2adae 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.ObjectIdRef;
import org.eclipse.jgit.lib.ProgressMonitor;
import org.eclipse.jgit.lib.Ref;
-import org.eclipse.jgit.lib.RefDirectory;
+import org.eclipse.jgit.storage.file.RefDirectory;
import org.eclipse.jgit.util.IO;
/**
diff --git a/org.eclipse.jgit/src/org/eclipse/jgit/treewalk/AbstractTreeIterator.java b/org.eclipse.jgit/src/org/eclipse/jgit/treewalk/AbstractTreeIterator.java
index 178657a..a54b3e9 100644
--- a/org.eclipse.jgit/src/org/eclipse/jgit/treewalk/AbstractTreeIterator.java
+++ b/org.eclipse.jgit/src/org/eclipse/jgit/treewalk/AbstractTreeIterator.java
@@ -55,8 +55,7 @@
import org.eclipse.jgit.lib.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;
/**
@@ -138,19 +137,11 @@ public abstract class AbstractTreeIterator {
*/
protected int pathLen;
- /**
- * Last modified time of the .gitignore file. Greater than 0 if a .gitignore
- * file exists.
- *
- */
- protected long gitIgnoreTimeStamp;
-
/** Create a new iterator with no parent. */
protected AbstractTreeIterator() {
parent = null;
path = new byte[DEFAULT_PATH_SIZE];
pathOffset = 0;
- gitIgnoreTimeStamp = 0l;
}
/**
@@ -170,7 +161,6 @@ protected AbstractTreeIterator() {
*/
protected AbstractTreeIterator(final String prefix) {
parent = null;
- gitIgnoreTimeStamp = 0l;
if (prefix != null && prefix.length() > 0) {
final ByteBuffer b;
@@ -205,7 +195,6 @@ protected AbstractTreeIterator(final String prefix) {
*/
protected AbstractTreeIterator(final byte[] prefix) {
parent = null;
- gitIgnoreTimeStamp = 0l;
if (prefix != null && prefix.length > 0) {
pathLen = prefix.length;
@@ -230,7 +219,6 @@ protected AbstractTreeIterator(final AbstractTreeIterator p) {
parent = p;
path = p.path;
pathOffset = p.pathLen + 1;
- gitIgnoreTimeStamp = 0l;
try {
path[pathOffset - 1] = '/';
@@ -261,7 +249,6 @@ protected AbstractTreeIterator(final AbstractTreeIterator p,
parent = p;
path = childPath;
pathOffset = childPathOffset;
- gitIgnoreTimeStamp = 0l;
}
/**
@@ -417,6 +404,24 @@ public String getEntryPathString() {
}
/**
+ * Get the current entry's path hash code.
+ * <p>
+ * This method computes a hash code on the fly for this path, the hash is
+ * suitable to cluster objects that may have similar paths together.
+ *
+ * @return path hash code; any integer may be returned.
+ */
+ public int getEntryPathHashCode() {
+ int hash = 0;
+ for (int i = Math.max(0, pathLen - 16); i < pathLen; i++) {
+ byte c = path[i];
+ if (c != ' ')
+ hash = (hash >>> 2) + (c << 24);
+ }
+ return hash;
+ }
+
+ /**
* Get the byte array buffer object IDs must be copied out of.
* <p>
* The id buffer contains the bytes necessary to construct an ObjectId for
@@ -445,8 +450,8 @@ public String getEntryPathString() {
* otherwise the caller would not be able to exit out of the subtree
* iterator correctly and return to continue walking <code>this</code>.
*
- * @param repo
- * repository to load the tree data from.
+ * @param reader
+ * reader to load the tree data from.
* @return a new parser that walks over the current subtree.
* @throws IncorrectObjectTypeException
* the current entry is not actually a tree and cannot be parsed
@@ -454,8 +459,9 @@ public String getEntryPathString() {
* @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.
@@ -473,12 +479,10 @@ public EmptyTreeIterator createEmptyTreeIterator() {
* the caller would not be able to exit out of the subtree iterator
* correctly and return to continue walking <code>this</code>.
*
- * @param repo
- * repository to load the tree data from.
+ * @param reader
+ * reader to load the tree data from.
* @param idBuffer
* temporary ObjectId buffer for use by this method.
- * @param curs
- * window cursor to use during repository access.
* @return a new parser that walks over the current subtree.
* @throws IncorrectObjectTypeException
* the current entry is not actually a tree and cannot be parsed
@@ -486,10 +490,10 @@ public EmptyTreeIterator createEmptyTreeIterator() {
* @throws IOException
* a loose object or pack file could not be read.
*/
- public AbstractTreeIterator createSubtreeIterator(final Repository repo,
- final MutableObjectId idBuffer, final WindowCursor curs)
+ public AbstractTreeIterator createSubtreeIterator(
+ final ObjectReader reader, final MutableObjectId idBuffer)
throws IncorrectObjectTypeException, IOException {
- return createSubtreeIterator(repo);
+ return createSubtreeIterator(reader);
}
/**
@@ -605,22 +609,4 @@ public int getNameLength() {
public void getName(byte[] buffer, int offset) {
System.arraycopy(path, pathOffset, buffer, offset, pathLen - pathOffset);
}
-
- /**
- * @return
- * True if this iterator encountered a .gitignore file when initializing entries.
- * Checks if the gitIgnoreTimeStamp > 0.
- */
- public boolean hasGitIgnore() {
- return gitIgnoreTimeStamp > 0;
- }
-
- /**
- * @return
- * Last modified time of the .gitignore file, if any. Will be > 0 if a .gitignore
- * exists.
- */
- public long getGitIgnoreLastModified() {
- return gitIgnoreTimeStamp;
- }
}
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 0b9dc00..8e4094a 100644
--- a/org.eclipse.jgit/src/org/eclipse/jgit/treewalk/CanonicalTreeParser.java
+++ b/org.eclipse.jgit/src/org/eclipse/jgit/treewalk/CanonicalTreeParser.java
@@ -54,9 +54,7 @@
import org.eclipse.jgit.lib.FileMode;
import org.eclipse.jgit.lib.MutableObjectId;
import org.eclipse.jgit.lib.ObjectId;
-import org.eclipse.jgit.lib.ObjectLoader;
-import org.eclipse.jgit.lib.Repository;
-import org.eclipse.jgit.lib.WindowCursor;
+import org.eclipse.jgit.lib.ObjectReader;
/** Parses raw Git trees from the canonical semi-text/semi-binary format. */
public class CanonicalTreeParser extends AbstractTreeIterator {
@@ -86,13 +84,11 @@ public CanonicalTreeParser() {
* may be null or the empty array to indicate the prefix is the
* root of the repository. A trailing slash ('/') is
* automatically appended if the prefix does not end in '/'.
- * @param repo
- * repository to load the tree data from.
+ * @param reader
+ * reader to load the tree data from.
* @param treeId
* identity of the tree being parsed; used only in exception
* messages if data corruption is found.
- * @param curs
- * a window cursor to use during data access from the repository.
* @throws MissingObjectException
* the object supplied is not available from the repository.
* @throws IncorrectObjectTypeException
@@ -101,11 +97,11 @@ public CanonicalTreeParser() {
* @throws IOException
* a loose object or pack file could not be read.
*/
- public CanonicalTreeParser(final byte[] prefix, final Repository repo,
- final AnyObjectId treeId, final WindowCursor curs)
- throws IncorrectObjectTypeException, IOException {
+ public CanonicalTreeParser(final byte[] prefix, final ObjectReader reader,
+ final AnyObjectId treeId) throws IncorrectObjectTypeException,
+ IOException {
super(prefix);
- reset(repo, treeId, curs);
+ reset(reader, treeId);
}
private CanonicalTreeParser(final CanonicalTreeParser p) {
@@ -131,13 +127,11 @@ public void reset(final byte[] treeData) {
/**
* Reset this parser to walk through the given tree.
*
- * @param repo
- * repository to load the tree data from.
+ * @param reader
+ * reader to use during repository access.
* @param id
* identity of the tree being parsed; used only in exception
* messages if data corruption is found.
- * @param curs
- * window cursor to use during repository access.
* @return the root level parser.
* @throws MissingObjectException
* the object supplied is not available from the repository.
@@ -147,13 +141,13 @@ public void reset(final byte[] treeData) {
* @throws IOException
* a loose object or pack file could not be read.
*/
- public CanonicalTreeParser resetRoot(final Repository repo,
- final AnyObjectId id, final WindowCursor curs)
- throws IncorrectObjectTypeException, IOException {
+ public CanonicalTreeParser resetRoot(final ObjectReader reader,
+ final AnyObjectId id) throws IncorrectObjectTypeException,
+ IOException {
CanonicalTreeParser p = this;
while (p.parent != null)
p = (CanonicalTreeParser) p.parent;
- p.reset(repo, id, curs);
+ p.reset(reader, id);
return p;
}
@@ -181,13 +175,11 @@ public CanonicalTreeParser next() {
/**
* Reset this parser to walk through the given tree.
*
- * @param repo
- * repository to load the tree data from.
+ * @param reader
+ * reader to use during repository access.
* @param id
* identity of the tree being parsed; used only in exception
* messages if data corruption is found.
- * @param curs
- * window cursor to use during repository access.
* @throws MissingObjectException
* the object supplied is not available from the repository.
* @throws IncorrectObjectTypeException
@@ -196,32 +188,21 @@ public CanonicalTreeParser next() {
* @throws IOException
* a loose object or pack file could not be read.
*/
- public void reset(final Repository repo, final AnyObjectId id,
- final WindowCursor curs)
+ public void reset(final ObjectReader reader, final AnyObjectId id)
throws IncorrectObjectTypeException, IOException {
- final ObjectLoader ldr = repo.openObject(curs, id);
- if (ldr == null) {
- final ObjectId me = id.toObjectId();
- throw new MissingObjectException(me, Constants.TYPE_TREE);
- }
- final byte[] subtreeData = ldr.getCachedBytes();
- if (ldr.getType() != Constants.OBJ_TREE) {
- final ObjectId me = id.toObjectId();
- throw new IncorrectObjectTypeException(me, Constants.TYPE_TREE);
- }
- reset(subtreeData);
+ reset(reader.open(id, Constants.OBJ_TREE).getCachedBytes());
}
@Override
- public CanonicalTreeParser createSubtreeIterator(final Repository repo,
- final MutableObjectId idBuffer, final WindowCursor curs)
+ public CanonicalTreeParser createSubtreeIterator(final ObjectReader reader,
+ final MutableObjectId idBuffer)
throws IncorrectObjectTypeException, IOException {
idBuffer.fromRaw(idBuffer(), idOffset());
if (!FileMode.TREE.equals(mode)) {
final ObjectId me = idBuffer.toObjectId();
throw new IncorrectObjectTypeException(me, Constants.TYPE_TREE);
}
- return createSubtreeIterator0(repo, idBuffer, curs);
+ return createSubtreeIterator0(reader, idBuffer);
}
/**
@@ -231,32 +212,25 @@ public CanonicalTreeParser createSubtreeIterator(final Repository repo,
* called only once the current entry has been identified as a tree and its
* identity has been converted into an ObjectId.
*
- * @param repo
- * repository to load the tree data from.
+ * @param reader
+ * reader to load the tree data from.
* @param id
* ObjectId of the tree to open.
- * @param curs
- * window cursor to use during repository access.
* @return a new parser that walks over the current subtree.
* @throws IOException
* a loose object or pack file could not be read.
*/
public final CanonicalTreeParser createSubtreeIterator0(
- final Repository repo, final AnyObjectId id, final WindowCursor curs)
+ final ObjectReader reader, final AnyObjectId id)
throws IOException {
final CanonicalTreeParser p = new CanonicalTreeParser(this);
- p.reset(repo, id, curs);
+ p.reset(reader, id);
return p;
}
- public CanonicalTreeParser createSubtreeIterator(final Repository repo)
+ public CanonicalTreeParser createSubtreeIterator(final ObjectReader reader)
throws IncorrectObjectTypeException, IOException {
- final WindowCursor curs = new WindowCursor();
- try {
- return createSubtreeIterator(repo, new MutableObjectId(), curs);
- } finally {
- curs.release();
- }
+ return createSubtreeIterator(reader, new MutableObjectId());
}
@Override
diff --git a/org.eclipse.jgit/src/org/eclipse/jgit/treewalk/EmptyTreeIterator.java b/org.eclipse.jgit/src/org/eclipse/jgit/treewalk/EmptyTreeIterator.java
index 1776b50..7d4ee6d 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 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 EmptyTreeIterator(final AbstractTreeIterator p,
}
@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 aab25ee..2d032ab 100644
--- a/org.eclipse.jgit/src/org/eclipse/jgit/treewalk/FileTreeIterator.java
+++ b/org.eclipse.jgit/src/org/eclipse/jgit/treewalk/FileTreeIterator.java
@@ -54,6 +54,7 @@
import org.eclipse.jgit.errors.IncorrectObjectTypeException;
import org.eclipse.jgit.lib.Constants;
import org.eclipse.jgit.lib.FileMode;
+import org.eclipse.jgit.lib.ObjectReader;
import org.eclipse.jgit.lib.Repository;
import org.eclipse.jgit.util.FS;
@@ -69,6 +70,7 @@ public class FileTreeIterator extends WorkingTreeIterator {
* the root of the repository.
*/
protected final File directory;
+
/**
* the file system abstraction which will be necessary to
* perform certain file system operations.
@@ -76,6 +78,17 @@ public class FileTreeIterator extends WorkingTreeIterator {
protected final FS fs;
/**
+ * Create a new iterator to traverse the work tree and its children.
+ *
+ * @param repo
+ * the repository whose working tree will be scanned.
+ */
+ public FileTreeIterator(Repository repo) {
+ this(repo.getWorkTree(), repo.getFS());
+ initRootIterator(repo);
+ }
+
+ /**
* Create a new iterator to traverse the given directory and its children.
*
* @param root
@@ -111,22 +124,18 @@ protected FileTreeIterator(final FileTreeIterator p, final File root, FS fs) {
}
@Override
- public AbstractTreeIterator createSubtreeIterator(final Repository repo)
+ public AbstractTreeIterator createSubtreeIterator(final ObjectReader reader)
throws IncorrectObjectTypeException, IOException {
return new FileTreeIterator(this, ((FileEntry) current()).file, fs);
}
private Entry[] entries() {
- gitIgnoreTimeStamp = 0l;
final File[] all = directory.listFiles();
if (all == null)
return EOF;
final Entry[] r = new Entry[all.length];
- for (int i = 0; i < r.length; i++) {
+ for (int i = 0; i < r.length; i++)
r[i] = new FileEntry(all[i], fs);
- if (all[i].getName().equals(Constants.DOT_GIT_IGNORE))
- gitIgnoreTimeStamp = r[i].getLastModified();
- }
return r;
}
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 b569174..2d6acbd 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 @@
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;
/**
@@ -86,6 +87,8 @@ public class NameConflictTreeWalk extends TreeWalk {
private boolean fastMinHasMatch;
+ private AbstractTreeIterator dfConflict;
+
/**
* Create a new tree walker for a given repository.
*
@@ -93,7 +96,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
@@ -130,6 +143,7 @@ private AbstractTreeIterator fastMin() {
if (minRef.eof())
return minRef;
+ boolean hasConflict = false;
minRef.matches = minRef;
while (++i < trees.length) {
final AbstractTreeIterator t = trees[i];
@@ -145,6 +159,7 @@ && nameEqual(minRef, t)) {
// tree anyway.
//
t.matches = minRef;
+ hasConflict = true;
} else {
fastMinHasMatch = false;
t.matches = t;
@@ -171,10 +186,13 @@ && nameEqual(t, minRef)) {
}
t.matches = t;
minRef = t;
+ hasConflict = true;
} else
fastMinHasMatch = false;
}
+ if (hasConflict && fastMinHasMatch && dfConflict == null)
+ dfConflict = minRef;
return minRef;
}
@@ -270,6 +288,10 @@ private AbstractTreeIterator combineDF(final AbstractTreeIterator minRef)
for (final AbstractTreeIterator t : trees)
if (t.matches == minRef)
t.matches = treeMatch;
+
+ if (dfConflict == null)
+ dfConflict = treeMatch;
+
return treeMatch;
}
@@ -291,6 +313,9 @@ void popEntriesEqual() throws CorruptObjectException {
t.matches = null;
}
}
+
+ if (ch == dfConflict)
+ dfConflict = null;
}
@Override
@@ -308,5 +333,26 @@ void skipEntriesEqual() throws CorruptObjectException {
t.matches = null;
}
}
+
+ if (ch == dfConflict)
+ dfConflict = null;
+ }
+
+ /**
+ * True if the current entry is covered by a directory/file conflict.
+ *
+ * This means that for some prefix of the current entry's path, this walk
+ * has detected a directory/file conflict. Also true if the current entry
+ * itself is a directory/file conflict.
+ *
+ * Example: If this TreeWalk points to foo/bar/a.txt and this method returns
+ * true then you know that either for path foo or for path foo/bar files and
+ * folders were detected.
+ *
+ * @return <code>true</code> if the current entry is covered by a
+ * directory/file conflict, <code>false</code> otherwise
+ */
+ public boolean isDirectoryFileConflict() {
+ return dfConflict != null;
}
}
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 64c1aad..1685964 100644
--- a/org.eclipse.jgit/src/org/eclipse/jgit/treewalk/TreeWalk.java
+++ b/org.eclipse.jgit/src/org/eclipse/jgit/treewalk/TreeWalk.java
@@ -56,8 +56,8 @@
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;
-import org.eclipse.jgit.lib.WindowCursor;
import org.eclipse.jgit.revwalk.RevTree;
import org.eclipse.jgit.treewalk.filter.PathFilterGroup;
import org.eclipse.jgit.treewalk.filter.TreeFilter;
@@ -91,6 +91,42 @@ public class TreeWalk {
* the caller should not need to invoke {@link #next()} unless they are
* looking for a possible directory/file name conflict.
*
+ * @param reader
+ * the reader the walker will obtain tree data from.
+ * @param path
+ * single path to advance the tree walk instance into.
+ * @param trees
+ * one or more trees to walk through, all with the same root.
+ * @return a new tree walk configured for exactly this one path; null if no
+ * path was found in any of the trees.
+ * @throws IOException
+ * reading a pack file or loose object failed.
+ * @throws CorruptObjectException
+ * an tree object could not be read as its data stream did not
+ * appear to be a tree, or could not be inflated.
+ * @throws IncorrectObjectTypeException
+ * an object we expected to be a tree was not a tree.
+ * @throws MissingObjectException
+ * a tree object was not found.
+ */
+ public static TreeWalk forPath(final ObjectReader reader, final String path,
+ final AnyObjectId... trees) throws MissingObjectException,
+ IncorrectObjectTypeException, CorruptObjectException, IOException {
+ final TreeWalk r = new TreeWalk(reader);
+ r.setFilter(PathFilterGroup.createFromStrings(Collections
+ .singleton(path)));
+ r.setRecursive(r.getFilter().shouldBeRecursive());
+ r.reset(trees);
+ return r.next() ? r : null;
+ }
+
+ /**
+ * Open a tree walk and filter to exactly one path.
+ * <p>
+ * The returned tree walk is already positioned on the requested path, so
+ * the caller should not need to invoke {@link #next()} unless they are
+ * looking for a possible directory/file name conflict.
+ *
* @param db
* repository to read tree object data from.
* @param path
@@ -112,12 +148,12 @@ public class TreeWalk {
public static TreeWalk forPath(final Repository db, final String path,
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;
+ ObjectReader reader = db.newObjectReader();
+ try {
+ return forPath(reader, path, trees);
+ } finally {
+ reader.release();
+ }
}
/**
@@ -151,12 +187,10 @@ public static TreeWalk forPath(final Repository db, final String path,
return forPath(db, path, new ObjectId[] { tree });
}
- private final Repository db;
+ private final ObjectReader reader;
private final MutableObjectId idBuffer = new MutableObjectId();
- private final WindowCursor curs = new WindowCursor();
-
private TreeFilter filter;
AbstractTreeIterator[] trees;
@@ -180,18 +214,34 @@ public static TreeWalk forPath(final Repository db, final String path,
* the repository the walker will obtain data from.
*/
public TreeWalk(final Repository repo) {
- db = repo;
+ this(repo.newObjectReader());
+ }
+
+ /**
+ * Create a new tree walker for a given repository.
+ *
+ * @param or
+ * the reader the walker will obtain tree data from.
+ */
+ public TreeWalk(final ObjectReader or) {
+ reader = or;
filter = TreeFilter.ALL;
trees = new AbstractTreeIterator[] { new EmptyTreeIterator() };
}
+ /** @return the reader this walker is using to load objects. */
+ public ObjectReader getObjectReader() {
+ return reader;
+ }
+
/**
- * Get the repository this tree walker is reading from.
- *
- * @return the repository configured when the walker was created.
+ * Release any resources used by this walker's reader.
+ * <p>
+ * A walker that has been released can be used again, but may need to be
+ * released after the subsequent usage.
*/
- public Repository getRepository() {
- return db;
+ public void release() {
+ reader.release();
}
/**
@@ -319,7 +369,7 @@ public void reset(final AnyObjectId id) throws MissingObjectException,
if (o instanceof CanonicalTreeParser) {
o.matches = null;
o.matchShift = 0;
- ((CanonicalTreeParser) o).reset(db, id, curs);
+ ((CanonicalTreeParser) o).reset(reader, id);
trees[0] = o;
} else {
trees[0] = parserFor(id);
@@ -366,7 +416,7 @@ public void reset(final AnyObjectId[] ids) throws MissingObjectException,
if (o instanceof CanonicalTreeParser && o.pathOffset == 0) {
o.matches = null;
o.matchShift = 0;
- ((CanonicalTreeParser) o).reset(db, ids[i], curs);
+ ((CanonicalTreeParser) o).reset(reader, ids[i]);
r[i] = o;
continue;
}
@@ -836,7 +886,7 @@ public void enterSubtree() throws MissingObjectException,
final AbstractTreeIterator t = trees[i];
final AbstractTreeIterator n;
if (t.matches == ch && !t.eof() && FileMode.TREE.equals(t.mode))
- n = t.createSubtreeIterator(db, idBuffer, curs);
+ n = t.createSubtreeIterator(reader, idBuffer);
else
n = t.createEmptyTreeIterator();
tmp[i] = n;
@@ -911,11 +961,15 @@ private void exitSubtree() {
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;
}
static String pathOf(final AbstractTreeIterator t) {
return RawParseUtils.decode(Constants.CHARSET, t.path, 0, t.pathLen);
}
+
+ static String pathOf(final byte[] buf, int pos, int end) {
+ return RawParseUtils.decode(Constants.CHARSET, buf, pos, end);
+ }
}
diff --git a/org.eclipse.jgit/src/org/eclipse/jgit/treewalk/WorkingTreeIterator.java b/org.eclipse.jgit/src/org/eclipse/jgit/treewalk/WorkingTreeIterator.java
index cd06c24..a46f4df 100644
--- a/org.eclipse.jgit/src/org/eclipse/jgit/treewalk/WorkingTreeIterator.java
+++ b/org.eclipse.jgit/src/org/eclipse/jgit/treewalk/WorkingTreeIterator.java
@@ -45,6 +45,8 @@
package org.eclipse.jgit.treewalk;
+import java.io.File;
+import java.io.FileInputStream;
import java.io.IOException;
import java.io.InputStream;
import java.nio.ByteBuffer;
@@ -54,14 +56,18 @@
import java.security.MessageDigest;
import java.text.MessageFormat;
import java.util.Arrays;
+import java.util.Collections;
import java.util.Comparator;
import org.eclipse.jgit.JGitText;
import org.eclipse.jgit.dircache.DirCache;
import org.eclipse.jgit.dircache.DirCacheEntry;
import org.eclipse.jgit.errors.CorruptObjectException;
+import org.eclipse.jgit.ignore.IgnoreNode;
+import org.eclipse.jgit.ignore.IgnoreRule;
import org.eclipse.jgit.lib.Constants;
import org.eclipse.jgit.lib.FileMode;
+import org.eclipse.jgit.lib.Repository;
import org.eclipse.jgit.util.FS;
/**
@@ -106,6 +112,9 @@ public abstract class WorkingTreeIterator extends AbstractTreeIterator {
/** Current position within {@link #entries}. */
private int ptr;
+ /** If there is a .gitignore file present, the parsed rules from it. */
+ private IgnoreNode ignoreNode;
+
/** Create a new iterator with no parent. */
protected WorkingTreeIterator() {
super();
@@ -143,6 +152,24 @@ protected WorkingTreeIterator(final WorkingTreeIterator p) {
nameEncoder = p.nameEncoder;
}
+ /**
+ * Initialize this iterator for the root level of a repository.
+ * <p>
+ * This method should only be invoked after calling {@link #init(Entry[])},
+ * and only for the root iterator.
+ *
+ * @param repo
+ * the repository.
+ */
+ protected void initRootIterator(Repository repo) {
+ Entry entry;
+ if (ignoreNode instanceof PerDirectoryIgnoreNode)
+ entry = ((PerDirectoryIgnoreNode) ignoreNode).entry;
+ else
+ entry = null;
+ ignoreNode = new RootIgnoreNode(entry, repo);
+ }
+
@Override
public byte[] idBuffer() {
if (contentIdFromPtr == ptr)
@@ -295,6 +322,57 @@ public long getEntryLastModified() {
return current().getLastModified();
}
+ /**
+ * Determine if the current entry path is ignored by an ignore rule.
+ *
+ * @return true if the entry was ignored by an ignore rule file.
+ * @throws IOException
+ * a relevant ignore rule file exists but cannot be read.
+ */
+ public boolean isEntryIgnored() throws IOException {
+ return isEntryIgnored(pathLen);
+ }
+
+ /**
+ * Determine if the entry path is ignored by an ignore rule.
+ *
+ * @param pLen
+ * the length of the path in the path buffer.
+ * @return true if the entry is ignored by an ignore rule.
+ * @throws IOException
+ * a relevant ignore rule file exists but cannot be read.
+ */
+ protected boolean isEntryIgnored(final int pLen) throws IOException {
+ IgnoreNode rules = getIgnoreNode();
+ if (rules != null) {
+ // The ignore code wants path to start with a '/' if possible.
+ // If we have the '/' in our path buffer because we are inside
+ // a subdirectory include it in the range we convert to string.
+ //
+ int pOff = pathOffset;
+ if (0 < pOff)
+ pOff--;
+ String p = TreeWalk.pathOf(path, pOff, pLen);
+ switch (rules.isIgnored(p, FileMode.TREE.equals(mode))) {
+ case IGNORED:
+ return true;
+ case NOT_IGNORED:
+ return false;
+ case CHECK_PARENT:
+ break;
+ }
+ }
+ if (parent instanceof WorkingTreeIterator)
+ return ((WorkingTreeIterator) parent).isEntryIgnored(pLen);
+ return false;
+ }
+
+ private IgnoreNode getIgnoreNode() throws IOException {
+ if (ignoreNode instanceof PerDirectoryIgnoreNode)
+ ignoreNode = ((PerDirectoryIgnoreNode) ignoreNode).load();
+ return ignoreNode;
+ }
+
private static final Comparator<Entry> ENTRY_CMP = new Comparator<Entry>() {
public int compare(final Entry o1, final Entry o2) {
final byte[] a = o1.encodedName;
@@ -345,6 +423,8 @@ protected void init(final Entry[] list) {
continue;
if (Constants.DOT_GIT.equals(name))
continue;
+ if (Constants.DOT_GIT_IGNORE.equals(name))
+ ignoreNode = new PerDirectoryIgnoreNode(e);
if (i != o)
entries[o] = e;
e.encodeName(nameEncoder);
@@ -398,20 +478,20 @@ public boolean isModified(DirCacheEntry entry, boolean forceContentCheck,
if (entry.isUpdateNeeded())
return true;
- if (getEntryLength() != entry.getLength())
+ if (!entry.isSmudged() && (getEntryLength() != entry.getLength()))
return true;
- // determine difference in mode-bits of file and index-entry. In the
+ // Determine difference in mode-bits of file and index-entry. In the
// bitwise presentation of modeDiff we'll have a '1' when the two modes
// differ at this position.
int modeDiff = getEntryRawMode() ^ entry.getRawMode();
- // ignore the executable file bits if checkFilemode tells me to do so.
+ // Ignore the executable file bits if checkFilemode tells me to do so.
// Ignoring is done by setting the bits representing a EXECUTABLE_FILE
// to '0' in modeDiff
if (!checkFilemode)
modeDiff &= ~FileMode.EXECUTABLE_FILE.getBits();
if (modeDiff != 0)
- // report a modification if the modes still (after potentially
+ // Report a modification if the modes still (after potentially
// ignoring EXECUTABLE_FILE bits) differ
return true;
@@ -422,14 +502,58 @@ public boolean isModified(DirCacheEntry entry, boolean forceContentCheck,
long fileLastModified = getEntryLastModified();
if (cacheLastModified % 1000 == 0)
fileLastModified = fileLastModified - fileLastModified % 1000;
- if (forceContentCheck) {
- if (fileLastModified == cacheLastModified)
- return false; // Same time, don't check content.
- else
- return !getEntryObjectId().equals(entry.getObjectId());
+
+ if (fileLastModified != cacheLastModified) {
+ // The file is dirty by timestamps
+ if (forceContentCheck) {
+ // But we are told to look at content even though timestamps
+ // tell us about modification
+ return contentCheck(entry);
+ } else {
+ // We are told to assume a modification if timestamps differs
+ return true;
+ }
} else {
- // No content check forced, assume dirty if stat differs.
- return fileLastModified != cacheLastModified;
+ // The file is clean when you look at timestamps.
+ if (entry.isSmudged()) {
+ // The file is clean by timestamps but the entry was smudged.
+ // Lets do a content check
+ return contentCheck(entry);
+ } else {
+ // The file is clean by timestamps and the entry is not
+ // smudged: Can't get any cleaner!
+ return false;
+ }
+ }
+ }
+
+ /**
+ * Compares the entries content with the content in the filesystem.
+ * Unsmudges the entry when it is detected that it is clean.
+ *
+ * @param entry
+ * the entry to be checked
+ * @return <code>true</code> if the content matches, <code>false</code>
+ * otherwise
+ */
+ private boolean contentCheck(DirCacheEntry entry) {
+ if (getEntryObjectId().equals(entry.getObjectId())) {
+ // Content has not changed
+
+ // We know the entry can't be racily clean because it's still clean.
+ // Therefore we unsmudge the entry!
+ // If by any chance we now unsmudge although we are still in the
+ // same time-slot as the last modification to the index file the
+ // next index write operation will smudge again.
+ // Caution: we are unsmudging just by setting the length of the
+ // in-memory entry object. It's the callers task to detect that we
+ // have modified the entry and to persist the modified index.
+ entry.setLength((int) getEntryLength());
+
+ return false;
+ } else {
+ // Content differs: that's a real change!
+ return true;
}
}
@@ -526,4 +650,59 @@ public String toString() {
*/
public abstract InputStream openInputStream() throws IOException;
}
+
+ /** Magic type indicating we know rules exist, but they aren't loaded. */
+ private static class PerDirectoryIgnoreNode extends IgnoreNode {
+ final Entry entry;
+
+ PerDirectoryIgnoreNode(Entry entry) {
+ super(Collections.<IgnoreRule> emptyList());
+ this.entry = entry;
+ }
+
+ IgnoreNode load() throws IOException {
+ IgnoreNode r = new IgnoreNode();
+ InputStream in = entry.openInputStream();
+ try {
+ r.parse(in);
+ } finally {
+ in.close();
+ }
+ return r.getRules().isEmpty() ? null : r;
+ }
+ }
+
+ /** Magic type indicating there may be rules for the top level. */
+ private static class RootIgnoreNode extends PerDirectoryIgnoreNode {
+ final Repository repository;
+
+ RootIgnoreNode(Entry entry, Repository repository) {
+ super(entry);
+ this.repository = repository;
+ }
+
+ @Override
+ IgnoreNode load() throws IOException {
+ IgnoreNode r;
+ if (entry != null) {
+ r = super.load();
+ if (r == null)
+ r = new IgnoreNode();
+ } else {
+ r = new IgnoreNode();
+ }
+
+ File exclude = new File(repository.getDirectory(), "info/exclude");
+ if (exclude.exists()) {
+ FileInputStream in = new FileInputStream(exclude);
+ try {
+ r.parse(in);
+ } finally {
+ in.close();
+ }
+ }
+
+ return r.getRules().isEmpty() ? null : r;
+ }
+ }
}
diff --git a/org.eclipse.jgit/src/org/eclipse/jgit/util/ChangeIdUtil.java b/org.eclipse.jgit/src/org/eclipse/jgit/util/ChangeIdUtil.java
index 6c146f7..a8e505d 100644
--- a/org.eclipse.jgit/src/org/eclipse/jgit/util/ChangeIdUtil.java
+++ b/org.eclipse.jgit/src/org/eclipse/jgit/util/ChangeIdUtil.java
@@ -42,13 +42,12 @@
*/
package org.eclipse.jgit.util;
-import java.io.ByteArrayInputStream;
import java.io.IOException;
import java.util.regex.Pattern;
import org.eclipse.jgit.lib.Constants;
+import org.eclipse.jgit.lib.ObjectInserter;
import org.eclipse.jgit.lib.ObjectId;
-import org.eclipse.jgit.lib.ObjectWriter;
import org.eclipse.jgit.lib.PersonIdent;
/**
@@ -62,6 +61,8 @@
*/
public class ChangeIdUtil {
+ static final String CHANGE_ID = "Change-Id:";
+
// package-private so the unit test can test this part only
static String clean(String msg) {
return msg.//
@@ -113,12 +114,8 @@ public static ObjectId computeChangeId(final ObjectId treeId,
b.append(committer.toExternalString());
b.append("\n\n");
b.append(cleanMessage);
- ObjectWriter w = new ObjectWriter(null);
- byte[] bytes = b.toString().getBytes(Constants.CHARACTER_ENCODING);
- ByteArrayInputStream is = new ByteArrayInputStream(bytes);
- ObjectId sha1 = w.computeObjectSha1(Constants.OBJ_COMMIT, bytes.length,
- is);
- return sha1;
+ return new ObjectInserter.Formatter().idFor(Constants.OBJ_COMMIT, //
+ b.toString().getBytes(Constants.CHARACTER_ENCODING));
}
private static final Pattern issuePattern = Pattern
@@ -141,8 +138,39 @@ public static ObjectId computeChangeId(final ObjectId treeId,
* @return a commit message with an inserted Change-Id line
*/
public static String insertId(String message, ObjectId changeId) {
- if (message.indexOf("\nChange-Id:") > 0)
+ return insertId(message, changeId, false);
+ }
+
+ /**
+ * Find the right place to insert a Change-Id and return it.
+ * <p>
+ * If no Change-Id is found the Change-Id is inserted before
+ * the first footer line but after a Bug line.
+ *
+ * If Change-Id is found and replaceExisting is set to false,
+ * the message is unchanged.
+ *
+ * If Change-Id is found and replaceExisting is set to true,
+ * the Change-Id is replaced with {@code changeId}.
+ *
+ * @param message
+ * @param changeId
+ * @param replaceExisting
+ * @return a commit message with an inserted Change-Id line
+ */
+ public static String insertId(String message, ObjectId changeId,
+ boolean replaceExisting) {
+ if (message.indexOf(CHANGE_ID) > 0) {
+ if (replaceExisting) {
+ int i = message.indexOf(CHANGE_ID) + 10;
+ while (message.charAt(i) == ' ')
+ i++;
+ String oldId = message.length() == (i + 40) ?
+ message.substring(i) : message.substring(i, i + 41);
+ message = message.replace(oldId, "I" + changeId.getName());
+ }
return message;
+ }
String[] lines = message.split("\n");
int footerFirstLine = lines.length;
@@ -178,7 +206,8 @@ public static String insertId(String message, ObjectId changeId) {
}
if (insertAfter == lines.length && insertAfter == footerFirstLine)
ret.append("\n");
- ret.append("Change-Id: I");
+ ret.append(CHANGE_ID);
+ ret.append(" I");
ret.append(ObjectId.toString(changeId));
ret.append("\n");
for (; i < lines.length; ++i) {
diff --git a/org.eclipse.jgit/src/org/eclipse/jgit/util/FS.java b/org.eclipse.jgit/src/org/eclipse/jgit/util/FS.java
index b8d4337..cb5c8bd 100644
--- a/org.eclipse.jgit/src/org/eclipse/jgit/util/FS.java
+++ b/org.eclipse.jgit/src/org/eclipse/jgit/util/FS.java
@@ -149,6 +149,13 @@ public File userHome() {
}
/**
+ * Does this file system have problems with atomic renames?
+ *
+ * @return true if the caller should retry a failed rename of a lock file.
+ */
+ public abstract boolean retryFailedLockFileCommit();
+
+ /**
* Determine the user's home directory (location where preferences are).
*
* @return the user's home directory; null if the user does not have one.
diff --git a/org.eclipse.jgit/src/org/eclipse/jgit/util/FS_POSIX_Java5.java b/org.eclipse.jgit/src/org/eclipse/jgit/util/FS_POSIX_Java5.java
index 4ce0366..950d491 100644
--- a/org.eclipse.jgit/src/org/eclipse/jgit/util/FS_POSIX_Java5.java
+++ b/org.eclipse.jgit/src/org/eclipse/jgit/util/FS_POSIX_Java5.java
@@ -57,4 +57,9 @@ public boolean canExecute(final File f) {
public boolean setExecute(final File f, final boolean canExec) {
return false;
}
+
+ @Override
+ public boolean retryFailedLockFileCommit() {
+ return false;
+ }
}
diff --git a/org.eclipse.jgit/src/org/eclipse/jgit/util/FS_POSIX_Java6.java b/org.eclipse.jgit/src/org/eclipse/jgit/util/FS_POSIX_Java6.java
index 8a86d2e..192d9bb 100644
--- a/org.eclipse.jgit/src/org/eclipse/jgit/util/FS_POSIX_Java6.java
+++ b/org.eclipse.jgit/src/org/eclipse/jgit/util/FS_POSIX_Java6.java
@@ -104,4 +104,9 @@ public boolean setExecute(final File f, final boolean canExec) {
throw new Error(e);
}
}
+
+ @Override
+ public boolean retryFailedLockFileCommit() {
+ return false;
+ }
}
diff --git a/org.eclipse.jgit/src/org/eclipse/jgit/util/FS_Win32.java b/org.eclipse.jgit/src/org/eclipse/jgit/util/FS_Win32.java
index c86f3e1..4e35e6e 100644
--- a/org.eclipse.jgit/src/org/eclipse/jgit/util/FS_Win32.java
+++ b/org.eclipse.jgit/src/org/eclipse/jgit/util/FS_Win32.java
@@ -71,4 +71,9 @@ public boolean canExecute(final File f) {
public boolean setExecute(final File f, final boolean canExec) {
return false;
}
+
+ @Override
+ public boolean retryFailedLockFileCommit() {
+ return true;
+ }
}
diff --git a/org.eclipse.jgit/src/org/eclipse/jgit/util/LongList.java b/org.eclipse.jgit/src/org/eclipse/jgit/util/LongList.java
index 26608bb..96b311d 100644
--- a/org.eclipse.jgit/src/org/eclipse/jgit/util/LongList.java
+++ b/org.eclipse.jgit/src/org/eclipse/jgit/util/LongList.java
@@ -83,6 +83,20 @@ public long get(final int i) {
return entries[i];
}
+ /**
+ * Determine if an entry appears in this collection.
+ *
+ * @param value
+ * the value to search for.
+ * @return true of {@code value} appears in this list.
+ */
+ public boolean contains(final long value) {
+ for (int i = 0; i < count; i++)
+ if (entries[i] == value)
+ return true;
+ return false;
+ }
+
/** Empty this list */
public void clear() {
count = 0;
diff --git a/org.eclipse.jgit/src/org/eclipse/jgit/util/SystemReader.java b/org.eclipse.jgit/src/org/eclipse/jgit/util/SystemReader.java
index 9d7feb0..475c871 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.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.
@@ -74,7 +74,7 @@ public String getProperty(String key) {
public FileBasedConfig openUserConfig(FS fs) {
final File home = fs.userHome();
- return new FileBasedConfig(new File(home, ".gitconfig"));
+ return new FileBasedConfig(new File(home, ".gitconfig"), fs);
}
public String getHostname() {
diff --git a/org.eclipse.jgit/src/org/eclipse/jgit/util/TemporaryBuffer.java b/org.eclipse.jgit/src/org/eclipse/jgit/util/TemporaryBuffer.java
index 6c421c5..baa45c5 100644
--- a/org.eclipse.jgit/src/org/eclipse/jgit/util/TemporaryBuffer.java
+++ b/org.eclipse.jgit/src/org/eclipse/jgit/util/TemporaryBuffer.java
@@ -126,7 +126,7 @@ public void write(final byte[] b, int off, int len) throws IOException {
blocks.add(s);
}
- final int n = Math.min(Block.SZ - s.count, len);
+ final int n = Math.min(s.buffer.length - s.count, len);
System.arraycopy(b, off, s.buffer, s.count, n);
s.count += n;
len -= n;
@@ -139,6 +139,19 @@ public void write(final byte[] b, int off, int len) throws IOException {
}
/**
+ * Dumps the entire buffer into the overflow stream, and flushes it.
+ *
+ * @throws IOException
+ * the overflow stream cannot be started, or the buffer contents
+ * cannot be written to it, or it failed to flush.
+ */
+ protected void doFlush() throws IOException {
+ if (overflow == null)
+ switchToOverflow();
+ overflow.flush();
+ }
+
+ /**
* Copy all bytes remaining on the input stream into this buffer.
*
* @param in
@@ -158,7 +171,7 @@ public void copy(final InputStream in) throws IOException {
blocks.add(s);
}
- final int n = in.read(s.buffer, s.count, Block.SZ - s.count);
+ int n = in.read(s.buffer, s.count, s.buffer.length - s.count);
if (n < 1)
return;
s.count += n;
@@ -179,8 +192,12 @@ public void copy(final InputStream in) throws IOException {
* @return total length of the buffer, in bytes.
*/
public long length() {
+ return inCoreLength();
+ }
+
+ private long inCoreLength() {
final Block last = last();
- return ((long) blocks.size()) * Block.SZ - (Block.SZ - last.count);
+ return ((long) blocks.size() - 1) * Block.SZ + last.count;
}
/**
@@ -238,8 +255,13 @@ public void reset() {
if (overflow != null) {
destroy();
}
- blocks = new ArrayList<Block>(inCoreLimit / Block.SZ);
- blocks.add(new Block());
+ if (inCoreLimit < Block.SZ) {
+ blocks = new ArrayList<Block>(1);
+ blocks.add(new Block(inCoreLimit));
+ } else {
+ blocks = new ArrayList<Block>(inCoreLimit / Block.SZ);
+ blocks.add(new Block());
+ }
}
/**
@@ -257,9 +279,14 @@ private Block last() {
}
private boolean reachedInCoreLimit() throws IOException {
- if (blocks.size() * Block.SZ < inCoreLimit)
+ if (inCoreLength() < inCoreLimit)
return false;
+ switchToOverflow();
+ return true;
+ }
+
+ private void switchToOverflow() throws IOException {
overflow = overflow();
final Block last = blocks.remove(blocks.size() - 1);
@@ -269,7 +296,6 @@ private boolean reachedInCoreLimit() throws IOException {
overflow = new BufferedOutputStream(overflow, Block.SZ);
overflow.write(last.buffer, 0, last.count);
- return true;
}
public void close() throws IOException {
@@ -427,12 +453,20 @@ protected OutputStream overflow() throws IOException {
static class Block {
static final int SZ = 8 * 1024;
- final byte[] buffer = new byte[SZ];
+ final byte[] buffer;
int count;
+ Block() {
+ buffer = new byte[SZ];
+ }
+
+ Block(int sz) {
+ buffer = new byte[sz];
+ }
+
boolean isFull() {
- return count == SZ;
+ return count == buffer.length;
}
}
}