Merge branch 'master' into stable-4.0

Change-Id: I962461630384b76e7f387f4e1c1248833fbc4673
Signed-off-by: Matthias Sohn <matthias.sohn@sap.com>
This commit is contained in:
Matthias Sohn 2015-05-04 22:02:23 +02:00
commit f1a1acbd1c
95 changed files with 1889 additions and 747 deletions

View File

@ -76,6 +76,7 @@
import org.eclipse.jgit.errors.UnpackException; import org.eclipse.jgit.errors.UnpackException;
import org.eclipse.jgit.lib.Repository; import org.eclipse.jgit.lib.Repository;
import org.eclipse.jgit.transport.InternalHttpServerGlue;
import org.eclipse.jgit.transport.ReceivePack; import org.eclipse.jgit.transport.ReceivePack;
import org.eclipse.jgit.transport.RefAdvertiser.PacketLineOutRefAdvertiser; import org.eclipse.jgit.transport.RefAdvertiser.PacketLineOutRefAdvertiser;
import org.eclipse.jgit.transport.resolver.ReceivePackFactory; import org.eclipse.jgit.transport.resolver.ReceivePackFactory;
@ -100,6 +101,9 @@ protected void begin(HttpServletRequest req, Repository db)
throws IOException, ServiceNotEnabledException, throws IOException, ServiceNotEnabledException,
ServiceNotAuthorizedException { ServiceNotAuthorizedException {
ReceivePack rp = receivePackFactory.create(req, db); ReceivePack rp = receivePackFactory.create(req, db);
InternalHttpServerGlue.setPeerUserAgent(
rp,
req.getHeader(HDR_USER_AGENT));
req.setAttribute(ATTRIBUTE_HANDLER, rp); req.setAttribute(ATTRIBUTE_HANDLER, rp);
} }

View File

@ -101,7 +101,7 @@ public void doFilter(ServletRequest request, ServletResponse response,
res.sendError(SC_UNAUTHORIZED); res.sendError(SC_UNAUTHORIZED);
return; return;
} catch (ServiceNotEnabledException e) { } catch (ServiceNotEnabledException e) {
sendError(req, res, SC_FORBIDDEN); sendError(req, res, SC_FORBIDDEN, e.getMessage());
return; return;
} }

View File

@ -74,6 +74,7 @@
import javax.servlet.http.HttpServletResponse; import javax.servlet.http.HttpServletResponse;
import org.eclipse.jgit.lib.Repository; import org.eclipse.jgit.lib.Repository;
import org.eclipse.jgit.transport.InternalHttpServerGlue;
import org.eclipse.jgit.transport.RefAdvertiser.PacketLineOutRefAdvertiser; import org.eclipse.jgit.transport.RefAdvertiser.PacketLineOutRefAdvertiser;
import org.eclipse.jgit.transport.UploadPack; import org.eclipse.jgit.transport.UploadPack;
import org.eclipse.jgit.transport.UploadPackInternalServerErrorException; import org.eclipse.jgit.transport.UploadPackInternalServerErrorException;
@ -100,6 +101,9 @@ protected void begin(HttpServletRequest req, Repository db)
throws IOException, ServiceNotEnabledException, throws IOException, ServiceNotEnabledException,
ServiceNotAuthorizedException { ServiceNotAuthorizedException {
UploadPack up = uploadPackFactory.create(req, db); UploadPack up = uploadPackFactory.create(req, db);
InternalHttpServerGlue.setPeerUserAgent(
up,
req.getHeader(HDR_USER_AGENT));
req.setAttribute(ATTRIBUTE_HANDLER, up); req.setAttribute(ATTRIBUTE_HANDLER, up);
} }

View File

@ -14,6 +14,7 @@ Import-Package: org.eclipse.jgit.api;version="[4.0.0,4.1.0)",
org.eclipse.jgit.internal.storage.file;version="[4.0.0,4.1.0)", org.eclipse.jgit.internal.storage.file;version="[4.0.0,4.1.0)",
org.eclipse.jgit.internal.storage.pack;version="[4.0.0,4.1.0)", org.eclipse.jgit.internal.storage.pack;version="[4.0.0,4.1.0)",
org.eclipse.jgit.lib;version="[4.0.0,4.1.0)", org.eclipse.jgit.lib;version="[4.0.0,4.1.0)",
org.eclipse.jgit.merge;version="[4.0.0,4.1.0)",
org.eclipse.jgit.revwalk;version="[4.0.0,4.1.0)", org.eclipse.jgit.revwalk;version="[4.0.0,4.1.0)",
org.eclipse.jgit.storage.file;version="[4.0.0,4.1.0)", org.eclipse.jgit.storage.file;version="[4.0.0,4.1.0)",
org.eclipse.jgit.treewalk;version="[4.0.0,4.1.0)", org.eclipse.jgit.treewalk;version="[4.0.0,4.1.0)",

View File

@ -52,11 +52,13 @@
import java.io.OutputStream; import java.io.OutputStream;
import java.security.MessageDigest; import java.security.MessageDigest;
import java.util.ArrayList; import java.util.ArrayList;
import java.util.Arrays;
import java.util.Collections; import java.util.Collections;
import java.util.Date; import java.util.Date;
import java.util.HashSet; import java.util.HashSet;
import java.util.List; import java.util.List;
import java.util.Set; import java.util.Set;
import java.util.TimeZone;
import org.eclipse.jgit.api.Git; import org.eclipse.jgit.api.Git;
import org.eclipse.jgit.dircache.DirCache; import org.eclipse.jgit.dircache.DirCache;
@ -88,6 +90,8 @@
import org.eclipse.jgit.lib.RefWriter; import org.eclipse.jgit.lib.RefWriter;
import org.eclipse.jgit.lib.Repository; import org.eclipse.jgit.lib.Repository;
import org.eclipse.jgit.lib.TagBuilder; import org.eclipse.jgit.lib.TagBuilder;
import org.eclipse.jgit.merge.MergeStrategy;
import org.eclipse.jgit.merge.ThreeWayMerger;
import org.eclipse.jgit.revwalk.ObjectWalk; import org.eclipse.jgit.revwalk.ObjectWalk;
import org.eclipse.jgit.revwalk.RevBlob; import org.eclipse.jgit.revwalk.RevBlob;
import org.eclipse.jgit.revwalk.RevCommit; import org.eclipse.jgit.revwalk.RevCommit;
@ -187,6 +191,11 @@ public Date getClock() {
return new Date(now); return new Date(now);
} }
/** @return timezone used for default identities. */
public TimeZone getTimeZone() {
return defaultCommitter.getTimeZone();
}
/** /**
* Adjust the current time that will used by the next commit. * Adjust the current time that will used by the next commit.
* *
@ -615,6 +624,59 @@ public void reset(String name) throws Exception {
} }
} }
/**
* Cherry-pick a commit onto HEAD.
* <p>
* This differs from {@code git cherry-pick} in that it works in a bare
* repository. As a result, any merge failure results in an exception, as
* there is no way to recover.
*
* @param id
* commit-ish to cherry-pick.
* @return newly created commit, or null if no work was done due to the
* resulting tree being identical.
* @throws Exception
*/
public RevCommit cherryPick(AnyObjectId id) throws Exception {
RevCommit commit = pool.parseCommit(id);
pool.parseBody(commit);
if (commit.getParentCount() != 1)
throw new IOException(String.format(
"Expected 1 parent for %s, found: %s",
id.name(), Arrays.asList(commit.getParents())));
RevCommit parent = commit.getParent(0);
pool.parseHeaders(parent);
Ref headRef = db.getRef(Constants.HEAD);
if (headRef == null)
throw new IOException("Missing HEAD");
RevCommit head = pool.parseCommit(headRef.getObjectId());
ThreeWayMerger merger = MergeStrategy.RECURSIVE.newMerger(db, true);
merger.setBase(parent.getTree());
if (merger.merge(head, commit)) {
if (AnyObjectId.equals(head.getTree(), merger.getResultTreeId()))
return null;
tick(1);
org.eclipse.jgit.lib.CommitBuilder b =
new org.eclipse.jgit.lib.CommitBuilder();
b.setParentId(head);
b.setTreeId(merger.getResultTreeId());
b.setAuthor(commit.getAuthorIdent());
b.setCommitter(new PersonIdent(defaultCommitter, new Date(now)));
b.setMessage(commit.getFullMessage());
ObjectId result;
try (ObjectInserter ins = inserter) {
result = ins.insert(b);
ins.flush();
}
update(Constants.HEAD, result);
return pool.parseCommit(result);
} else {
throw new IOException("Merge conflict");
}
}
/** /**
* Update the dumb client server info files. * Update the dumb client server info files.
* *

View File

@ -68,6 +68,10 @@
<id>repo.eclipse.org.cbi-releases</id> <id>repo.eclipse.org.cbi-releases</id>
<url>https://repo.eclipse.org/content/repositories/cbi-releases/</url> <url>https://repo.eclipse.org/content/repositories/cbi-releases/</url>
</pluginRepository> </pluginRepository>
<pluginRepository>
<id>repo.eclipse.org.cbi-snapshots</id>
<url>https://repo.eclipse.org/content/repositories/cbi-snapshots/</url>
</pluginRepository>
</pluginRepositories> </pluginRepositories>
<modules> <modules>
@ -210,7 +214,7 @@
<plugin> <plugin>
<groupId>org.eclipse.cbi.maven.plugins</groupId> <groupId>org.eclipse.cbi.maven.plugins</groupId>
<artifactId>eclipse-jarsigner-plugin</artifactId> <artifactId>eclipse-jarsigner-plugin</artifactId>
<version>1.1.1</version> <version>1.1.2-SNAPSHOT</version>
</plugin> </plugin>
<plugin> <plugin>
<groupId>org.codehaus.mojo</groupId> <groupId>org.codehaus.mojo</groupId>

View File

@ -47,14 +47,15 @@
import java.io.File; import java.io.File;
import java.io.IOException; import java.io.IOException;
import java.io.PrintWriter; import java.io.PrintWriter;
import java.lang.reflect.InvocationTargetException;
import java.net.MalformedURLException; import java.net.MalformedURLException;
import java.net.URL; import java.net.URL;
import java.text.MessageFormat; import java.text.MessageFormat;
import java.util.ArrayList; import java.util.ArrayList;
import java.util.List; import java.util.List;
import org.eclipse.jgit.console.ConsoleAuthenticator; import org.eclipse.jgit.awtui.AwtAuthenticator;
import org.eclipse.jgit.console.ConsoleCredentialsProvider; import org.eclipse.jgit.awtui.AwtCredentialsProvider;
import org.eclipse.jgit.errors.TransportException; import org.eclipse.jgit.errors.TransportException;
import org.eclipse.jgit.lib.Repository; import org.eclipse.jgit.lib.Repository;
import org.eclipse.jgit.lib.RepositoryBuilder; import org.eclipse.jgit.lib.RepositoryBuilder;
@ -115,8 +116,10 @@ public static void main(final String[] argv) {
*/ */
protected void run(final String[] argv) { protected void run(final String[] argv) {
try { try {
ConsoleAuthenticator.install(); if (!installConsole()) {
ConsoleCredentialsProvider.install(); AwtAuthenticator.install();
AwtCredentialsProvider.install();
}
configureHttpProxy(); configureHttpProxy();
execute(argv); execute(argv);
} catch (Die err) { } catch (Die err) {
@ -246,6 +249,45 @@ protected Repository openGitDir(String aGitdir) throws IOException {
return rb.build(); return rb.build();
} }
private static boolean installConsole() {
try {
install("org.eclipse.jgit.console.ConsoleAuthenticator"); //$NON-NLS-1$
install("org.eclipse.jgit.console.ConsoleCredentialsProvider"); //$NON-NLS-1$
return true;
} catch (ClassNotFoundException e) {
return false;
} catch (NoClassDefFoundError e) {
return false;
} catch (UnsupportedClassVersionError e) {
return false;
} catch (IllegalArgumentException e) {
throw new RuntimeException(CLIText.get().cannotSetupConsole, e);
} catch (SecurityException e) {
throw new RuntimeException(CLIText.get().cannotSetupConsole, e);
} catch (IllegalAccessException e) {
throw new RuntimeException(CLIText.get().cannotSetupConsole, e);
} catch (InvocationTargetException e) {
throw new RuntimeException(CLIText.get().cannotSetupConsole, e);
} catch (NoSuchMethodException e) {
throw new RuntimeException(CLIText.get().cannotSetupConsole, e);
}
}
private static void install(final String name)
throws IllegalAccessException, InvocationTargetException,
NoSuchMethodException, ClassNotFoundException {
try {
Class.forName(name).getMethod("install").invoke(null); //$NON-NLS-1$
} catch (InvocationTargetException e) {
if (e.getCause() instanceof RuntimeException)
throw (RuntimeException) e.getCause();
if (e.getCause() instanceof Error)
throw (Error) e.getCause();
throw e;
}
}
/** /**
* Configure the JRE's standard HTTP based on <code>http_proxy</code>. * Configure the JRE's standard HTTP based on <code>http_proxy</code>.
* <p> * <p>

View File

@ -180,7 +180,7 @@ public void testPullConflict() throws Exception {
+ remoteUri + remoteUri
+ "\nSource change\n=======\nTarget change\n>>>>>>> 42453fd Target change in local\n"; + "\nSource change\n=======\nTarget change\n>>>>>>> 42453fd Target change in local\n";
assertFileContentsEqual(targetFile, result); assertFileContentsEqual(targetFile, result);
assertEquals(RepositoryState.REBASING_INTERACTIVE, target assertEquals(RepositoryState.REBASING_MERGE, target
.getRepository().getRepositoryState()); .getRepository().getRepositoryState());
} }
@ -225,7 +225,7 @@ public void testPullLocalConflict() throws Exception {
String result = "<<<<<<< Upstream, based on branch 'master' of local repository\n" String result = "<<<<<<< Upstream, based on branch 'master' of local repository\n"
+ "Master change\n=======\nSlave change\n>>>>>>> 4049c9e Source change in based on master\n"; + "Master change\n=======\nSlave change\n>>>>>>> 4049c9e Source change in based on master\n";
assertFileContentsEqual(targetFile, result); assertFileContentsEqual(targetFile, result);
assertEquals(RepositoryState.REBASING_INTERACTIVE, target assertEquals(RepositoryState.REBASING_MERGE, target
.getRepository().getRepositoryState()); .getRepository().getRepositoryState());
} }

View File

@ -824,7 +824,7 @@ public void testStopOnConflict() throws Exception {
"<<<<<<< Upstream, based on master\n1master\n=======\n1topic", "<<<<<<< Upstream, based on master\n1master\n=======\n1topic",
">>>>>>> e0d1dea change file1 in topic\n2\n3\ntopic4"); ">>>>>>> e0d1dea change file1 in topic\n2\n3\ntopic4");
assertEquals(RepositoryState.REBASING_INTERACTIVE, db assertEquals(RepositoryState.REBASING_MERGE, db
.getRepositoryState()); .getRepositoryState());
assertTrue(new File(db.getDirectory(), "rebase-merge").exists()); assertTrue(new File(db.getDirectory(), "rebase-merge").exists());
// the first one should be included, so we should have left two picks in // the first one should be included, so we should have left two picks in
@ -887,7 +887,7 @@ public void testStopOnConflictAndAbortWithDetachedHEAD() throws Exception {
"<<<<<<< Upstream, based on master\n1master\n=======\n1topic", "<<<<<<< Upstream, based on master\n1master\n=======\n1topic",
">>>>>>> e0d1dea change file1 in topic\n2\n3\ntopic4"); ">>>>>>> e0d1dea change file1 in topic\n2\n3\ntopic4");
assertEquals(RepositoryState.REBASING_INTERACTIVE, assertEquals(RepositoryState.REBASING_MERGE,
db.getRepositoryState()); db.getRepositoryState());
assertTrue(new File(db.getDirectory(), "rebase-merge").exists()); assertTrue(new File(db.getDirectory(), "rebase-merge").exists());
// the first one should be included, so we should have left two picks in // the first one should be included, so we should have left two picks in
@ -1009,7 +1009,7 @@ public void testStopOnConflictAndContinueWithNoDeltaToMaster()
res = git.rebase().setOperation(Operation.CONTINUE).call(); res = git.rebase().setOperation(Operation.CONTINUE).call();
assertNotNull(res); assertNotNull(res);
assertEquals(Status.NOTHING_TO_COMMIT, res.getStatus()); assertEquals(Status.NOTHING_TO_COMMIT, res.getStatus());
assertEquals(RepositoryState.REBASING_INTERACTIVE, assertEquals(RepositoryState.REBASING_MERGE,
db.getRepositoryState()); db.getRepositoryState());
git.rebase().setOperation(Operation.SKIP).call(); git.rebase().setOperation(Operation.SKIP).call();
@ -1300,7 +1300,7 @@ public void testStopOnConflictCommitAndContinue() throws Exception {
// user can decide what to do. if he accidentally committed, reset soft, // user can decide what to do. if he accidentally committed, reset soft,
// and continue, if he really has nothing to commit, skip. // and continue, if he really has nothing to commit, skip.
assertEquals(Status.NOTHING_TO_COMMIT, res.getStatus()); assertEquals(Status.NOTHING_TO_COMMIT, res.getStatus());
assertEquals(RepositoryState.REBASING_INTERACTIVE, assertEquals(RepositoryState.REBASING_MERGE,
db.getRepositoryState()); db.getRepositoryState());
git.rebase().setOperation(Operation.SKIP).call(); git.rebase().setOperation(Operation.SKIP).call();
@ -1401,7 +1401,7 @@ public void testStopOnConflictFileCreationAndDeletion() throws Exception {
assertEquals(Status.STOPPED, res.getStatus()); assertEquals(Status.STOPPED, res.getStatus());
assertEquals(conflicting, res.getCurrentCommit()); assertEquals(conflicting, res.getCurrentCommit());
assertEquals(RepositoryState.REBASING_INTERACTIVE, db assertEquals(RepositoryState.REBASING_MERGE, db
.getRepositoryState()); .getRepositoryState());
assertTrue(new File(db.getDirectory(), "rebase-merge").exists()); assertTrue(new File(db.getDirectory(), "rebase-merge").exists());
// the first one should be included, so we should have left two picks in // the first one should be included, so we should have left two picks in

View File

@ -46,6 +46,7 @@
import static java.nio.charset.StandardCharsets.UTF_8; import static java.nio.charset.StandardCharsets.UTF_8;
import static org.junit.Assert.assertEquals; import static org.junit.Assert.assertEquals;
import static org.junit.Assert.assertFalse; import static org.junit.Assert.assertFalse;
import static org.junit.Assert.assertNotEquals;
import static org.junit.Assert.assertNull; import static org.junit.Assert.assertNull;
import static org.junit.Assert.assertSame; import static org.junit.Assert.assertSame;
import static org.junit.Assert.assertTrue; import static org.junit.Assert.assertTrue;
@ -55,9 +56,9 @@
import org.eclipse.jgit.internal.storage.dfs.DfsRepositoryDescription; import org.eclipse.jgit.internal.storage.dfs.DfsRepositoryDescription;
import org.eclipse.jgit.internal.storage.dfs.InMemoryRepository; import org.eclipse.jgit.internal.storage.dfs.InMemoryRepository;
import org.eclipse.jgit.lib.ObjectId;
import org.eclipse.jgit.lib.AnyObjectId; import org.eclipse.jgit.lib.AnyObjectId;
import org.eclipse.jgit.lib.Constants; import org.eclipse.jgit.lib.Constants;
import org.eclipse.jgit.lib.ObjectId;
import org.eclipse.jgit.lib.ObjectLoader; import org.eclipse.jgit.lib.ObjectLoader;
import org.eclipse.jgit.lib.PersonIdent; import org.eclipse.jgit.lib.PersonIdent;
import org.eclipse.jgit.lib.Ref; import org.eclipse.jgit.lib.Ref;
@ -296,6 +297,83 @@ public void commitToUnbornHead() throws Exception {
assertEquals("refs/heads/master", ref.getTarget().getName()); assertEquals("refs/heads/master", ref.getTarget().getName());
} }
@Test
public void cherryPick() throws Exception {
repo.updateRef("HEAD").link("refs/heads/master");
RevCommit head = tr.branch("master").commit()
.add("foo", "foo contents\n")
.create();
rw.parseBody(head);
RevCommit toPick = tr.commit()
.parent(tr.commit().create()) // Can't cherry-pick root.
.author(new PersonIdent("Cherrypick Author", "cpa@example.com",
tr.getClock(), tr.getTimeZone()))
.author(new PersonIdent("Cherrypick Committer", "cpc@example.com",
tr.getClock(), tr.getTimeZone()))
.message("message to cherry-pick")
.add("bar", "bar contents\n")
.create();
RevCommit result = tr.cherryPick(toPick);
rw.parseBody(result);
Ref headRef = tr.getRepository().getRef("HEAD");
assertEquals(result, headRef.getObjectId());
assertTrue(headRef.isSymbolic());
assertEquals("refs/heads/master", headRef.getLeaf().getName());
assertEquals(1, result.getParentCount());
assertEquals(head, result.getParent(0));
assertEquals(toPick.getAuthorIdent(), result.getAuthorIdent());
// Committer name/email matches default, and time was incremented.
assertEquals(new PersonIdent(head.getCommitterIdent(), new Date(0)),
new PersonIdent(result.getCommitterIdent(), new Date(0)));
assertTrue(toPick.getCommitTime() < result.getCommitTime());
assertEquals("message to cherry-pick", result.getFullMessage());
assertEquals("foo contents\n", blobAsString(result, "foo"));
assertEquals("bar contents\n", blobAsString(result, "bar"));
}
@Test
public void cherryPickWithContentMerge() throws Exception {
RevCommit base = tr.branch("HEAD").commit()
.add("foo", "foo contents\n\n")
.create();
tr.branch("HEAD").commit()
.add("foo", "foo contents\n\nlast line\n")
.create();
RevCommit toPick = tr.commit()
.message("message to cherry-pick")
.parent(base)
.add("foo", "changed foo contents\n\n")
.create();
RevCommit result = tr.cherryPick(toPick);
rw.parseBody(result);
assertEquals("message to cherry-pick", result.getFullMessage());
assertEquals("changed foo contents\n\nlast line\n",
blobAsString(result, "foo"));
}
@Test
public void cherryPickWithIdenticalContents() throws Exception {
RevCommit base = tr.branch("HEAD").commit().add("foo", "foo contents\n")
.create();
RevCommit head = tr.branch("HEAD").commit()
.parent(base)
.add("bar", "bar contents\n")
.create();
RevCommit toPick = tr.commit()
.parent(base)
.message("message to cherry-pick")
.add("bar", "bar contents\n")
.create();
assertNotEquals(head, toPick);
assertNull(tr.cherryPick(toPick));
assertEquals(head, repo.getRef("HEAD").getObjectId());
}
private String blobAsString(AnyObjectId treeish, String path) private String blobAsString(AnyObjectId treeish, String path)
throws Exception { throws Exception {
RevObject obj = tr.get(rw.parseTree(treeish), path); RevObject obj = tr.get(rw.parseTree(treeish), path);

View File

@ -120,6 +120,42 @@ public void testValidCommitBlankAuthor() throws CorruptObjectException {
checker.check(Constants.OBJ_COMMIT, data); checker.check(Constants.OBJ_COMMIT, data);
} }
@Test
public void testCommitCorruptAuthor() throws CorruptObjectException {
StringBuilder b = new StringBuilder();
b.append("tree be9bfa841874ccc9f2ef7c48d0c76226f89b7189\n");
b.append("author b <b@c> <b@c> 0 +0000\n");
b.append("committer <> 0 +0000\n");
byte[] data = Constants.encodeASCII(b.toString());
try {
checker.checkCommit(data);
fail("Did not catch corrupt object");
} catch (CorruptObjectException e) {
assertEquals("invalid author", e.getMessage());
}
checker.setAllowInvalidPersonIdent(true);
checker.checkCommit(data);
}
@Test
public void testCommitCorruptCommitter() throws CorruptObjectException {
StringBuilder b = new StringBuilder();
b.append("tree be9bfa841874ccc9f2ef7c48d0c76226f89b7189\n");
b.append("author <> 0 +0000\n");
b.append("committer b <b@c> <b@c> 0 +0000\n");
byte[] data = Constants.encodeASCII(b.toString());
try {
checker.checkCommit(data);
fail("Did not catch corrupt object");
} catch (CorruptObjectException e) {
assertEquals("invalid committer", e.getMessage());
}
checker.setAllowInvalidPersonIdent(true);
checker.checkCommit(data);
}
@Test @Test
public void testValidCommit1Parent() throws CorruptObjectException { public void testValidCommit1Parent() throws CorruptObjectException {
final StringBuilder b = new StringBuilder(); final StringBuilder b = new StringBuilder();
@ -940,7 +976,8 @@ public void testValidTagHasNoTaggerHeader() throws CorruptObjectException {
} }
@Test @Test
public void testInvalidTagInvalidTaggerHeader1() { public void testInvalidTagInvalidTaggerHeader1()
throws CorruptObjectException {
final StringBuilder b = new StringBuilder(); final StringBuilder b = new StringBuilder();
b.append("object "); b.append("object ");
@ -958,6 +995,8 @@ public void testInvalidTagInvalidTaggerHeader1() {
} catch (CorruptObjectException e) { } catch (CorruptObjectException e) {
assertEquals("invalid tagger", e.getMessage()); assertEquals("invalid tagger", e.getMessage());
} }
checker.setAllowInvalidPersonIdent(true);
checker.checkTag(data);
} }
@Test @Test

View File

@ -51,11 +51,25 @@
import org.eclipse.jgit.diff.RawText; import org.eclipse.jgit.diff.RawText;
import org.eclipse.jgit.diff.RawTextComparator; import org.eclipse.jgit.diff.RawTextComparator;
import org.eclipse.jgit.lib.Constants; import org.eclipse.jgit.lib.Constants;
import org.junit.Assume;
import org.junit.Test; import org.junit.Test;
import org.junit.experimental.theories.DataPoints;
import org.junit.experimental.theories.Theories;
import org.junit.runner.RunWith;
@RunWith(Theories.class)
public class MergeAlgorithmTest { public class MergeAlgorithmTest {
MergeFormatter fmt=new MergeFormatter(); MergeFormatter fmt=new MergeFormatter();
private final boolean newlineAtEnd;
@DataPoints
public static boolean[] newlineAtEndDataPoints = { false, true };
public MergeAlgorithmTest(boolean newlineAtEnd) {
this.newlineAtEnd = newlineAtEnd;
}
/** /**
* Check for a conflict where the second text was changed similar to the * Check for a conflict where the second text was changed similar to the
* first one, but the second texts modification covers one more line. * first one, but the second texts modification covers one more line.
@ -174,28 +188,55 @@ public void testAdjacentModifications() throws IOException {
} }
@Test @Test
public void testSeperateModifications() throws IOException { public void testSeparateModifications() throws IOException {
assertEquals(t("aZcYe"), merge("abcde", "aZcde", "abcYe")); assertEquals(t("aZcYe"), merge("abcde", "aZcde", "abcYe"));
} }
@Test
public void testBlankLines() throws IOException {
assertEquals(t("aZc\nYe"), merge("abc\nde", "aZc\nde", "abc\nYe"));
}
/** /**
* Test merging two contents which do one similar modification and one * Test merging two contents which do one similar modification and one
* insertion is only done by one side. Between modification and insertion is * insertion is only done by one side, in the middle. Between modification
* a block which is common between the two contents and the common base * and insertion is a block which is common between the two contents and the
* common base
* *
* @throws IOException * @throws IOException
*/ */
@Test @Test
public void testTwoSimilarModsAndOneInsert() throws IOException { public void testTwoSimilarModsAndOneInsert() throws IOException {
assertEquals(t("IAAJ"), merge("iA", "IA", "IAAJ"));
assertEquals(t("aBcDde"), merge("abcde", "aBcde", "aBcDde")); assertEquals(t("aBcDde"), merge("abcde", "aBcde", "aBcDde"));
assertEquals(t("IAJ"), merge("iA", "IA", "IAJ"));
assertEquals(t("IAAAJ"), merge("iA", "IA", "IAAAJ"));
assertEquals(t("IAAAJCAB"), merge("iACAB", "IACAB", "IAAAJCAB")); assertEquals(t("IAAAJCAB"), merge("iACAB", "IACAB", "IAAAJCAB"));
assertEquals(t("HIAAAJCAB"), merge("HiACAB", "HIACAB", "HIAAAJCAB")); assertEquals(t("HIAAAJCAB"), merge("HiACAB", "HIACAB", "HIAAAJCAB"));
assertEquals(t("AGADEFHIAAAJCAB"), assertEquals(t("AGADEFHIAAAJCAB"),
merge("AGADEFHiACAB", "AGADEFHIACAB", "AGADEFHIAAAJCAB")); merge("AGADEFHiACAB", "AGADEFHIACAB", "AGADEFHIAAAJCAB"));
}
/**
* Test merging two contents which do one similar modification and one
* insertion is only done by one side, at the end. Between modification and
* insertion is a block which is common between the two contents and the
* common base
*
* @throws IOException
*/
@Test
public void testTwoSimilarModsAndOneInsertAtEnd() throws IOException {
Assume.assumeTrue(newlineAtEnd);
assertEquals(t("IAAJ"), merge("iA", "IA", "IAAJ"));
assertEquals(t("IAJ"), merge("iA", "IA", "IAJ"));
assertEquals(t("IAAAJ"), merge("iA", "IA", "IAAAJ"));
}
@Test
public void testTwoSimilarModsAndOneInsertAtEndNoNewlineAtEnd()
throws IOException {
Assume.assumeFalse(newlineAtEnd);
assertEquals(t("I<A=AAJ>"), merge("iA", "IA", "IAAJ"));
assertEquals(t("I<A=AJ>"), merge("iA", "IA", "IAJ"));
assertEquals(t("I<A=AAAJ>"), merge("iA", "IA", "IAAAJ"));
} }
/** /**
@ -225,7 +266,7 @@ private String merge(String commonBase, String ours, String theirs) throws IOExc
return new String(bo.toByteArray(), Constants.CHARACTER_ENCODING); return new String(bo.toByteArray(), Constants.CHARACTER_ENCODING);
} }
public static String t(String text) { public String t(String text) {
StringBuilder r = new StringBuilder(); StringBuilder r = new StringBuilder();
for (int i = 0; i < text.length(); i++) { for (int i = 0; i < text.length(); i++) {
char c = text.charAt(i); char c = text.charAt(i);
@ -241,13 +282,14 @@ public static String t(String text) {
break; break;
default: default:
r.append(c); r.append(c);
r.append('\n'); if (newlineAtEnd || i < text.length() - 1)
r.append('\n');
} }
} }
return r.toString(); return r.toString();
} }
public static RawText T(String text) { public RawText T(String text) {
return new RawText(Constants.encode(t(text))); return new RawText(Constants.encode(t(text)));
} }
} }

View File

@ -175,6 +175,69 @@ public void crissCrossMerge(MergeStrategy strategy, IndexState indexState,
} }
} }
@Theory
/**
* Merging m2,s2 from the following topology. m1 and s1 are the two root
* commits of the repo. In master and side different files are touched.
* No need to do a real content merge.
*
* <pre>
* m1--m2
* \/
* /\
* s1--s2
* </pre>
*/
public void crissCrossMerge_twoRoots(MergeStrategy strategy,
IndexState indexState, WorktreeState worktreeState)
throws Exception {
if (!validateStates(indexState, worktreeState))
return;
// fill the repo
BranchBuilder master = db_t.branch("master");
BranchBuilder side = db_t.branch("side");
RevCommit m1 = master.commit().add("m", "m1").message("m1").create();
db_t.getRevWalk().parseCommit(m1);
RevCommit s1 = side.commit().add("s", "s1").message("s1").create();
RevCommit s2 = side.commit().parent(m1).add("m", "m1")
.message("s2(merge)").create();
RevCommit m2 = master.commit().parent(s1).add("s", "s1")
.message("m2(merge)").create();
Git git = Git.wrap(db);
git.checkout().setName("master").call();
modifyWorktree(worktreeState, "m", "side");
modifyWorktree(worktreeState, "s", "side");
modifyIndex(indexState, "m", "side");
modifyIndex(indexState, "s", "side");
ResolveMerger merger = (ResolveMerger) strategy.newMerger(db,
worktreeState == WorktreeState.Bare);
if (worktreeState != WorktreeState.Bare)
merger.setWorkingTreeIterator(new FileTreeIterator(db));
try {
boolean expectSuccess = true;
if (!(indexState == IndexState.Bare
|| indexState == IndexState.Missing
|| indexState == IndexState.SameAsHead || indexState == IndexState.SameAsOther))
// index is dirty
expectSuccess = false;
assertEquals(Boolean.valueOf(expectSuccess),
Boolean.valueOf(merger.merge(new RevCommit[] { m2, s2 })));
assertEquals(MergeStrategy.RECURSIVE, strategy);
assertEquals("m1",
contentAsString(db, merger.getResultTreeId(), "m"));
assertEquals("s1",
contentAsString(db, merger.getResultTreeId(), "s"));
} catch (NoMergeBaseException e) {
assertEquals(MergeStrategy.RESOLVE, strategy);
assertEquals(e.getReason(),
MergeBaseFailureReason.MULTIPLE_MERGE_BASES_NOT_SUPPORTED);
}
}
@Theory @Theory
/** /**
* Merging m2,s2 from the following topology. The same file is modified * Merging m2,s2 from the following topology. The same file is modified

View File

@ -184,7 +184,7 @@ public void checkMergeMergeableTreesWithoutIndex(MergeStrategy strategy)
MergeResult mergeRes = git.merge().setStrategy(strategy) MergeResult mergeRes = git.merge().setStrategy(strategy)
.include(masterCommit).call(); .include(masterCommit).call();
assertEquals(MergeStatus.MERGED, mergeRes.getMergeStatus()); assertEquals(MergeStatus.MERGED, mergeRes.getMergeStatus());
assertEquals("[d/1, mode:100644, content:1master\n2\n3side\n]", assertEquals("[d/1, mode:100644, content:1master\n2\n3side]",
indexState(CONTENT)); indexState(CONTENT));
} }
@ -561,7 +561,7 @@ public void checkMergeCrissCross(MergeStrategy strategy) throws Exception {
assertEquals(MergeStrategy.RECURSIVE, strategy); assertEquals(MergeStrategy.RECURSIVE, strategy);
assertEquals(MergeResult.MergeStatus.MERGED, assertEquals(MergeResult.MergeStatus.MERGED,
mergeResult.getMergeStatus()); mergeResult.getMergeStatus());
assertEquals("1master2\n2\n3side2\n", read("1")); assertEquals("1master2\n2\n3side2", read("1"));
} catch (JGitInternalException e) { } catch (JGitInternalException e) {
assertEquals(MergeStrategy.RESOLVE, strategy); assertEquals(MergeStrategy.RESOLVE, strategy);
assertTrue(e.getCause() instanceof NoMergeBaseException); assertTrue(e.getCause() instanceof NoMergeBaseException);
@ -697,7 +697,7 @@ public void checkForCorrectIndex(MergeStrategy strategy) throws Exception {
assertEquals( assertEquals(
"[0, mode:100644, content:master]" // "[0, mode:100644, content:master]" //
+ "[1, mode:100644, content:side]" // + "[1, mode:100644, content:side]" //
+ "[2, mode:100644, content:1master\n2\n3side\n]" // + "[2, mode:100644, content:1master\n2\n3side]" //
+ "[3, mode:100644, stage:1, content:orig][3, mode:100644, stage:2, content:side][3, mode:100644, stage:3, content:master]" // + "[3, mode:100644, stage:1, content:orig][3, mode:100644, stage:2, content:side][3, mode:100644, stage:3, content:master]" //
+ "[4, mode:100644, content:orig]", // + "[4, mode:100644, content:orig]", //
indexState(CONTENT)); indexState(CONTENT));

View File

@ -214,21 +214,6 @@ public void testCull() throws Exception {
assertNull(objw.nextObject()); assertNull(objw.nextObject());
} }
@Test
public void testMarkUninterestingPropagation() throws Exception {
final RevBlob f = blob("1");
final RevTree t = tree(file("f", f));
final RevCommit c1 = commit(t);
final RevCommit c2 = commit(t);
markUninteresting(c1);
markStart(c2);
assertSame(c2, objw.next());
assertNull(objw.next());
assertNull(objw.nextObject());
}
@Test @Test
public void testEmptyTreeCorruption() throws Exception { public void testEmptyTreeCorruption() throws Exception {
ObjectId bId = ObjectId ObjectId bId = ObjectId

View File

@ -0,0 +1,204 @@
/*
* Copyright (C) 2010, Google Inc.
* Copyright (C) 2008, Robin Rosenberg <robin.rosenberg@dewire.com>
* Copyright (C) 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.awtui;
import java.awt.GridBagConstraints;
import java.awt.GridBagLayout;
import java.awt.Insets;
import javax.swing.JLabel;
import javax.swing.JOptionPane;
import javax.swing.JPanel;
import javax.swing.JPasswordField;
import javax.swing.JTextField;
import org.eclipse.jgit.errors.UnsupportedCredentialItem;
import org.eclipse.jgit.transport.CredentialItem;
import org.eclipse.jgit.transport.CredentialsProvider;
import org.eclipse.jgit.transport.URIish;
/** Interacts with the user during authentication by using AWT/Swing dialogs. */
public class AwtCredentialsProvider extends CredentialsProvider {
/** Install this implementation as the default. */
public static void install() {
CredentialsProvider.setDefault(new AwtCredentialsProvider());
}
@Override
public boolean isInteractive() {
return true;
}
@Override
public boolean supports(CredentialItem... items) {
for (CredentialItem i : items) {
if (i instanceof CredentialItem.StringType)
continue;
else if (i instanceof CredentialItem.CharArrayType)
continue;
else if (i instanceof CredentialItem.YesNoType)
continue;
else if (i instanceof CredentialItem.InformationalMessage)
continue;
else
return false;
}
return true;
}
@Override
public boolean get(URIish uri, CredentialItem... items)
throws UnsupportedCredentialItem {
if (items.length == 0) {
return true;
} else if (items.length == 1) {
final CredentialItem item = items[0];
if (item instanceof CredentialItem.InformationalMessage) {
JOptionPane.showMessageDialog(null, item.getPromptText(),
UIText.get().warning, JOptionPane.INFORMATION_MESSAGE);
return true;
} else if (item instanceof CredentialItem.YesNoType) {
CredentialItem.YesNoType v = (CredentialItem.YesNoType) item;
int r = JOptionPane.showConfirmDialog(null, v.getPromptText(),
UIText.get().warning, JOptionPane.YES_NO_OPTION);
switch (r) {
case JOptionPane.YES_OPTION:
v.setValue(true);
return true;
case JOptionPane.NO_OPTION:
v.setValue(false);
return true;
case JOptionPane.CANCEL_OPTION:
case JOptionPane.CLOSED_OPTION:
default:
return false;
}
} else {
return interactive(uri, items);
}
} else {
return interactive(uri, items);
}
}
private static boolean interactive(URIish uri, CredentialItem[] items) {
final GridBagConstraints gbc = new GridBagConstraints(0, 0, 1, 1, 1, 1,
GridBagConstraints.NORTHWEST, GridBagConstraints.NONE,
new Insets(0, 0, 0, 0), 0, 0);
final JPanel panel = new JPanel();
panel.setLayout(new GridBagLayout());
final JTextField[] texts = new JTextField[items.length];
for (int i = 0; i < items.length; i++) {
CredentialItem item = items[i];
if (item instanceof CredentialItem.StringType
|| item instanceof CredentialItem.CharArrayType) {
gbc.fill = GridBagConstraints.NONE;
gbc.gridwidth = GridBagConstraints.RELATIVE;
gbc.gridx = 0;
panel.add(new JLabel(item.getPromptText()), gbc);
gbc.fill = GridBagConstraints.HORIZONTAL;
gbc.gridwidth = GridBagConstraints.RELATIVE;
gbc.gridx = 1;
if (item.isValueSecure())
texts[i] = new JPasswordField(20);
else
texts[i] = new JTextField(20);
panel.add(texts[i], gbc);
gbc.gridy++;
} else if (item instanceof CredentialItem.InformationalMessage) {
gbc.fill = GridBagConstraints.NONE;
gbc.gridwidth = GridBagConstraints.REMAINDER;
gbc.gridx = 0;
panel.add(new JLabel(item.getPromptText()), gbc);
gbc.gridy++;
} else {
throw new UnsupportedCredentialItem(uri, item.getPromptText());
}
}
if (JOptionPane.showConfirmDialog(null, panel,
UIText.get().authenticationRequired,
JOptionPane.OK_CANCEL_OPTION, JOptionPane.QUESTION_MESSAGE) != JOptionPane.OK_OPTION)
return false; // cancel
for (int i = 0; i < items.length; i++) {
CredentialItem item = items[i];
JTextField f = texts[i];
if (item instanceof CredentialItem.StringType) {
CredentialItem.StringType v = (CredentialItem.StringType) item;
if (f instanceof JPasswordField)
v.setValue(new String(((JPasswordField) f).getPassword()));
else
v.setValue(f.getText());
} else if (item instanceof CredentialItem.CharArrayType) {
CredentialItem.CharArrayType v = (CredentialItem.CharArrayType) item;
if (f instanceof JPasswordField)
v.setValueNoCopy(((JPasswordField) f).getPassword());
else
v.setValueNoCopy(f.getText().toCharArray());
}
}
return true;
}
}

View File

@ -1,50 +1,5 @@
<?xml version="1.0" encoding="UTF-8" standalone="no"?> <?xml version="1.0" encoding="UTF-8" standalone="no"?>
<component id="org.eclipse.jgit" version="2"> <component id="org.eclipse.jgit" version="2">
<resource path="META-INF/MANIFEST.MF">
<filter comment="minor addition" id="924844039">
<message_arguments>
<message_argument value="3.4.0"/>
<message_argument value="3.4.0"/>
</message_arguments>
</filter>
</resource>
<resource path="src/org/eclipse/jgit/lib/ObjectInserter.java" type="org.eclipse.jgit.lib.ObjectInserter">
<filter id="336695337">
<message_arguments>
<message_argument value="org.eclipse.jgit.lib.ObjectInserter"/>
<message_argument value="newReader()"/>
</message_arguments>
</filter>
</resource>
<resource path="src/org/eclipse/jgit/merge/ResolveMerger.java" type="org.eclipse.jgit.merge.ResolveMerger">
<filter comment="Doesn't break consumers. Breaking providers is allowed also in minor versions." id="338792546">
<message_arguments>
<message_argument value="org.eclipse.jgit.merge.ResolveMerger"/>
<message_argument value="mergeTreeWalk(TreeWalk)"/>
</message_arguments>
</filter>
<filter comment="Doesn't break consumers. Breaking providers is allowed also in minor versions." id="338792546">
<message_arguments>
<message_argument value="org.eclipse.jgit.merge.ResolveMerger"/>
<message_argument value="mergeTrees(AbstractTreeIterator, RevTree, RevTree)"/>
</message_arguments>
</filter>
<filter comment="Doesn't break consumers. Breaking providers is allowed also in minor versions." id="338792546">
<message_arguments>
<message_argument value="org.eclipse.jgit.merge.ResolveMerger"/>
<message_argument value="processEntry(CanonicalTreeParser, CanonicalTreeParser, CanonicalTreeParser, DirCacheBuildIterator, WorkingTreeIterator)"/>
</message_arguments>
</filter>
</resource>
<resource path="src/org/eclipse/jgit/transport/GitProtocolConstants.java" type="org.eclipse.jgit.transport.GitProtocolConstants">
<filter id="388194388">
<message_arguments>
<message_argument value="org.eclipse.jgit.transport.GitProtocolConstants"/>
<message_argument value="CAPABILITY_ATOMIC"/>
<message_argument value="atomic-push"/>
</message_arguments>
</filter>
</resource>
<resource path="src/org/eclipse/jgit/util/FileUtil.java" type="org.eclipse.jgit.util.FileUtil"> <resource path="src/org/eclipse/jgit/util/FileUtil.java" type="org.eclipse.jgit.util.FileUtil">
<filter comment="moved into another bundle keeping original package" id="1110441988"> <filter comment="moved into another bundle keeping original package" id="1110441988">
<message_arguments> <message_arguments>

View File

@ -44,6 +44,7 @@ cannotBeCombined=Cannot be combined.
cannotBeRecursiveWhenTreesAreIncluded=TreeWalk shouldn't be recursive when tree objects are included. cannotBeRecursiveWhenTreesAreIncluded=TreeWalk shouldn't be recursive when tree objects are included.
cannotChangeActionOnComment=Cannot change action on comment line in git-rebase-todo file, old action: {0}, new action: {1}. cannotChangeActionOnComment=Cannot change action on comment line in git-rebase-todo file, old action: {0}, new action: {1}.
cannotChangeToComment=Cannot change a non-comment line to a comment line. cannotChangeToComment=Cannot change a non-comment line to a comment line.
cannotCheckoutOursSwitchBranch=Checking out ours/theirs is only possible when checking out index, not when switching branches.
cannotCombineSquashWithNoff=Cannot combine --squash with --no-ff. cannotCombineSquashWithNoff=Cannot combine --squash with --no-ff.
cannotCombineTreeFilterWithRevFilter=Cannot combine TreeFilter {0} with RevFilter {1}. cannotCombineTreeFilterWithRevFilter=Cannot combine TreeFilter {0} with RevFilter {1}.
cannotCommitOnARepoWithState=Cannot commit on a repo with state: {0} cannotCommitOnARepoWithState=Cannot commit on a repo with state: {0}
@ -231,6 +232,7 @@ fileCannotBeDeleted=File cannot be deleted: {0}
fileIsTooBigForThisConvenienceMethod=File is too big for this convenience method ({0} bytes). fileIsTooBigForThisConvenienceMethod=File is too big for this convenience method ({0} bytes).
fileIsTooLarge=File is too large: {0} fileIsTooLarge=File is too large: {0}
fileModeNotSetForPath=FileMode not set for path {0} fileModeNotSetForPath=FileMode not set for path {0}
findingGarbage=Finding garbage
flagIsDisposed={0} is disposed. flagIsDisposed={0} is disposed.
flagNotFromThis={0} not from this. flagNotFromThis={0} not from this.
flagsAlreadyCreated={0} flags already created. flagsAlreadyCreated={0} flags already created.
@ -258,6 +260,7 @@ indexWriteException=Modified index could not be written
initFailedBareRepoDifferentDirs=When initializing a bare repo with directory {0} and separate git-dir {1} specified both folders must point to the same location initFailedBareRepoDifferentDirs=When initializing a bare repo with directory {0} and separate git-dir {1} specified both folders must point to the same location
initFailedNonBareRepoSameDirs=When initializing a non-bare repo with directory {0} and separate git-dir {1} specified both folders should not point to the same location initFailedNonBareRepoSameDirs=When initializing a non-bare repo with directory {0} and separate git-dir {1} specified both folders should not point to the same location
inMemoryBufferLimitExceeded=In-memory buffer limit exceeded inMemoryBufferLimitExceeded=In-memory buffer limit exceeded
inputDidntMatchLength=Input did not match supplied length. {0} bytes are missing.
inputStreamMustSupportMark=InputStream must support mark() inputStreamMustSupportMark=InputStream must support mark()
integerValueOutOfRange=Integer value {0}.{1} out of range integerValueOutOfRange=Integer value {0}.{1} out of range
internalRevisionError=internal revision error internalRevisionError=internal revision error
@ -391,6 +394,7 @@ packfileCorruptionDetected=Packfile corruption detected: {0}
packFileInvalid=Pack file invalid: {0} packFileInvalid=Pack file invalid: {0}
packfileIsTruncated=Packfile {0} is truncated. packfileIsTruncated=Packfile {0} is truncated.
packfileIsTruncatedNoParam=Packfile is truncated. packfileIsTruncatedNoParam=Packfile is truncated.
packHandleIsStale=Pack file {0} handle is stale, removing it from pack list
packHasUnresolvedDeltas=pack has unresolved deltas packHasUnresolvedDeltas=pack has unresolved deltas
packingCancelledDuringObjectsWriting=Packing cancelled during objects writing packingCancelledDuringObjectsWriting=Packing cancelled during objects writing
packObjectCountMismatch=Pack object count mismatch: pack {0} index {1}: {2} packObjectCountMismatch=Pack object count mismatch: pack {0} index {1}: {2}
@ -502,6 +506,7 @@ statelessRPCRequiresOptionToBeEnabled=stateless RPC requires {0} to be enabled
submoduleExists=Submodule ''{0}'' already exists in the index submoduleExists=Submodule ''{0}'' already exists in the index
submoduleParentRemoteUrlInvalid=Cannot remove segment from remote url ''{0}'' submoduleParentRemoteUrlInvalid=Cannot remove segment from remote url ''{0}''
submodulesNotSupported=Submodules are not supported submodulesNotSupported=Submodules are not supported
supportOnlyPackIndexVersion2=Only support index version 2
symlinkCannotBeWrittenAsTheLinkTarget=Symlink "{0}" cannot be written as the link target cannot be read from within Java. symlinkCannotBeWrittenAsTheLinkTarget=Symlink "{0}" cannot be written as the link target cannot be read from within Java.
systemConfigFileInvalid=Systen wide config file {0} is invalid {1} systemConfigFileInvalid=Systen wide config file {0} is invalid {1}
tagAlreadyExists=tag ''{0}'' already exists tagAlreadyExists=tag ''{0}'' already exists
@ -536,6 +541,7 @@ truncatedHunkNewLinesMissing=Truncated hunk, at least {0} new lines is missing
truncatedHunkOldLinesMissing=Truncated hunk, at least {0} old lines is missing truncatedHunkOldLinesMissing=Truncated hunk, at least {0} old lines is missing
tSizeMustBeGreaterOrEqual1=tSize must be >= 1 tSizeMustBeGreaterOrEqual1=tSize must be >= 1
unableToCheckConnectivity=Unable to check connectivity. unableToCheckConnectivity=Unable to check connectivity.
unableToCreateNewObject=Unable to create new object: {0}
unableToStore=Unable to store {0}. unableToStore=Unable to store {0}.
unableToWrite=Unable to write {0} unableToWrite=Unable to write {0}
unencodeableFile=Unencodable file: {0} unencodeableFile=Unencodable file: {0}

View File

@ -137,13 +137,12 @@ public DirCache call() throws GitAPIException, NoFilepatternException {
if (filepatterns.contains(".")) //$NON-NLS-1$ if (filepatterns.contains(".")) //$NON-NLS-1$
addAll = true; addAll = true;
ObjectInserter inserter = repo.newObjectInserter(); try (ObjectInserter inserter = repo.newObjectInserter();
try { final TreeWalk tw = new TreeWalk(repo)) {
dc = repo.lockDirCache(); dc = repo.lockDirCache();
DirCacheIterator c; DirCacheIterator c;
DirCacheBuilder builder = dc.builder(); DirCacheBuilder builder = dc.builder();
final TreeWalk tw = new TreeWalk(repo);
tw.addTree(new DirCacheBuildIterator(builder)); tw.addTree(new DirCacheBuildIterator(builder));
if (workingTreeIterator == null) if (workingTreeIterator == null)
workingTreeIterator = new FileTreeIterator(repo); workingTreeIterator = new FileTreeIterator(repo);
@ -212,7 +211,6 @@ else if (!(path.equals(lastAddedFile))) {
throw new JGitInternalException( throw new JGitInternalException(
JGitText.get().exceptionCaughtDuringExecutionOfAddCommand, e); JGitText.get().exceptionCaughtDuringExecutionOfAddCommand, e);
} finally { } finally {
inserter.release();
if (dc != null) if (dc != null)
dc.unlock(); dc.unlock();
} }

View File

@ -83,11 +83,10 @@ protected AddNoteCommand(Repository repo) {
public Note call() throws GitAPIException { public Note call() throws GitAPIException {
checkCallable(); checkCallable();
RevWalk walk = new RevWalk(repo);
ObjectInserter inserter = repo.newObjectInserter();
NoteMap map = NoteMap.newEmptyMap(); NoteMap map = NoteMap.newEmptyMap();
RevCommit notesCommit = null; RevCommit notesCommit = null;
try { try (RevWalk walk = new RevWalk(repo);
ObjectInserter inserter = repo.newObjectInserter()) {
Ref ref = repo.getRef(notesRef); Ref ref = repo.getRef(notesRef);
// if we have a notes ref, use it // if we have a notes ref, use it
if (ref != null) { if (ref != null) {
@ -96,13 +95,10 @@ public Note call() throws GitAPIException {
} }
map.set(id, message, inserter); map.set(id, message, inserter);
commitNoteMap(walk, map, notesCommit, inserter, commitNoteMap(walk, map, notesCommit, inserter,
"Notes added by 'git notes add'"); "Notes added by 'git notes add'"); //$NON-NLS-1$
return map.getNote(id); return map.getNote(id);
} catch (IOException e) { } catch (IOException e) {
throw new JGitInternalException(e.getMessage(), e); throw new JGitInternalException(e.getMessage(), e);
} finally {
inserter.release();
walk.release();
} }
} }

View File

@ -365,13 +365,11 @@ public ArchiveCommand(Repository repo) {
private <T extends Closeable> OutputStream writeArchive(Format<T> fmt) { private <T extends Closeable> OutputStream writeArchive(Format<T> fmt) {
final String pfx = prefix == null ? "" : prefix; //$NON-NLS-1$ final String pfx = prefix == null ? "" : prefix; //$NON-NLS-1$
final TreeWalk walk = new TreeWalk(repo); try (final TreeWalk walk = new TreeWalk(repo)) {
try {
final T outa = fmt.createArchiveOutputStream(out, formatOptions); final T outa = fmt.createArchiveOutputStream(out, formatOptions);
try { try (final RevWalk rw = new RevWalk(walk.getObjectReader())) {
final MutableObjectId idBuf = new MutableObjectId(); final MutableObjectId idBuf = new MutableObjectId();
final ObjectReader reader = walk.getObjectReader(); final ObjectReader reader = walk.getObjectReader();
final RevWalk rw = new RevWalk(walk.getObjectReader());
walk.reset(rw.parseTree(tree)); walk.reset(rw.parseTree(tree));
if (!paths.isEmpty()) if (!paths.isEmpty())
@ -405,8 +403,6 @@ private <T extends Closeable> OutputStream writeArchive(Format<T> fmt) {
// TODO(jrn): Throw finer-grained errors. // TODO(jrn): Throw finer-grained errors.
throw new JGitInternalException( throw new JGitInternalException(
JGitText.get().exceptionCaughtDuringExecutionOfArchiveCommand, e); JGitText.get().exceptionCaughtDuringExecutionOfArchiveCommand, e);
} finally {
walk.release();
} }
} }

View File

@ -200,8 +200,7 @@ public BlameCommand reverse(AnyObjectId start, Collection<ObjectId> end)
*/ */
public BlameResult call() throws GitAPIException { public BlameResult call() throws GitAPIException {
checkCallable(); checkCallable();
BlameGenerator gen = new BlameGenerator(repo, path); try (BlameGenerator gen = new BlameGenerator(repo, path)) {
try {
if (diffAlgorithm != null) if (diffAlgorithm != null)
gen.setDiffAlgorithm(diffAlgorithm); gen.setDiffAlgorithm(diffAlgorithm);
if (textComparator != null) if (textComparator != null)
@ -231,8 +230,6 @@ else if (startCommit != null)
return gen.computeBlameResult(); return gen.computeBlameResult();
} catch (IOException e) { } catch (IOException e) {
throw new JGitInternalException(e.getMessage(), e); throw new JGitInternalException(e.getMessage(), e);
} finally {
gen.release();
} }
} }

View File

@ -208,16 +208,17 @@ public Ref call() throws GitAPIException, RefAlreadyExistsException,
} }
if (createBranch) { if (createBranch) {
Git git = new Git(repo); try (Git git = new Git(repo)) {
CreateBranchCommand command = git.branchCreate(); CreateBranchCommand command = git.branchCreate();
command.setName(name); command.setName(name);
if (startCommit != null) if (startCommit != null)
command.setStartPoint(startCommit); command.setStartPoint(startCommit);
else else
command.setStartPoint(startPoint); command.setStartPoint(startPoint);
if (upstreamMode != null) if (upstreamMode != null)
command.setUpstreamMode(upstreamMode); command.setUpstreamMode(upstreamMode);
command.call(); command.call();
}
} }
Ref headRef = repo.getRef(Constants.HEAD); Ref headRef = repo.getRef(Constants.HEAD);
@ -243,11 +244,14 @@ public Ref call() throws GitAPIException, RefAlreadyExistsException,
JGitText.get().refNotResolved, name)); JGitText.get().refNotResolved, name));
} }
RevWalk revWalk = new RevWalk(repo); RevCommit headCommit = null;
AnyObjectId headId = headRef.getObjectId(); RevCommit newCommit = null;
RevCommit headCommit = headId == null ? null : revWalk try (RevWalk revWalk = new RevWalk(repo)) {
.parseCommit(headId); AnyObjectId headId = headRef.getObjectId();
RevCommit newCommit = revWalk.parseCommit(branch); headCommit = headId == null ? null
: revWalk.parseCommit(headId);
newCommit = revWalk.parseCommit(branch);
}
RevTree headTree = headCommit == null ? null : headCommit.getTree(); RevTree headTree = headCommit == null ? null : headCommit.getTree();
DirCacheCheckout dco; DirCacheCheckout dco;
DirCache dc = repo.lockDirCache(); DirCache dc = repo.lockDirCache();
@ -376,26 +380,20 @@ public CheckoutCommand setAllPaths(boolean all) {
*/ */
protected CheckoutCommand checkoutPaths() throws IOException, protected CheckoutCommand checkoutPaths() throws IOException,
RefNotFoundException { RefNotFoundException {
RevWalk revWalk = new RevWalk(repo);
DirCache dc = repo.lockDirCache(); DirCache dc = repo.lockDirCache();
try { try (RevWalk revWalk = new RevWalk(repo);
TreeWalk treeWalk = new TreeWalk(revWalk.getObjectReader()); TreeWalk treeWalk = new TreeWalk(revWalk.getObjectReader())) {
treeWalk.setRecursive(true); treeWalk.setRecursive(true);
if (!checkoutAllPaths) if (!checkoutAllPaths)
treeWalk.setFilter(PathFilterGroup.createFromStrings(paths)); treeWalk.setFilter(PathFilterGroup.createFromStrings(paths));
try { if (isCheckoutIndex())
if (isCheckoutIndex()) checkoutPathsFromIndex(treeWalk, dc);
checkoutPathsFromIndex(treeWalk, dc); else {
else { RevCommit commit = revWalk.parseCommit(getStartPointObjectId());
RevCommit commit = revWalk.parseCommit(getStartPointObjectId()); checkoutPathsFromCommit(treeWalk, dc, commit);
checkoutPathsFromCommit(treeWalk, dc, commit);
}
} finally {
treeWalk.release();
} }
} finally { } finally {
dc.unlock(); dc.unlock();
revWalk.release();
} }
return this; return this;
} }
@ -675,7 +673,6 @@ public CheckoutResult getResult() {
private void checkOptions() { private void checkOptions() {
if (checkoutStage != null && !isCheckoutIndex()) if (checkoutStage != null && !isCheckoutIndex())
throw new IllegalStateException( throw new IllegalStateException(
"Checking out ours/theirs is only possible when checking out index, " JGitText.get().cannotCheckoutOursSwitchBranch);
+ "not when switching branches.");
} }
} }

View File

@ -123,8 +123,7 @@ public CherryPickResult call() throws GitAPIException, NoMessageException,
List<Ref> cherryPickedRefs = new LinkedList<Ref>(); List<Ref> cherryPickedRefs = new LinkedList<Ref>();
checkCallable(); checkCallable();
RevWalk revWalk = new RevWalk(repo); try (RevWalk revWalk = new RevWalk(repo)) {
try {
// get the head commit // get the head commit
Ref headRef = repo.getRef(Constants.HEAD); Ref headRef = repo.getRef(Constants.HEAD);
@ -153,7 +152,7 @@ public CherryPickResult call() throws GitAPIException, NoMessageException,
ResolveMerger merger = (ResolveMerger) strategy.newMerger(repo); ResolveMerger merger = (ResolveMerger) strategy.newMerger(repo);
merger.setWorkingTreeIterator(new FileTreeIterator(repo)); merger.setWorkingTreeIterator(new FileTreeIterator(repo));
merger.setBase(srcParent.getTree()); merger.setBase(srcParent.getTree());
merger.setCommitNames(new String[] { "BASE", ourName, merger.setCommitNames(new String[] { "BASE", ourName, //$NON-NLS-1$
cherryPickName }); cherryPickName });
if (merger.merge(newHead, srcCommit)) { if (merger.merge(newHead, srcCommit)) {
if (AnyObjectId.equals(newHead.getTree().getId(), merger if (AnyObjectId.equals(newHead.getTree().getId(), merger
@ -194,8 +193,6 @@ public CherryPickResult call() throws GitAPIException, NoMessageException,
MessageFormat.format( MessageFormat.format(
JGitText.get().exceptionCaughtDuringExecutionOfCherryPickCommand, JGitText.get().exceptionCaughtDuringExecutionOfCherryPickCommand,
e), e); e), e);
} finally {
revWalk.release();
} }
return new CherryPickResult(newHead, cherryPickedRefs); return new CherryPickResult(newHead, cherryPickedRefs);
} }

View File

@ -63,21 +63,21 @@ public enum CherryPickStatus {
OK { OK {
@Override @Override
public String toString() { public String toString() {
return "Ok"; return "Ok"; //$NON-NLS-1$
} }
}, },
/** */ /** */
FAILED { FAILED {
@Override @Override
public String toString() { public String toString() {
return "Failed"; return "Failed"; //$NON-NLS-1$
} }
}, },
/** */ /** */
CONFLICTING { CONFLICTING {
@Override @Override
public String toString() { public String toString() {
return "Conflicting"; return "Conflicting"; //$NON-NLS-1$
} }
} }
} }

View File

@ -322,12 +322,9 @@ private void addMergeConfig(Repository clonedRepo, Ref head)
private RevCommit parseCommit(final Repository clonedRepo, final Ref ref) private RevCommit parseCommit(final Repository clonedRepo, final Ref ref)
throws MissingObjectException, IncorrectObjectTypeException, throws MissingObjectException, IncorrectObjectTypeException,
IOException { IOException {
final RevWalk rw = new RevWalk(clonedRepo);
final RevCommit commit; final RevCommit commit;
try { try (final RevWalk rw = new RevWalk(clonedRepo)) {
commit = rw.parseCommit(ref.getObjectId()); commit = rw.parseCommit(ref.getObjectId());
} finally {
rw.release();
} }
return commit; return commit;
} }

View File

@ -165,9 +165,7 @@ public RevCommit call() throws GitAPIException, NoHeadException,
checkCallable(); checkCallable();
Collections.sort(only); Collections.sort(only);
RevWalk rw = new RevWalk(repo); try (RevWalk rw = new RevWalk(repo)) {
try {
RepositoryState state = repo.getRepositoryState(); RepositoryState state = repo.getRepositoryState();
if (!state.canCommit()) if (!state.canCommit())
throw new WrongRepositoryStateException(MessageFormat.format( throw new WrongRepositoryStateException(MessageFormat.format(
@ -181,8 +179,7 @@ public RevCommit call() throws GitAPIException, NoHeadException,
processOptions(state, rw); processOptions(state, rw);
if (all && !repo.isBare() && repo.getWorkTree() != null) { if (all && !repo.isBare() && repo.getWorkTree() != null) {
Git git = new Git(repo); try (Git git = new Git(repo)) {
try {
git.add() git.add()
.addFilepattern(".") //$NON-NLS-1$ .addFilepattern(".") //$NON-NLS-1$
.setUpdate(true).call(); .setUpdate(true).call();
@ -221,80 +218,74 @@ public RevCommit call() throws GitAPIException, NoHeadException,
// lock the index // lock the index
DirCache index = repo.lockDirCache(); DirCache index = repo.lockDirCache();
try { try (ObjectInserter odi = repo.newObjectInserter()) {
if (!only.isEmpty()) if (!only.isEmpty())
index = createTemporaryIndex(headId, index, rw); index = createTemporaryIndex(headId, index, rw);
ObjectInserter odi = repo.newObjectInserter(); // Write the index as tree to the object database. This may
try { // fail for example when the index contains unmerged paths
// Write the index as tree to the object database. This may // (unresolved conflicts)
// fail for example when the index contains unmerged paths ObjectId indexTreeId = index.writeTree(odi);
// (unresolved conflicts)
ObjectId indexTreeId = index.writeTree(odi);
if (insertChangeId) if (insertChangeId)
insertChangeId(indexTreeId); insertChangeId(indexTreeId);
// Create a Commit object, populate it and write it // Create a Commit object, populate it and write it
CommitBuilder commit = new CommitBuilder(); CommitBuilder commit = new CommitBuilder();
commit.setCommitter(committer); commit.setCommitter(committer);
commit.setAuthor(author); commit.setAuthor(author);
commit.setMessage(message); commit.setMessage(message);
commit.setParentIds(parents); commit.setParentIds(parents);
commit.setTreeId(indexTreeId); commit.setTreeId(indexTreeId);
ObjectId commitId = odi.insert(commit); ObjectId commitId = odi.insert(commit);
odi.flush(); odi.flush();
RevCommit revCommit = rw.parseCommit(commitId); RevCommit revCommit = rw.parseCommit(commitId);
RefUpdate ru = repo.updateRef(Constants.HEAD); RefUpdate ru = repo.updateRef(Constants.HEAD);
ru.setNewObjectId(commitId); ru.setNewObjectId(commitId);
if (reflogComment != null) { if (reflogComment != null) {
ru.setRefLogMessage(reflogComment, false); ru.setRefLogMessage(reflogComment, false);
} else { } else {
String prefix = amend ? "commit (amend): " //$NON-NLS-1$ String prefix = amend ? "commit (amend): " //$NON-NLS-1$
: parents.size() == 0 ? "commit (initial): " //$NON-NLS-1$ : parents.size() == 0 ? "commit (initial): " //$NON-NLS-1$
: "commit: "; //$NON-NLS-1$ : "commit: "; //$NON-NLS-1$
ru.setRefLogMessage( ru.setRefLogMessage(prefix + revCommit.getShortMessage(),
prefix + revCommit.getShortMessage(), false); false);
}
if (headId != null)
ru.setExpectedOldObjectId(headId);
else
ru.setExpectedOldObjectId(ObjectId.zeroId());
Result rc = ru.forceUpdate();
switch (rc) {
case NEW:
case FORCED:
case FAST_FORWARD: {
setCallable(false);
if (state == RepositoryState.MERGING_RESOLVED
|| isMergeDuringRebase(state)) {
// Commit was successful. Now delete the files
// used for merge commits
repo.writeMergeCommitMsg(null);
repo.writeMergeHeads(null);
} else if (state == RepositoryState.CHERRY_PICKING_RESOLVED) {
repo.writeMergeCommitMsg(null);
repo.writeCherryPickHead(null);
} else if (state == RepositoryState.REVERTING_RESOLVED) {
repo.writeMergeCommitMsg(null);
repo.writeRevertHead(null);
} }
if (headId != null) return revCommit;
ru.setExpectedOldObjectId(headId); }
else case REJECTED:
ru.setExpectedOldObjectId(ObjectId.zeroId()); case LOCK_FAILURE:
Result rc = ru.forceUpdate(); throw new ConcurrentRefUpdateException(
switch (rc) { JGitText.get().couldNotLockHEAD, ru.getRef(), rc);
case NEW: default:
case FORCED: throw new JGitInternalException(MessageFormat.format(
case FAST_FORWARD: { JGitText.get().updatingRefFailed, Constants.HEAD,
setCallable(false); commitId.toString(), rc));
if (state == RepositoryState.MERGING_RESOLVED
|| isMergeDuringRebase(state)) {
// Commit was successful. Now delete the files
// used for merge commits
repo.writeMergeCommitMsg(null);
repo.writeMergeHeads(null);
} else if (state == RepositoryState.CHERRY_PICKING_RESOLVED) {
repo.writeMergeCommitMsg(null);
repo.writeCherryPickHead(null);
} else if (state == RepositoryState.REVERTING_RESOLVED) {
repo.writeMergeCommitMsg(null);
repo.writeRevertHead(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 {
odi.release();
} }
} finally { } finally {
index.unlock(); index.unlock();
@ -304,8 +295,6 @@ public RevCommit call() throws GitAPIException, NoHeadException,
} catch (IOException e) { } catch (IOException e) {
throw new JGitInternalException( throw new JGitInternalException(
JGitText.get().exceptionCaughtDuringExecutionOfCommitCommand, e); JGitText.get().exceptionCaughtDuringExecutionOfCommitCommand, e);
} finally {
rw.dispose();
} }
} }
@ -338,114 +327,120 @@ private DirCache createTemporaryIndex(ObjectId headId, DirCache index,
onlyProcessed = new boolean[only.size()]; onlyProcessed = new boolean[only.size()];
boolean emptyCommit = true; boolean emptyCommit = true;
TreeWalk treeWalk = new TreeWalk(repo); try (TreeWalk treeWalk = new TreeWalk(repo)) {
int dcIdx = treeWalk.addTree(new DirCacheBuildIterator(existingBuilder)); int dcIdx = treeWalk
int fIdx = treeWalk.addTree(new FileTreeIterator(repo)); .addTree(new DirCacheBuildIterator(existingBuilder));
int hIdx = -1; int fIdx = treeWalk.addTree(new FileTreeIterator(repo));
if (headId != null) int hIdx = -1;
hIdx = treeWalk.addTree(rw.parseTree(headId)); if (headId != null)
treeWalk.setRecursive(true); hIdx = treeWalk.addTree(rw.parseTree(headId));
treeWalk.setRecursive(true);
String lastAddedFile = null; String lastAddedFile = null;
while (treeWalk.next()) { while (treeWalk.next()) {
String path = treeWalk.getPathString(); String path = treeWalk.getPathString();
// check if current entry's path matches a specified path // check if current entry's path matches a specified path
int pos = lookupOnly(path); int pos = lookupOnly(path);
CanonicalTreeParser hTree = null; CanonicalTreeParser hTree = null;
if (hIdx != -1) if (hIdx != -1)
hTree = treeWalk.getTree(hIdx, CanonicalTreeParser.class); hTree = treeWalk.getTree(hIdx, CanonicalTreeParser.class);
DirCacheIterator dcTree = treeWalk.getTree(dcIdx, DirCacheIterator dcTree = treeWalk.getTree(dcIdx,
DirCacheIterator.class); DirCacheIterator.class);
if (pos >= 0) { if (pos >= 0) {
// include entry in commit // include entry in commit
FileTreeIterator fTree = treeWalk.getTree(fIdx, FileTreeIterator fTree = treeWalk.getTree(fIdx,
FileTreeIterator.class); FileTreeIterator.class);
// check if entry refers to a tracked file // check if entry refers to a tracked file
boolean tracked = dcTree != null || hTree != null; boolean tracked = dcTree != null || hTree != null;
if (!tracked) if (!tracked)
break; break;
// for an unmerged path, DirCacheBuildIterator will yield 3 // for an unmerged path, DirCacheBuildIterator will yield 3
// entries, we only want to add one // entries, we only want to add one
if (path.equals(lastAddedFile)) if (path.equals(lastAddedFile))
continue; continue;
lastAddedFile = path; lastAddedFile = path;
if (fTree != null) { if (fTree != null) {
// create a new DirCacheEntry with data retrieved from disk // create a new DirCacheEntry with data retrieved from
final DirCacheEntry dcEntry = new DirCacheEntry(path); // disk
long entryLength = fTree.getEntryLength(); final DirCacheEntry dcEntry = new DirCacheEntry(path);
dcEntry.setLength(entryLength); long entryLength = fTree.getEntryLength();
dcEntry.setLastModified(fTree.getEntryLastModified()); dcEntry.setLength(entryLength);
dcEntry.setFileMode(fTree.getIndexFileMode(dcTree)); dcEntry.setLastModified(fTree.getEntryLastModified());
dcEntry.setFileMode(fTree.getIndexFileMode(dcTree));
boolean objectExists = (dcTree != null && fTree boolean objectExists = (dcTree != null
.idEqual(dcTree)) && fTree.idEqual(dcTree))
|| (hTree != null && fTree.idEqual(hTree)); || (hTree != null && fTree.idEqual(hTree));
if (objectExists) { if (objectExists) {
dcEntry.setObjectId(fTree.getEntryObjectId());
} else {
if (FileMode.GITLINK.equals(dcEntry.getFileMode()))
dcEntry.setObjectId(fTree.getEntryObjectId()); dcEntry.setObjectId(fTree.getEntryObjectId());
else { } else {
// insert object if (FileMode.GITLINK.equals(dcEntry.getFileMode()))
if (inserter == null) dcEntry.setObjectId(fTree.getEntryObjectId());
inserter = repo.newObjectInserter(); else {
long contentLength = fTree.getEntryContentLength(); // insert object
InputStream inputStream = fTree.openEntryStream(); if (inserter == null)
try { inserter = repo.newObjectInserter();
dcEntry.setObjectId(inserter.insert( long contentLength = fTree
Constants.OBJ_BLOB, contentLength, .getEntryContentLength();
inputStream)); InputStream inputStream = fTree
} finally { .openEntryStream();
inputStream.close(); try {
dcEntry.setObjectId(inserter.insert(
Constants.OBJ_BLOB, contentLength,
inputStream));
} finally {
inputStream.close();
}
} }
} }
// add to existing index
existingBuilder.add(dcEntry);
// add to temporary in-core index
tempBuilder.add(dcEntry);
if (emptyCommit
&& (hTree == null || !hTree.idEqual(fTree)
|| hTree.getEntryRawMode() != fTree
.getEntryRawMode()))
// this is a change
emptyCommit = false;
} else {
// if no file exists on disk, neither add it to
// index nor to temporary in-core index
if (emptyCommit && hTree != null)
// this is a change
emptyCommit = false;
} }
// add to existing index // keep track of processed path
existingBuilder.add(dcEntry); onlyProcessed[pos] = true;
// add to temporary in-core index
tempBuilder.add(dcEntry);
if (emptyCommit
&& (hTree == null || !hTree.idEqual(fTree) || hTree
.getEntryRawMode() != fTree
.getEntryRawMode()))
// this is a change
emptyCommit = false;
} else { } else {
// if no file exists on disk, neither add it to // add entries from HEAD for all other paths
// index nor to temporary in-core index if (hTree != null) {
// create a new DirCacheEntry with data retrieved from
// HEAD
final DirCacheEntry dcEntry = new DirCacheEntry(path);
dcEntry.setObjectId(hTree.getEntryObjectId());
dcEntry.setFileMode(hTree.getEntryFileMode());
if (emptyCommit && hTree != null) // add to temporary in-core index
// this is a change tempBuilder.add(dcEntry);
emptyCommit = false; }
// preserve existing entry in index
if (dcTree != null)
existingBuilder.add(dcTree.getDirCacheEntry());
} }
// keep track of processed path
onlyProcessed[pos] = true;
} else {
// add entries from HEAD for all other paths
if (hTree != null) {
// create a new DirCacheEntry with data retrieved from HEAD
final DirCacheEntry dcEntry = new DirCacheEntry(path);
dcEntry.setObjectId(hTree.getEntryObjectId());
dcEntry.setFileMode(hTree.getEntryFileMode());
// add to temporary in-core index
tempBuilder.add(dcEntry);
}
// preserve existing entry in index
if (dcTree != null)
existingBuilder.add(dcTree.getDirCacheEntry());
} }
} }

View File

@ -315,7 +315,7 @@ public int compare(Candidate o1, Candidate o2) {
throw new JGitInternalException(e.getMessage(), e); throw new JGitInternalException(e.getMessage(), e);
} finally { } finally {
setCallable(false); setCallable(false);
w.release(); w.close();
} }
} }
} }

View File

@ -124,11 +124,8 @@ public List<DiffEntry> call() throws GitAPIException {
if (head == null) if (head == null)
throw new NoHeadException(JGitText.get().cannotReadTree); throw new NoHeadException(JGitText.get().cannotReadTree);
CanonicalTreeParser p = new CanonicalTreeParser(); CanonicalTreeParser p = new CanonicalTreeParser();
ObjectReader reader = repo.newObjectReader(); try (ObjectReader reader = repo.newObjectReader()) {
try {
p.reset(reader, head); p.reset(reader, head);
} finally {
reader.release();
} }
oldTree = p; oldTree = p;
} }
@ -159,7 +156,7 @@ public List<DiffEntry> call() throws GitAPIException {
} catch (IOException e) { } catch (IOException e) {
throw new JGitInternalException(e.getMessage(), e); throw new JGitInternalException(e.getMessage(), e);
} finally { } finally {
diffFmt.release(); diffFmt.close();
} }
} }

View File

@ -185,11 +185,11 @@ public Map<ObjectId, String> call() throws GitAPIException {
} }
setCallable(false); setCallable(false);
walk.release();
return result; return result;
} catch (IOException e) { } catch (IOException e) {
walk.reset();
throw new JGitInternalException(e.getMessage(), e); throw new JGitInternalException(e.getMessage(), e);
} finally {
walk.close();
} }
} }

View File

@ -1088,7 +1088,9 @@ else if (!isInteractive() && walk.isMergedInto(headCommit, upstream)) {
rebaseState.createFile(HEAD_NAME, headName); rebaseState.createFile(HEAD_NAME, headName);
rebaseState.createFile(ONTO, upstreamCommit.name()); rebaseState.createFile(ONTO, upstreamCommit.name());
rebaseState.createFile(ONTO_NAME, upstreamCommitName); rebaseState.createFile(ONTO_NAME, upstreamCommitName);
rebaseState.createFile(INTERACTIVE, ""); //$NON-NLS-1$ if (isInteractive()) {
rebaseState.createFile(INTERACTIVE, ""); //$NON-NLS-1$
}
rebaseState.createFile(QUIET, ""); //$NON-NLS-1$ rebaseState.createFile(QUIET, ""); //$NON-NLS-1$
ArrayList<RebaseTodoLine> toDoSteps = new ArrayList<RebaseTodoLine>(); ArrayList<RebaseTodoLine> toDoSteps = new ArrayList<RebaseTodoLine>();

View File

@ -227,9 +227,9 @@ else if (repo.readSquashCommitMsg() != null)
setCallable(false); setCallable(false);
return result; return result;
} catch (IOException e) { } catch (IOException e) {
throw new JGitInternalException( throw new JGitInternalException(MessageFormat.format(
JGitText.get().exceptionCaughtDuringExecutionOfResetCommand, JGitText.get().exceptionCaughtDuringExecutionOfResetCommand,
e); e.getMessage()), e);
} }
} }

View File

@ -85,7 +85,7 @@ public static BlameResult create(BlameGenerator gen) throws IOException {
String path = gen.getResultPath(); String path = gen.getResultPath();
RawText contents = gen.getResultContents(); RawText contents = gen.getResultContents();
if (contents == null) { if (contents == null) {
gen.release(); gen.close();
return null; return null;
} }
return new BlameResult(gen, path, contents); return new BlameResult(gen, path, contents);
@ -239,7 +239,7 @@ public void computeAll() throws IOException {
while (gen.next()) while (gen.next())
loadFrom(gen); loadFrom(gen);
} finally { } finally {
gen.release(); gen.close();
generator = null; generator = null;
} }
} }
@ -265,7 +265,7 @@ public int computeNext() throws IOException {
lastLength = gen.getRegionLength(); lastLength = gen.getRegionLength();
return gen.getResultStart(); return gen.getResultStart();
} else { } else {
gen.release(); gen.close();
generator = null; generator = null;
return -1; return -1;
} }
@ -300,7 +300,7 @@ public void computeRange(int start, int end) throws IOException {
return; return;
if (!gen.next()) { if (!gen.next()) {
gen.release(); gen.close();
generator = null; generator = null;
return; return;
} }

View File

@ -173,7 +173,7 @@ protected OutputStream getOutputStream() {
*/ */
public void setRepository(Repository repository) { public void setRepository(Repository repository) {
if (reader != null) if (reader != null)
reader.release(); reader.close();
db = repository; db = repository;
reader = db.newObjectReader(); reader = db.newObjectReader();
@ -422,10 +422,11 @@ public List<DiffEntry> scan(AnyObjectId a, AnyObjectId b)
throws IOException { throws IOException {
assertHaveRepository(); assertHaveRepository();
RevWalk rw = new RevWalk(reader); try (RevWalk rw = new RevWalk(reader)) {
RevTree aTree = a != null ? rw.parseTree(a) : null; RevTree aTree = a != null ? rw.parseTree(a) : null;
RevTree bTree = b != null ? rw.parseTree(b) : null; RevTree bTree = b != null ? rw.parseTree(b) : null;
return scan(aTree, bTree); return scan(aTree, bTree);
}
} }
/** /**

View File

@ -961,9 +961,8 @@ private void registerIndexChangedListener(IndexChangedListener listener) {
* @throws IOException * @throws IOException
*/ */
private void updateSmudgedEntries() throws IOException { private void updateSmudgedEntries() throws IOException {
TreeWalk walk = new TreeWalk(repository);
List<String> paths = new ArrayList<String>(128); List<String> paths = new ArrayList<String>(128);
try { try (TreeWalk walk = new TreeWalk(repository)) {
for (int i = 0; i < entryCnt; i++) for (int i = 0; i < entryCnt; i++)
if (sortedEntries[i].isSmudged()) if (sortedEntries[i].isSmudged())
paths.add(sortedEntries[i].getPathString()); paths.add(sortedEntries[i].getPathString());
@ -989,8 +988,6 @@ private void updateSmudgedEntries() throws IOException {
entry.setLastModified(fIter.getEntryLastModified()); entry.setLastModified(fIter.getEntryLastModified());
} }
} }
} finally {
walk.release();
} }
} }
} }

View File

@ -1284,10 +1284,8 @@ private static void checkValidPath(CanonicalTreeParser t)
* @throws InvalidPathException * @throws InvalidPathException
* if the path is invalid * if the path is invalid
* @since 3.3 * @since 3.3
* @deprecated Use {@link SystemReader#checkPath(String)}.
*/ */
@Deprecated static void checkValidPath(String path) throws InvalidPathException {
public static void checkValidPath(String path) throws InvalidPathException {
try { try {
SystemReader.getInstance().checkPath(path); SystemReader.getInstance().checkPath(path);
} catch (CorruptObjectException e) { } catch (CorruptObjectException e) {

View File

@ -103,6 +103,7 @@ public static JGitText get() {
/***/ public String cannotBeRecursiveWhenTreesAreIncluded; /***/ public String cannotBeRecursiveWhenTreesAreIncluded;
/***/ public String cannotChangeActionOnComment; /***/ public String cannotChangeActionOnComment;
/***/ public String cannotChangeToComment; /***/ public String cannotChangeToComment;
/***/ public String cannotCheckoutOursSwitchBranch;
/***/ public String cannotCombineSquashWithNoff; /***/ public String cannotCombineSquashWithNoff;
/***/ public String cannotCombineTreeFilterWithRevFilter; /***/ public String cannotCombineTreeFilterWithRevFilter;
/***/ public String cannotCommitOnARepoWithState; /***/ public String cannotCommitOnARepoWithState;
@ -290,6 +291,7 @@ public static JGitText get() {
/***/ public String fileIsTooBigForThisConvenienceMethod; /***/ public String fileIsTooBigForThisConvenienceMethod;
/***/ public String fileIsTooLarge; /***/ public String fileIsTooLarge;
/***/ public String fileModeNotSetForPath; /***/ public String fileModeNotSetForPath;
/***/ public String findingGarbage;
/***/ public String flagIsDisposed; /***/ public String flagIsDisposed;
/***/ public String flagNotFromThis; /***/ public String flagNotFromThis;
/***/ public String flagsAlreadyCreated; /***/ public String flagsAlreadyCreated;
@ -317,6 +319,7 @@ public static JGitText get() {
/***/ public String initFailedBareRepoDifferentDirs; /***/ public String initFailedBareRepoDifferentDirs;
/***/ public String initFailedNonBareRepoSameDirs; /***/ public String initFailedNonBareRepoSameDirs;
/***/ public String inMemoryBufferLimitExceeded; /***/ public String inMemoryBufferLimitExceeded;
/***/ public String inputDidntMatchLength;
/***/ public String inputStreamMustSupportMark; /***/ public String inputStreamMustSupportMark;
/***/ public String integerValueOutOfRange; /***/ public String integerValueOutOfRange;
/***/ public String internalRevisionError; /***/ public String internalRevisionError;
@ -450,6 +453,7 @@ public static JGitText get() {
/***/ public String packFileInvalid; /***/ public String packFileInvalid;
/***/ public String packfileIsTruncated; /***/ public String packfileIsTruncated;
/***/ public String packfileIsTruncatedNoParam; /***/ public String packfileIsTruncatedNoParam;
/***/ public String packHandleIsStale;
/***/ public String packHasUnresolvedDeltas; /***/ public String packHasUnresolvedDeltas;
/***/ public String packingCancelledDuringObjectsWriting; /***/ public String packingCancelledDuringObjectsWriting;
/***/ public String packObjectCountMismatch; /***/ public String packObjectCountMismatch;
@ -561,6 +565,7 @@ public static JGitText get() {
/***/ public String submoduleExists; /***/ public String submoduleExists;
/***/ public String submodulesNotSupported; /***/ public String submodulesNotSupported;
/***/ public String submoduleParentRemoteUrlInvalid; /***/ public String submoduleParentRemoteUrlInvalid;
/***/ public String supportOnlyPackIndexVersion2;
/***/ public String symlinkCannotBeWrittenAsTheLinkTarget; /***/ public String symlinkCannotBeWrittenAsTheLinkTarget;
/***/ public String systemConfigFileInvalid; /***/ public String systemConfigFileInvalid;
/***/ public String tagAlreadyExists; /***/ public String tagAlreadyExists;
@ -595,6 +600,7 @@ public static JGitText get() {
/***/ public String truncatedHunkOldLinesMissing; /***/ public String truncatedHunkOldLinesMissing;
/***/ public String tSizeMustBeGreaterOrEqual1; /***/ public String tSizeMustBeGreaterOrEqual1;
/***/ public String unableToCheckConnectivity; /***/ public String unableToCheckConnectivity;
/***/ public String unableToCreateNewObject;
/***/ public String unableToStore; /***/ public String unableToStore;
/***/ public String unableToWrite; /***/ public String unableToWrite;
/***/ public String unencodeableFile; /***/ public String unencodeableFile;

View File

@ -46,7 +46,6 @@
package org.eclipse.jgit.internal.storage.dfs; package org.eclipse.jgit.internal.storage.dfs;
import java.io.IOException; import java.io.IOException;
import java.security.MessageDigest;
import java.util.zip.CRC32; import java.util.zip.CRC32;
import java.util.zip.DataFormatException; import java.util.zip.DataFormatException;
import java.util.zip.Inflater; import java.util.zip.Inflater;
@ -101,12 +100,9 @@ void crc32(CRC32 out, long pos, int cnt) {
out.update(block, ptr, cnt); out.update(block, ptr, cnt);
} }
void write(PackOutputStream out, long pos, int cnt, MessageDigest digest) void write(PackOutputStream out, long pos, int cnt)
throws IOException { throws IOException {
int ptr = (int) (pos - start); out.write(block, (int) (pos - start), cnt);
out.write(block, ptr, cnt);
if (digest != null)
digest.update(block, ptr, cnt);
} }
void check(Inflater inf, byte[] tmp, long pos, int cnt) void check(Inflater inf, byte[] tmp, long pos, int cnt)

View File

@ -135,6 +135,9 @@ public static DfsBlockCache getInstance() {
/** Maximum number of bytes the cache should hold. */ /** Maximum number of bytes the cache should hold. */
private final long maxBytes; private final long maxBytes;
/** Pack files smaller than this size can be copied through the cache. */
private final long maxStreamThroughCache;
/** /**
* Suggested block size to read from pack files in. * Suggested block size to read from pack files in.
* <p> * <p>
@ -191,6 +194,7 @@ else if (eb < 4)
eb = tableSize; eb = tableSize;
maxBytes = cfg.getBlockLimit(); maxBytes = cfg.getBlockLimit();
maxStreamThroughCache = (long) (maxBytes * cfg.getStreamRatio());
blockSize = cfg.getBlockSize(); blockSize = cfg.getBlockSize();
blockSizeShift = Integer.numberOfTrailingZeros(blockSize); blockSizeShift = Integer.numberOfTrailingZeros(blockSize);
@ -206,6 +210,10 @@ else if (eb < 4)
statMiss = new AtomicLong(); statMiss = new AtomicLong();
} }
boolean shouldCopyThroughCache(long length) {
return length <= maxStreamThroughCache;
}
/** @return total number of bytes in the cache. */ /** @return total number of bytes in the cache. */
public long getCurrentSize() { public long getCurrentSize() {
return liveBytes; return liveBytes;

View File

@ -47,7 +47,11 @@
import static org.eclipse.jgit.lib.ConfigConstants.CONFIG_DFS_SECTION; import static org.eclipse.jgit.lib.ConfigConstants.CONFIG_DFS_SECTION;
import static org.eclipse.jgit.lib.ConfigConstants.CONFIG_KEY_BLOCK_LIMIT; import static org.eclipse.jgit.lib.ConfigConstants.CONFIG_KEY_BLOCK_LIMIT;
import static org.eclipse.jgit.lib.ConfigConstants.CONFIG_KEY_BLOCK_SIZE; import static org.eclipse.jgit.lib.ConfigConstants.CONFIG_KEY_BLOCK_SIZE;
import static org.eclipse.jgit.lib.ConfigConstants.CONFIG_KEY_STREAM_RATIO;
import java.text.MessageFormat;
import org.eclipse.jgit.internal.JGitText;
import org.eclipse.jgit.lib.Config; import org.eclipse.jgit.lib.Config;
/** Configuration parameters for {@link DfsBlockCache}. */ /** Configuration parameters for {@link DfsBlockCache}. */
@ -59,13 +63,14 @@ public class DfsBlockCacheConfig {
public static final int MB = 1024 * KB; public static final int MB = 1024 * KB;
private long blockLimit; private long blockLimit;
private int blockSize; private int blockSize;
private double streamRatio;
/** Create a default configuration. */ /** Create a default configuration. */
public DfsBlockCacheConfig() { public DfsBlockCacheConfig() {
setBlockLimit(32 * MB); setBlockLimit(32 * MB);
setBlockSize(64 * KB); setBlockSize(64 * KB);
setStreamRatio(0.30);
} }
/** /**
@ -105,6 +110,27 @@ public DfsBlockCacheConfig setBlockSize(final int newSize) {
return this; return this;
} }
/**
* @return highest percentage of {@link #getBlockLimit()} a single pack can
* occupy while being copied by the pack reuse strategy. <b>Default
* is 0.30, or 30%</b>.
* @since 4.0
*/
public double getStreamRatio() {
return streamRatio;
}
/**
* @param ratio
* percentage of cache to occupy with a copied pack.
* @return {@code this}
* @since 4.0
*/
public DfsBlockCacheConfig setStreamRatio(double ratio) {
streamRatio = Math.max(0, Math.min(ratio, 1.0));
return this;
}
/** /**
* Update properties by setting fields from the configuration. * Update properties by setting fields from the configuration.
* <p> * <p>
@ -127,6 +153,22 @@ public DfsBlockCacheConfig fromConfig(final Config rc) {
CONFIG_DFS_SECTION, CONFIG_DFS_SECTION,
CONFIG_KEY_BLOCK_SIZE, CONFIG_KEY_BLOCK_SIZE,
getBlockSize())); getBlockSize()));
String v = rc.getString(
CONFIG_CORE_SECTION,
CONFIG_DFS_SECTION,
CONFIG_KEY_STREAM_RATIO);
if (v != null) {
try {
setStreamRatio(Double.parseDouble(v));
} catch (NumberFormatException e) {
throw new IllegalArgumentException(MessageFormat.format(
JGitText.get().enumValueNotSupported3,
CONFIG_CORE_SECTION,
CONFIG_DFS_SECTION,
CONFIG_KEY_STREAM_RATIO, v));
}
}
return this; return this;
} }
} }

View File

@ -78,8 +78,7 @@ public boolean hasObject(ObjectToPack obj, StoredObjectRepresentation rep) {
return ((DfsObjectRepresentation) rep).pack == pack; return ((DfsObjectRepresentation) rep).pack == pack;
} }
void copyAsIs(PackOutputStream out, boolean validate, DfsReader ctx) void copyAsIs(PackOutputStream out, DfsReader ctx) throws IOException {
throws IOException { pack.copyPackAsIs(out, ctx);
pack.copyPackAsIs(out, validate, ctx);
} }
} }

View File

@ -58,6 +58,7 @@
import java.util.Map; import java.util.Map;
import java.util.Set; import java.util.Set;
import org.eclipse.jgit.internal.JGitText;
import org.eclipse.jgit.internal.storage.dfs.DfsObjDatabase.PackSource; import org.eclipse.jgit.internal.storage.dfs.DfsObjDatabase.PackSource;
import org.eclipse.jgit.internal.storage.file.PackIndex; import org.eclipse.jgit.internal.storage.file.PackIndex;
import org.eclipse.jgit.internal.storage.pack.PackExt; import org.eclipse.jgit.internal.storage.pack.PackExt;
@ -188,7 +189,8 @@ public boolean pack(ProgressMonitor pm) throws IOException {
if (pm == null) if (pm == null)
pm = NullProgressMonitor.INSTANCE; pm = NullProgressMonitor.INSTANCE;
if (packConfig.getIndexVersion() != 2) if (packConfig.getIndexVersion() != 2)
throw new IllegalStateException("Only index version 2"); throw new IllegalStateException(
JGitText.get().supportOnlyPackIndexVersion2);
ctx = (DfsReader) objdb.newReader(); ctx = (DfsReader) objdb.newReader();
try { try {
@ -272,14 +274,11 @@ private void packHeads(ProgressMonitor pm) throws IOException {
if (allHeads.isEmpty()) if (allHeads.isEmpty())
return; return;
PackWriter pw = newPackWriter(); try (PackWriter pw = newPackWriter()) {
try {
pw.setTagTargets(tagTargets); pw.setTagTargets(tagTargets);
pw.preparePack(pm, allHeads, Collections.<ObjectId> emptySet()); pw.preparePack(pm, allHeads, Collections.<ObjectId> emptySet());
if (0 < pw.getObjectCount()) if (0 < pw.getObjectCount())
writePack(GC, pw, pm); writePack(GC, pw, pm);
} finally {
pw.release();
} }
} }
@ -287,15 +286,12 @@ private void packRest(ProgressMonitor pm) throws IOException {
if (nonHeads.isEmpty()) if (nonHeads.isEmpty())
return; return;
PackWriter pw = newPackWriter(); try (PackWriter pw = newPackWriter()) {
try {
for (PackWriter.ObjectIdSet packedObjs : newPackObj) for (PackWriter.ObjectIdSet packedObjs : newPackObj)
pw.excludeObjects(packedObjs); pw.excludeObjects(packedObjs);
pw.preparePack(pm, nonHeads, allHeads); pw.preparePack(pm, nonHeads, allHeads);
if (0 < pw.getObjectCount()) if (0 < pw.getObjectCount())
writePack(GC, pw, pm); writePack(GC, pw, pm);
} finally {
pw.release();
} }
} }
@ -307,12 +303,11 @@ private void packGarbage(ProgressMonitor pm) throws IOException {
cfg.setDeltaCompress(false); cfg.setDeltaCompress(false);
cfg.setBuildBitmaps(false); cfg.setBuildBitmaps(false);
PackWriter pw = new PackWriter(cfg, ctx); try (PackWriter pw = new PackWriter(cfg, ctx);
pw.setDeltaBaseAsOffset(true); RevWalk pool = new RevWalk(ctx)) {
pw.setReuseDeltaCommits(true); pw.setDeltaBaseAsOffset(true);
try { pw.setReuseDeltaCommits(true);
RevWalk pool = new RevWalk(ctx); pm.beginTask(JGitText.get().findingGarbage, objectsBefore());
pm.beginTask("Finding garbage", objectsBefore());
for (DfsPackFile oldPack : packsBefore) { for (DfsPackFile oldPack : packsBefore) {
PackIndex oldIdx = oldPack.getPackIndex(ctx); PackIndex oldIdx = oldPack.getPackIndex(ctx);
for (PackIndex.MutableEntry ent : oldIdx) { for (PackIndex.MutableEntry ent : oldIdx) {
@ -328,8 +323,6 @@ private void packGarbage(ProgressMonitor pm) throws IOException {
pm.endTask(); pm.endTask();
if (0 < pw.getObjectCount()) if (0 < pw.getObjectCount())
writePack(UNREACHABLE_GARBAGE, pw, pm); writePack(UNREACHABLE_GARBAGE, pw, pm);
} finally {
pw.release();
} }
} }

View File

@ -54,6 +54,7 @@
import java.io.EOFException; import java.io.EOFException;
import java.io.IOException; import java.io.IOException;
import java.io.InputStream; import java.io.InputStream;
import java.nio.ByteBuffer;
import java.nio.channels.Channels; import java.nio.channels.Channels;
import java.text.MessageFormat; import java.text.MessageFormat;
import java.util.Set; import java.util.Set;
@ -80,7 +81,6 @@
import org.eclipse.jgit.lib.ObjectId; import org.eclipse.jgit.lib.ObjectId;
import org.eclipse.jgit.lib.ObjectLoader; import org.eclipse.jgit.lib.ObjectLoader;
import org.eclipse.jgit.lib.Repository; import org.eclipse.jgit.lib.Repository;
import org.eclipse.jgit.util.IO;
import org.eclipse.jgit.util.LongList; import org.eclipse.jgit.util.LongList;
/** /**
@ -464,11 +464,73 @@ private byte[] decompress(long position, int sz, DfsReader ctx)
return dstbuf; return dstbuf;
} }
void copyPackAsIs(PackOutputStream out, boolean validate, DfsReader ctx) void copyPackAsIs(PackOutputStream out, DfsReader ctx)
throws IOException { throws IOException {
// Pin the first window, this ensures the length is accurate. // If the length hasn't been determined yet, pin to set it.
ctx.pin(this, 0); if (length == -1) {
ctx.copyPackAsIs(this, length, validate, out); ctx.pin(this, 0);
ctx.unpin();
}
if (cache.shouldCopyThroughCache(length))
copyPackThroughCache(out, ctx);
else
copyPackBypassCache(out, ctx);
}
private void copyPackThroughCache(PackOutputStream out, DfsReader ctx)
throws IOException {
long position = 12;
long remaining = length - (12 + 20);
while (0 < remaining) {
DfsBlock b = cache.getOrLoad(this, position, ctx);
int ptr = (int) (position - b.start);
int n = (int) Math.min(b.size() - ptr, remaining);
b.write(out, position, n);
position += n;
remaining -= n;
}
}
private long copyPackBypassCache(PackOutputStream out, DfsReader ctx)
throws IOException {
try (ReadableChannel rc = ctx.db.openFile(packDesc, PACK)) {
ByteBuffer buf = newCopyBuffer(out, rc);
if (ctx.getOptions().getStreamPackBufferSize() > 0)
rc.setReadAheadBytes(ctx.getOptions().getStreamPackBufferSize());
long position = 12;
long remaining = length - (12 + 20);
while (0 < remaining) {
DfsBlock b = cache.get(key, alignToBlock(position));
if (b != null) {
int ptr = (int) (position - b.start);
int n = (int) Math.min(b.size() - ptr, remaining);
b.write(out, position, n);
position += n;
remaining -= n;
rc.position(position);
continue;
}
buf.position(0);
int n = read(rc, buf);
if (n <= 0)
throw packfileIsTruncated();
else if (n > remaining)
n = (int) remaining;
out.write(buf.array(), 0, n);
position += n;
remaining -= n;
}
return position;
}
}
private ByteBuffer newCopyBuffer(PackOutputStream out, ReadableChannel rc) {
int bs = blockSize(rc);
byte[] copyBuf = out.getCopyBuffer();
if (bs > copyBuf.length)
copyBuf = new byte[bs];
return ByteBuffer.wrap(copyBuf, 0, bs);
} }
void copyAsIs(PackOutputStream out, DfsObjectToPack src, void copyAsIs(PackOutputStream out, DfsObjectToPack src,
@ -617,7 +679,7 @@ void copyAsIs(PackOutputStream out, DfsObjectToPack src,
// and we have it pinned. Write this out without copying. // and we have it pinned. Write this out without copying.
// //
out.writeHeader(src, inflatedLength); out.writeHeader(src, inflatedLength);
quickCopy.write(out, dataOffset, (int) dataLength, null); quickCopy.write(out, dataOffset, (int) dataLength);
} else if (dataLength <= buf.length) { } else if (dataLength <= buf.length) {
// Tiny optimization: Lots of objects are very small deltas or // Tiny optimization: Lots of objects are very small deltas or
@ -668,6 +730,12 @@ void setInvalid() {
invalid = true; invalid = true;
} }
private IOException packfileIsTruncated() {
invalid = true;
return new IOException(MessageFormat.format(
JGitText.get().packfileIsTruncated, getPackName()));
}
private void readFully(long position, byte[] dstbuf, int dstoff, int cnt, private void readFully(long position, byte[] dstbuf, int dstoff, int cnt,
DfsReader ctx) throws IOException { DfsReader ctx) throws IOException {
if (ctx.copy(this, position, dstbuf, dstoff, cnt) != cnt) if (ctx.copy(this, position, dstbuf, dstoff, cnt) != cnt)
@ -692,18 +760,8 @@ DfsBlock readOneBlock(long pos, DfsReader ctx)
ReadableChannel rc = ctx.db.openFile(packDesc, PACK); ReadableChannel rc = ctx.db.openFile(packDesc, PACK);
try { try {
// If the block alignment is not yet known, discover it. Prefer the int size = blockSize(rc);
// larger size from either the cache or the file itself. pos = (pos / size) * size;
int size = blockSize;
if (size == 0) {
size = rc.blockSize();
if (size <= 0)
size = cache.getBlockSize();
else if (size < cache.getBlockSize())
size = (cache.getBlockSize() / size) * size;
blockSize = size;
pos = (pos / size) * size;
}
// If the size of the file is not yet known, try to discover it. // If the size of the file is not yet known, try to discover it.
// Channels may choose to return -1 to indicate they don't // Channels may choose to return -1 to indicate they don't
@ -725,7 +783,7 @@ else if (size < cache.getBlockSize())
byte[] buf = new byte[size]; byte[] buf = new byte[size];
rc.position(pos); rc.position(pos);
int cnt = IO.read(rc, buf, 0, size); int cnt = read(rc, ByteBuffer.wrap(buf, 0, size));
if (cnt != size) { if (cnt != size) {
if (0 <= len) { if (0 <= len) {
throw new EOFException(MessageFormat.format( throw new EOFException(MessageFormat.format(
@ -754,6 +812,30 @@ else if (size < cache.getBlockSize())
} }
} }
private int blockSize(ReadableChannel rc) {
// If the block alignment is not yet known, discover it. Prefer the
// larger size from either the cache or the file itself.
int size = blockSize;
if (size == 0) {
size = rc.blockSize();
if (size <= 0)
size = cache.getBlockSize();
else if (size < cache.getBlockSize())
size = (cache.getBlockSize() / size) * size;
blockSize = size;
}
return size;
}
private static int read(ReadableChannel rc, ByteBuffer buf)
throws IOException {
int n;
do {
n = rc.read(buf);
} while (0 < n && buf.hasRemaining());
return buf.position();
}
ObjectLoader load(DfsReader ctx, long pos) ObjectLoader load(DfsReader ctx, long pos)
throws IOException { throws IOException {
try { try {

View File

@ -44,14 +44,10 @@
package org.eclipse.jgit.internal.storage.dfs; package org.eclipse.jgit.internal.storage.dfs;
import static org.eclipse.jgit.internal.storage.pack.PackExt.PACK;
import static org.eclipse.jgit.lib.Constants.OBJECT_ID_LENGTH; import static org.eclipse.jgit.lib.Constants.OBJECT_ID_LENGTH;
import java.io.IOException; import java.io.IOException;
import java.security.MessageDigest;
import java.text.MessageFormat;
import java.util.ArrayList; import java.util.ArrayList;
import java.util.Arrays;
import java.util.Collection; import java.util.Collection;
import java.util.Collections; import java.util.Collections;
import java.util.Comparator; import java.util.Comparator;
@ -65,7 +61,6 @@
import org.eclipse.jgit.errors.IncorrectObjectTypeException; import org.eclipse.jgit.errors.IncorrectObjectTypeException;
import org.eclipse.jgit.errors.MissingObjectException; import org.eclipse.jgit.errors.MissingObjectException;
import org.eclipse.jgit.errors.StoredObjectRepresentationNotAvailableException; import org.eclipse.jgit.errors.StoredObjectRepresentationNotAvailableException;
import org.eclipse.jgit.internal.JGitText;
import org.eclipse.jgit.internal.storage.file.BitmapIndexImpl; import org.eclipse.jgit.internal.storage.file.BitmapIndexImpl;
import org.eclipse.jgit.internal.storage.file.PackBitmapIndex; import org.eclipse.jgit.internal.storage.file.PackBitmapIndex;
import org.eclipse.jgit.internal.storage.file.PackIndex; import org.eclipse.jgit.internal.storage.file.PackIndex;
@ -81,7 +76,6 @@
import org.eclipse.jgit.lib.AsyncObjectSizeQueue; import org.eclipse.jgit.lib.AsyncObjectSizeQueue;
import org.eclipse.jgit.lib.BitmapIndex; import org.eclipse.jgit.lib.BitmapIndex;
import org.eclipse.jgit.lib.BitmapIndex.BitmapBuilder; import org.eclipse.jgit.lib.BitmapIndex.BitmapBuilder;
import org.eclipse.jgit.lib.Constants;
import org.eclipse.jgit.lib.InflaterCache; import org.eclipse.jgit.lib.InflaterCache;
import org.eclipse.jgit.lib.ObjectId; import org.eclipse.jgit.lib.ObjectId;
import org.eclipse.jgit.lib.ObjectLoader; import org.eclipse.jgit.lib.ObjectLoader;
@ -498,9 +492,9 @@ public void writeObjects(PackOutputStream out, List<ObjectToPack> list)
out.writeObject(otp); out.writeObject(otp);
} }
public void copyPackAsIs(PackOutputStream out, CachedPack pack, public void copyPackAsIs(PackOutputStream out, CachedPack pack)
boolean validate) throws IOException { throws IOException {
((DfsCachedPack) pack).copyAsIs(out, validate, this); ((DfsCachedPack) pack).copyAsIs(out, this);
} }
/** /**
@ -547,52 +541,6 @@ int copy(DfsPackFile pack, long position, byte[] dstbuf, int dstoff, int cnt)
return cnt - need; return cnt - need;
} }
void copyPackAsIs(DfsPackFile pack, long length, boolean validate,
PackOutputStream out) throws IOException {
MessageDigest md = null;
if (validate) {
md = Constants.newMessageDigest();
byte[] buf = out.getCopyBuffer();
pin(pack, 0);
if (block.copy(0, buf, 0, 12) != 12) {
pack.setInvalid();
throw new IOException(MessageFormat.format(
JGitText.get().packfileIsTruncated, pack.getPackName()));
}
md.update(buf, 0, 12);
}
long position = 12;
long remaining = length - (12 + 20);
while (0 < remaining) {
pin(pack, position);
int ptr = (int) (position - block.start);
int n = (int) Math.min(block.size() - ptr, remaining);
block.write(out, position, n, md);
position += n;
remaining -= n;
}
if (md != null) {
byte[] buf = new byte[20];
byte[] actHash = md.digest();
pin(pack, position);
if (block.copy(position, buf, 0, 20) != 20) {
pack.setInvalid();
throw new IOException(MessageFormat.format(
JGitText.get().packfileIsTruncated, pack.getPackName()));
}
if (!Arrays.equals(actHash, buf)) {
pack.setInvalid();
throw new IOException(MessageFormat.format(
JGitText.get().packfileCorruptionDetected,
pack.getPackDescription().getFileName(PACK)));
}
}
}
/** /**
* Inflate a region of the pack starting at {@code position}. * Inflate a region of the pack starting at {@code position}.
* *
@ -664,6 +612,10 @@ void pin(DfsPackFile pack, long position) throws IOException {
} }
} }
void unpin() {
block = null;
}
/** Release the current window cursor. */ /** Release the current window cursor. */
@Override @Override
public void release() { public void release() {

View File

@ -46,6 +46,7 @@
import static org.eclipse.jgit.lib.ConfigConstants.CONFIG_CORE_SECTION; import static org.eclipse.jgit.lib.ConfigConstants.CONFIG_CORE_SECTION;
import static org.eclipse.jgit.lib.ConfigConstants.CONFIG_DFS_SECTION; import static org.eclipse.jgit.lib.ConfigConstants.CONFIG_DFS_SECTION;
import static org.eclipse.jgit.lib.ConfigConstants.CONFIG_KEY_DELTA_BASE_CACHE_LIMIT; import static org.eclipse.jgit.lib.ConfigConstants.CONFIG_KEY_DELTA_BASE_CACHE_LIMIT;
import static org.eclipse.jgit.lib.ConfigConstants.CONFIG_KEY_STREAM_BUFFER;
import static org.eclipse.jgit.lib.ConfigConstants.CONFIG_KEY_STREAM_FILE_TRESHOLD; import static org.eclipse.jgit.lib.ConfigConstants.CONFIG_KEY_STREAM_FILE_TRESHOLD;
import org.eclipse.jgit.lib.Config; import org.eclipse.jgit.lib.Config;
@ -60,9 +61,10 @@ public class DfsReaderOptions {
public static final int MiB = 1024 * KiB; public static final int MiB = 1024 * KiB;
private int deltaBaseCacheLimit; private int deltaBaseCacheLimit;
private int streamFileThreshold; private int streamFileThreshold;
private int streamPackBufferSize;
/** Create a default reader configuration. */ /** Create a default reader configuration. */
public DfsReaderOptions() { public DfsReaderOptions() {
setDeltaBaseCacheLimit(10 * MiB); setDeltaBaseCacheLimit(10 * MiB);
@ -104,6 +106,27 @@ public DfsReaderOptions setStreamFileThreshold(final int newLimit) {
return this; return this;
} }
/**
* @return number of bytes to use for buffering when streaming a pack file
* during copying. If 0 the block size of the pack is used.
* @since 4.0
*/
public int getStreamPackBufferSize() {
return streamPackBufferSize;
}
/**
* @param bufsz
* new buffer size in bytes for buffers used when streaming pack
* files during copying.
* @return {@code this}
* @since 4.0
*/
public DfsReaderOptions setStreamPackBufferSize(int bufsz) {
streamPackBufferSize = Math.max(0, bufsz);
return this;
}
/** /**
* Update properties by setting fields from the configuration. * Update properties by setting fields from the configuration.
* <p> * <p>
@ -130,6 +153,12 @@ public DfsReaderOptions fromConfig(Config rc) {
sft = Math.min(sft, maxMem / 4); // don't use more than 1/4 of the heap 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 sft = Math.min(sft, Integer.MAX_VALUE); // cannot exceed array length
setStreamFileThreshold((int) sft); setStreamFileThreshold((int) sft);
setStreamPackBufferSize(rc.getInt(
CONFIG_CORE_SECTION,
CONFIG_DFS_SECTION,
CONFIG_KEY_STREAM_BUFFER,
getStreamPackBufferSize()));
return this; return this;
} }
} }

View File

@ -227,6 +227,10 @@ public long size() {
public int blockSize() { public int blockSize() {
return 0; return 0;
} }
public void setReadAheadBytes(int b) {
// Unnecessary on a byte array.
}
} }
private class MemRefDatabase extends DfsRefDatabase { private class MemRefDatabase extends DfsRefDatabase {

View File

@ -100,4 +100,33 @@ public interface ReadableChannel extends ReadableByteChannel {
* not need to be a power of 2. * not need to be a power of 2.
*/ */
public int blockSize(); public int blockSize();
/**
* Recommend the channel maintain a read-ahead buffer.
* <p>
* A read-ahead buffer of approximately {@code bufferSize} in bytes may be
* allocated and used by the channel to smooth out latency for read.
* <p>
* Callers can continue to read in smaller than {@code bufferSize} chunks.
* With read-ahead buffering enabled read latency may fluctuate in a pattern
* of one slower read followed by {@code (bufferSize / readSize) - 1} fast
* reads satisfied by the read-ahead buffer. When summed up overall time to
* read the same contiguous range should be lower than if read-ahead was not
* enabled, as the implementation can combine reads to increase throughput.
* <p>
* To avoid unnecessary IO callers should only enable read-ahead if the
* majority of the channel will be accessed in order.
* <p>
* Implementations may chose to read-ahead using asynchronous APIs or
* background threads, or may simply aggregate reads using a buffer.
* <p>
* This read ahead stays in effect until the channel is closed or the buffer
* size is set to 0.
*
* @param bufferSize
* requested size of the read ahead buffer, in bytes.
* @throws IOException
* if the read ahead cannot be adjusted.
*/
public void setReadAheadBytes(int bufferSize) throws IOException;
} }

View File

@ -46,7 +46,6 @@
package org.eclipse.jgit.internal.storage.file; package org.eclipse.jgit.internal.storage.file;
import java.io.IOException; import java.io.IOException;
import java.security.MessageDigest;
import java.util.zip.CRC32; import java.util.zip.CRC32;
import java.util.zip.DataFormatException; import java.util.zip.DataFormatException;
import java.util.zip.Inflater; import java.util.zip.Inflater;
@ -84,12 +83,10 @@ void crc32(CRC32 out, long pos, int cnt) {
} }
@Override @Override
void write(PackOutputStream out, long pos, int cnt, MessageDigest digest) void write(PackOutputStream out, long pos, int cnt)
throws IOException { throws IOException {
int ptr = (int) (pos - start); int ptr = (int) (pos - start);
out.write(array, ptr, cnt); out.write(array, ptr, cnt);
if (digest != null)
digest.update(array, ptr, cnt);
} }
void check(Inflater inf, byte[] tmp, long pos, int cnt) void check(Inflater inf, byte[] tmp, long pos, int cnt)

View File

@ -47,7 +47,6 @@
import java.io.IOException; import java.io.IOException;
import java.nio.ByteBuffer; import java.nio.ByteBuffer;
import java.security.MessageDigest;
import java.util.zip.DataFormatException; import java.util.zip.DataFormatException;
import java.util.zip.Inflater; import java.util.zip.Inflater;
@ -76,7 +75,7 @@ protected int copy(final int p, final byte[] b, final int o, int n) {
} }
@Override @Override
void write(PackOutputStream out, long pos, int cnt, MessageDigest digest) void write(PackOutputStream out, long pos, int cnt)
throws IOException { throws IOException {
final ByteBuffer s = buffer.slice(); final ByteBuffer s = buffer.slice();
s.position((int) (pos - start)); s.position((int) (pos - start));
@ -86,8 +85,6 @@ void write(PackOutputStream out, long pos, int cnt, MessageDigest digest)
int n = Math.min(cnt, buf.length); int n = Math.min(cnt, buf.length);
s.get(buf, 0, n); s.get(buf, 0, n);
out.write(buf, 0, n); out.write(buf, 0, n);
if (digest != null)
digest.update(buf, 0, n);
cnt -= n; cnt -= n;
} }
} }

View File

@ -45,7 +45,6 @@
package org.eclipse.jgit.internal.storage.file; package org.eclipse.jgit.internal.storage.file;
import java.io.IOException; import java.io.IOException;
import java.security.MessageDigest;
import java.util.zip.DataFormatException; import java.util.zip.DataFormatException;
import java.util.zip.Inflater; import java.util.zip.Inflater;
@ -121,8 +120,8 @@ final int copy(long pos, byte[] dstbuf, int dstoff, int cnt) {
*/ */
protected abstract int copy(int pos, byte[] dstbuf, int dstoff, int cnt); protected abstract int copy(int pos, byte[] dstbuf, int dstoff, int cnt);
abstract void write(PackOutputStream out, long pos, int cnt, abstract void write(PackOutputStream out, long pos, int cnt)
MessageDigest md) throws IOException; throws IOException;
final int setInput(long pos, Inflater inf) throws DataFormatException { final int setInput(long pos, Inflater inf) throws DataFormatException {
return setInput((int) (pos - start), inf); return setInput((int) (pos - start), inf);

View File

@ -618,22 +618,19 @@ private Map<String, Ref> getAllRefs() throws IOException {
*/ */
private Set<ObjectId> listNonHEADIndexObjects() private Set<ObjectId> listNonHEADIndexObjects()
throws CorruptObjectException, IOException { throws CorruptObjectException, IOException {
RevWalk revWalk = null;
try { try {
if (repo.getIndexFile() == null) if (repo.getIndexFile() == null)
return Collections.emptySet(); return Collections.emptySet();
} catch (NoWorkTreeException e) { } catch (NoWorkTreeException e) {
return Collections.emptySet(); return Collections.emptySet();
} }
TreeWalk treeWalk = new TreeWalk(repo); try (TreeWalk treeWalk = new TreeWalk(repo)) {
try {
treeWalk.addTree(new DirCacheIterator(repo.readDirCache())); treeWalk.addTree(new DirCacheIterator(repo.readDirCache()));
ObjectId headID = repo.resolve(Constants.HEAD); ObjectId headID = repo.resolve(Constants.HEAD);
if (headID != null) { if (headID != null) {
revWalk = new RevWalk(repo); try (RevWalk revWalk = new RevWalk(repo)) {
treeWalk.addTree(revWalk.parseTree(headID)); treeWalk.addTree(revWalk.parseTree(headID));
revWalk.dispose(); }
revWalk = null;
} }
treeWalk.setFilter(TreeFilter.ANY_DIFF); treeWalk.setFilter(TreeFilter.ANY_DIFF);
@ -662,10 +659,6 @@ private Set<ObjectId> listNonHEADIndexObjects()
} }
} }
return ret; return ret;
} finally {
if (revWalk != null)
revWalk.dispose();
treeWalk.release();
} }
} }
@ -689,8 +682,9 @@ public int compare(PackExt o1, PackExt o2) {
} }
}); });
PackWriter pw = new PackWriter((pconfig == null) ? new PackConfig(repo) : pconfig, repo.newObjectReader()); try (PackWriter pw = new PackWriter(
try { (pconfig == null) ? new PackConfig(repo) : pconfig,
repo.newObjectReader())) {
// prepare the PackWriter // prepare the PackWriter
pw.setDeltaBaseAsOffset(true); pw.setDeltaBaseAsOffset(true);
pw.setReuseDeltaCommits(false); pw.setReuseDeltaCommits(false);
@ -810,7 +804,6 @@ public int compare(PackExt o1, PackExt o2) {
} }
return repo.getObjectDatabase().openPack(realPack); return repo.getObjectDatabase().openPack(realPack);
} finally { } finally {
pw.release();
if (tmpPack != null && tmpPack.exists()) if (tmpPack != null && tmpPack.exists())
tmpPack.delete(); tmpPack.delete();
for (File tmpExt : tmpExts.values()) { for (File tmpExt : tmpExts.values()) {

View File

@ -79,10 +79,10 @@ public long getObjectCount() throws IOException {
return cnt; return cnt;
} }
void copyAsIs(PackOutputStream out, boolean validate, WindowCursor wc) void copyAsIs(PackOutputStream out, WindowCursor wc)
throws IOException { throws IOException {
for (PackFile pack : getPacks()) for (PackFile pack : getPacks())
pack.copyPackAsIs(out, validate, wc); pack.copyPackAsIs(out, wc);
} }
@Override @Override

View File

@ -114,6 +114,8 @@ public class ObjectDirectory extends FileObjectDatabase {
/** Maximum number of candidates offered as resolutions of abbreviation. */ /** Maximum number of candidates offered as resolutions of abbreviation. */
private static final int RESOLVE_ABBREV_LIMIT = 256; private static final int RESOLVE_ABBREV_LIMIT = 256;
private static final String STALE_FILE_HANDLE_MSG = "stale file handle"; //$NON-NLS-1$
private final Config config; private final Config config;
private final File objects; private final File objects;
@ -554,22 +556,35 @@ void selectObjectRepresentation(PackWriter packer, ObjectToPack otp,
} }
private void handlePackError(IOException e, PackFile p) { private void handlePackError(IOException e, PackFile p) {
String tmpl; String warnTmpl = null;
if ((e instanceof CorruptObjectException) if ((e instanceof CorruptObjectException)
|| (e instanceof PackInvalidException)) { || (e instanceof PackInvalidException)) {
tmpl = JGitText.get().corruptPack; warnTmpl = JGitText.get().corruptPack;
// Assume the pack is corrupted, and remove it from the list. // Assume the pack is corrupted, and remove it from the list.
removePack(p); removePack(p);
} else if (e instanceof FileNotFoundException) { } else if (e instanceof FileNotFoundException) {
tmpl = JGitText.get().packWasDeleted; warnTmpl = JGitText.get().packWasDeleted;
removePack(p); removePack(p);
} else if (e.getMessage() != null
&& e.getMessage().toLowerCase().contains(STALE_FILE_HANDLE_MSG)) {
warnTmpl = JGitText.get().packHandleIsStale;
removePack(p);
}
if (warnTmpl != null) {
if (LOG.isDebugEnabled()) {
LOG.debug(MessageFormat.format(warnTmpl,
p.getPackFile().getAbsolutePath()), e);
} else {
LOG.warn(MessageFormat.format(warnTmpl,
p.getPackFile().getAbsolutePath()));
}
} else { } else {
tmpl = JGitText.get().exceptionWhileReadingPack;
// Don't remove the pack from the list, as the error may be // Don't remove the pack from the list, as the error may be
// transient. // transient.
LOG.error(MessageFormat.format(
JGitText.get().exceptionWhileReadingPack, p.getPackFile()
.getAbsolutePath()), e);
} }
LOG.error(MessageFormat.format(tmpl,
p.getPackFile().getAbsolutePath()), e);
} }
@Override @Override

View File

@ -55,10 +55,12 @@
import java.nio.channels.Channels; import java.nio.channels.Channels;
import java.security.DigestOutputStream; import java.security.DigestOutputStream;
import java.security.MessageDigest; import java.security.MessageDigest;
import java.text.MessageFormat;
import java.util.zip.Deflater; import java.util.zip.Deflater;
import java.util.zip.DeflaterOutputStream; import java.util.zip.DeflaterOutputStream;
import org.eclipse.jgit.errors.ObjectWritingException; import org.eclipse.jgit.errors.ObjectWritingException;
import org.eclipse.jgit.internal.JGitText;
import org.eclipse.jgit.lib.Config; import org.eclipse.jgit.lib.Config;
import org.eclipse.jgit.lib.Constants; import org.eclipse.jgit.lib.Constants;
import org.eclipse.jgit.lib.ObjectId; import org.eclipse.jgit.lib.ObjectId;
@ -123,7 +125,8 @@ private ObjectId insertOneObject(final File tmp, final ObjectId id)
} }
final File dst = db.fileFor(id); final File dst = db.fileFor(id);
throw new ObjectWritingException("Unable to create new object: " + dst); throw new ObjectWritingException(MessageFormat
.format(JGitText.get().unableToCreateNewObject, dst));
} }
@Override @Override
@ -242,7 +245,7 @@ DeflaterOutputStream compress(final OutputStream out) {
} }
private static EOFException shortInput(long missing) { private static EOFException shortInput(long missing) {
return new EOFException("Input did not match supplied length. " return new EOFException(MessageFormat.format(
+ missing + " bytes are missing."); JGitText.get().inputDidntMatchLength, Long.valueOf(missing)));
} }
} }

View File

@ -344,11 +344,11 @@ private final byte[] decompress(final long position, final int sz,
return dstbuf; return dstbuf;
} }
void copyPackAsIs(PackOutputStream out, boolean validate, WindowCursor curs) void copyPackAsIs(PackOutputStream out, WindowCursor curs)
throws IOException { throws IOException {
// Pin the first window, this ensures the length is accurate. // Pin the first window, this ensures the length is accurate.
curs.pin(this, 0); curs.pin(this, 0);
curs.copyPackAsIs(this, length, validate, out); curs.copyPackAsIs(this, length, out);
} }
final void copyAsIs(PackOutputStream out, LocalObjectToPack src, final void copyAsIs(PackOutputStream out, LocalObjectToPack src,
@ -362,6 +362,7 @@ final void copyAsIs(PackOutputStream out, LocalObjectToPack src,
} }
} }
@SuppressWarnings("null")
private void copyAsIs2(PackOutputStream out, LocalObjectToPack src, private void copyAsIs2(PackOutputStream out, LocalObjectToPack src,
boolean validate, WindowCursor curs) throws IOException, boolean validate, WindowCursor curs) throws IOException,
StoredObjectRepresentationNotAvailableException { StoredObjectRepresentationNotAvailableException {
@ -501,7 +502,7 @@ private void copyAsIs2(PackOutputStream out, LocalObjectToPack src,
// and we have it pinned. Write this out without copying. // and we have it pinned. Write this out without copying.
// //
out.writeHeader(src, inflatedLength); out.writeHeader(src, inflatedLength);
quickCopy.write(out, dataOffset, (int) dataLength, null); quickCopy.write(out, dataOffset, (int) dataLength);
} else if (dataLength <= buf.length) { } else if (dataLength <= buf.length) {
// Tiny optimization: Lots of objects are very small deltas or // Tiny optimization: Lots of objects are very small deltas or
@ -703,6 +704,7 @@ private void onOpenPack() throws IOException {
, getPackFile())); , getPackFile()));
} }
@SuppressWarnings("null")
ObjectLoader load(final WindowCursor curs, long pos) ObjectLoader load(final WindowCursor curs, long pos)
throws IOException, LargeObjectException { throws IOException, LargeObjectException {
try { try {

View File

@ -232,7 +232,7 @@ public long findCRC32(AnyObjectId objId) throws MissingObjectException {
final int levelOne = objId.getFirstByte(); final int levelOne = objId.getFirstByte();
final int levelTwo = binarySearchLevelTwo(objId, levelOne); final int levelTwo = binarySearchLevelTwo(objId, levelOne);
if (levelTwo == -1) if (levelTwo == -1)
throw new MissingObjectException(objId.copy(), "unknown"); throw new MissingObjectException(objId.copy(), "unknown"); //$NON-NLS-1$
return NB.decodeUInt32(crc32[levelOne], levelTwo << 2); return NB.decodeUInt32(crc32[levelOne], levelTwo << 2);
} }

View File

@ -45,9 +45,6 @@
package org.eclipse.jgit.internal.storage.file; package org.eclipse.jgit.internal.storage.file;
import java.io.IOException; import java.io.IOException;
import java.security.MessageDigest;
import java.text.MessageFormat;
import java.util.Arrays;
import java.util.Collection; import java.util.Collection;
import java.util.Collections; import java.util.Collections;
import java.util.HashSet; import java.util.HashSet;
@ -59,7 +56,6 @@
import org.eclipse.jgit.errors.IncorrectObjectTypeException; import org.eclipse.jgit.errors.IncorrectObjectTypeException;
import org.eclipse.jgit.errors.MissingObjectException; import org.eclipse.jgit.errors.MissingObjectException;
import org.eclipse.jgit.errors.StoredObjectRepresentationNotAvailableException; import org.eclipse.jgit.errors.StoredObjectRepresentationNotAvailableException;
import org.eclipse.jgit.internal.JGitText;
import org.eclipse.jgit.internal.storage.pack.CachedPack; import org.eclipse.jgit.internal.storage.pack.CachedPack;
import org.eclipse.jgit.internal.storage.pack.ObjectReuseAsIs; import org.eclipse.jgit.internal.storage.pack.ObjectReuseAsIs;
import org.eclipse.jgit.internal.storage.pack.ObjectToPack; import org.eclipse.jgit.internal.storage.pack.ObjectToPack;
@ -232,27 +228,13 @@ int copy(final PackFile pack, long position, final byte[] dstbuf,
return cnt - need; return cnt - need;
} }
public void copyPackAsIs(PackOutputStream out, CachedPack pack, public void copyPackAsIs(PackOutputStream out, CachedPack pack)
boolean validate) throws IOException { throws IOException {
((LocalCachedPack) pack).copyAsIs(out, validate, this); ((LocalCachedPack) pack).copyAsIs(out, this);
} }
void copyPackAsIs(final PackFile pack, final long length, boolean validate, void copyPackAsIs(final PackFile pack, final long length,
final PackOutputStream out) throws IOException { final PackOutputStream out) throws IOException {
MessageDigest md = null;
if (validate) {
md = Constants.newMessageDigest();
byte[] buf = out.getCopyBuffer();
pin(pack, 0);
if (window.copy(0, buf, 0, 12) != 12) {
pack.setInvalid();
throw new IOException(MessageFormat.format(
JGitText.get().packfileIsTruncated, pack.getPackFile()
.getPath()));
}
md.update(buf, 0, 12);
}
long position = 12; long position = 12;
long remaining = length - (12 + 20); long remaining = length - (12 + 20);
while (0 < remaining) { while (0 < remaining) {
@ -260,29 +242,10 @@ void copyPackAsIs(final PackFile pack, final long length, boolean validate,
int ptr = (int) (position - window.start); int ptr = (int) (position - window.start);
int n = (int) Math.min(window.size() - ptr, remaining); int n = (int) Math.min(window.size() - ptr, remaining);
window.write(out, position, n, md); window.write(out, position, n);
position += n; position += n;
remaining -= n; remaining -= n;
} }
if (md != null) {
byte[] buf = new byte[20];
byte[] actHash = md.digest();
pin(pack, position);
if (window.copy(position, buf, 0, 20) != 20) {
pack.setInvalid();
throw new IOException(MessageFormat.format(
JGitText.get().packfileIsTruncated, pack.getPackFile()
.getPath()));
}
if (!Arrays.equals(actHash, buf)) {
pack.setInvalid();
throw new IOException(MessageFormat.format(
JGitText.get().packfileCorruptionDetected, pack
.getPackFile().getPath()));
}
}
} }
/** /**

View File

@ -209,16 +209,11 @@ public void copyObjectAsIs(PackOutputStream out, ObjectToPack otp,
* stream to append the pack onto. * stream to append the pack onto.
* @param pack * @param pack
* the cached pack to send. * the cached pack to send.
* @param validate
* if true the representation must be validated and not be
* corrupt before being reused. If false, validation may be
* skipped as it will be performed elsewhere in the processing
* pipeline.
* @throws IOException * @throws IOException
* the pack cannot be read, or stream did not accept a write. * the pack cannot be read, or stream did not accept a write.
*/ */
public abstract void copyPackAsIs(PackOutputStream out, CachedPack pack, public abstract void copyPackAsIs(PackOutputStream out, CachedPack pack)
boolean validate) throws IOException; throws IOException;
/** /**
* Obtain the available cached packs that match the bitmap and update * Obtain the available cached packs that match the bitmap and update

View File

@ -1048,7 +1048,7 @@ public void writePack(ProgressMonitor compressMonitor,
stats.reusedObjects += pack.getObjectCount(); stats.reusedObjects += pack.getObjectCount();
stats.reusedDeltas += deltaCnt; stats.reusedDeltas += deltaCnt;
stats.totalDeltas += deltaCnt; stats.totalDeltas += deltaCnt;
reuseSupport.copyPackAsIs(out, pack, reuseValidate); reuseSupport.copyPackAsIs(out, pack);
} }
writeChecksum(out); writeChecksum(out);
out.flush(); out.flush();
@ -1866,7 +1866,7 @@ private void findObjectsToPackUsingBitmaps(
false); false);
BitmapBuilder needBitmap = wantBitmap.andNot(haveBitmap); BitmapBuilder needBitmap = wantBitmap.andNot(haveBitmap);
if (useCachedPacks && reuseSupport != null if (useCachedPacks && reuseSupport != null && !reuseValidate
&& (excludeInPacks == null || excludeInPacks.length == 0)) && (excludeInPacks == null || excludeInPacks.length == 0))
cachedPacks.addAll( cachedPacks.addAll(
reuseSupport.getCachedPacksAndUpdate(needBitmap)); reuseSupport.getCachedPacksAndUpdate(needBitmap));

View File

@ -104,11 +104,8 @@ public BlobBasedConfig(Config base, Repository db, AnyObjectId objectId)
private static byte[] read(Repository db, AnyObjectId blobId) private static byte[] read(Repository db, AnyObjectId blobId)
throws MissingObjectException, IncorrectObjectTypeException, throws MissingObjectException, IncorrectObjectTypeException,
IOException { IOException {
ObjectReader or = db.newObjectReader(); try (ObjectReader or = db.newObjectReader()) {
try {
return read(or, blobId); return read(or, blobId);
} finally {
or.release();
} }
} }
@ -146,15 +143,12 @@ public BlobBasedConfig(Config base, Repository db, AnyObjectId treeish,
private static byte[] read(Repository db, AnyObjectId treeish, String path) private static byte[] read(Repository db, AnyObjectId treeish, String path)
throws MissingObjectException, IncorrectObjectTypeException, throws MissingObjectException, IncorrectObjectTypeException,
IOException { IOException {
ObjectReader or = db.newObjectReader(); try (ObjectReader or = db.newObjectReader()) {
try {
TreeWalk tree = TreeWalk.forPath(or, path, asTree(or, treeish)); TreeWalk tree = TreeWalk.forPath(or, path, asTree(or, treeish));
if (tree == null) if (tree == null)
throw new FileNotFoundException(MessageFormat.format(JGitText throw new FileNotFoundException(MessageFormat.format(JGitText
.get().entryNotFoundByPath, path)); .get().entryNotFoundByPath, path));
return read(or, tree.getObjectId(0)); return read(or, tree.getObjectId(0));
} finally {
or.release();
} }
} }
@ -168,6 +162,8 @@ private static AnyObjectId asTree(ObjectReader or, AnyObjectId treeish)
&& ((RevCommit) treeish).getTree() != null) && ((RevCommit) treeish).getTree() != null)
return ((RevCommit) treeish).getTree(); return ((RevCommit) treeish).getTree();
return new RevWalk(or).parseTree(treeish).getId(); try (RevWalk rw = new RevWalk(or)) {
return rw.parseTree(treeish).getId();
}
} }
} }

View File

@ -299,4 +299,16 @@ public class ConfigConstants {
* @since 3.3 * @since 3.3
*/ */
public static final String CONFIG_KEY_PRUNE = "prune"; public static final String CONFIG_KEY_PRUNE = "prune";
/**
* The "streamBuffer" key
* @since 4.0
*/
public static final String CONFIG_KEY_STREAM_BUFFER = "streamBuffer";
/**
* The "streamRatio" key
* @since 4.0
*/
public static final String CONFIG_KEY_STREAM_RATIO = "streamRatio";
} }

View File

@ -104,6 +104,8 @@ public class ObjectChecker {
private final MutableInteger ptrout = new MutableInteger(); private final MutableInteger ptrout = new MutableInteger();
private boolean allowZeroMode; private boolean allowZeroMode;
private boolean allowInvalidPersonIdent;
private boolean windows; private boolean windows;
private boolean macosx; private boolean macosx;
@ -124,6 +126,22 @@ public ObjectChecker setAllowLeadingZeroFileMode(boolean allow) {
return this; return this;
} }
/**
* Enable accepting invalid author, committer and tagger identities.
* <p>
* Some broken Git versions/libraries allowed users to create commits and
* tags with invalid formatting between the name, email and timestamp.
*
* @param allow
* if true accept invalid person identity strings.
* @return {@code this}.
* @since 4.0
*/
public ObjectChecker setAllowInvalidPersonIdent(boolean allow) {
allowInvalidPersonIdent = allow;
return this;
}
/** /**
* Restrict trees to only names legal on Windows platforms. * Restrict trees to only names legal on Windows platforms.
* <p> * <p>
@ -198,6 +216,9 @@ private int id(final byte[] raw, final int ptr) {
} }
private int personIdent(final byte[] raw, int ptr) { private int personIdent(final byte[] raw, int ptr) {
if (allowInvalidPersonIdent)
return nextLF(raw, ptr) - 1;
final int emailB = nextLF(raw, ptr, '<'); final int emailB = nextLF(raw, ptr, '<');
if (emailB == ptr || raw[emailB - 1] != '<') if (emailB == ptr || raw[emailB - 1] != '<')
return -1; return -1;

View File

@ -120,11 +120,8 @@ public void create() throws IOException {
* the object store cannot be accessed. * the object store cannot be accessed.
*/ */
public boolean has(final AnyObjectId objectId) throws IOException { public boolean has(final AnyObjectId objectId) throws IOException {
final ObjectReader or = newReader(); try (final ObjectReader or = newReader()) {
try {
return or.has(objectId); return or.has(objectId);
} finally {
or.release();
} }
} }
@ -172,11 +169,8 @@ public ObjectLoader open(final AnyObjectId objectId)
public ObjectLoader open(AnyObjectId objectId, int typeHint) public ObjectLoader open(AnyObjectId objectId, int typeHint)
throws MissingObjectException, IncorrectObjectTypeException, throws MissingObjectException, IncorrectObjectTypeException,
IOException { IOException {
final ObjectReader or = newReader(); try (final ObjectReader or = newReader()) {
try {
return or.open(objectId, typeHint); return or.open(objectId, typeHint);
} finally {
or.release();
} }
} }

View File

@ -0,0 +1,95 @@
/*
* Copyright (C) 2014, André de Oliveira <andre.oliveira@liferay.com>
*
* 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.merge;
import java.io.IOException;
import java.io.OutputStream;
/**
* An output stream which is aware of newlines and can be asked to begin a new
* line if not already in one.
*/
class EolAwareOutputStream extends OutputStream {
private final OutputStream out;
private boolean bol = true;
/**
* Initialize a new EOL aware stream.
*
* @param out
* stream to output all writes to.
*/
EolAwareOutputStream(OutputStream out) {
this.out = out;
}
/**
* Begin a new line if not already in one.
*
* @exception IOException
* if an I/O error occurs.
*/
void beginln() throws IOException {
if (!bol)
write('\n');
}
/** @return true if a new line has just begun. */
boolean isBeginln() {
return bol;
}
@Override
public void write(int val) throws IOException {
out.write(val);
bol = (val == '\n');
}
@Override
public void write(byte[] buf, int pos, int cnt) throws IOException {
if (cnt > 0) {
out.write(buf, pos, cnt);
bol = (buf[pos + (cnt - 1)] == '\n');
}
}
}

View File

@ -49,7 +49,6 @@
import java.util.List; import java.util.List;
import org.eclipse.jgit.diff.RawText; import org.eclipse.jgit.diff.RawText;
import org.eclipse.jgit.merge.MergeChunk.ConflictState;
/** /**
* A class to convert merge results into a Git conformant textual presentation * A class to convert merge results into a Git conformant textual presentation
@ -78,47 +77,7 @@ public class MergeFormatter {
*/ */
public void formatMerge(OutputStream out, MergeResult<RawText> res, public void formatMerge(OutputStream out, MergeResult<RawText> res,
List<String> seqName, String charsetName) throws IOException { List<String> seqName, String charsetName) throws IOException {
String lastConflictingName = null; // is set to non-null whenever we are new MergeFormatterPass(out, res, seqName, charsetName).formatMerge();
// in a conflict
boolean threeWayMerge = (res.getSequences().size() == 3);
for (MergeChunk chunk : res) {
RawText seq = res.getSequences().get(chunk.getSequenceIndex());
if (lastConflictingName != null
&& chunk.getConflictState() != ConflictState.NEXT_CONFLICTING_RANGE) {
// found the end of an conflict
out.write((">>>>>>> " + lastConflictingName + "\n").getBytes(charsetName)); //$NON-NLS-1$ //$NON-NLS-2$
lastConflictingName = null;
}
if (chunk.getConflictState() == ConflictState.FIRST_CONFLICTING_RANGE) {
// found the start of an conflict
out.write(("<<<<<<< " + seqName.get(chunk.getSequenceIndex()) + //$NON-NLS-1$
"\n").getBytes(charsetName)); //$NON-NLS-1$
lastConflictingName = seqName.get(chunk.getSequenceIndex());
} else if (chunk.getConflictState() == ConflictState.NEXT_CONFLICTING_RANGE) {
// found another conflicting chunk
/*
* In case of a non-three-way merge I'll add the name of the
* conflicting chunk behind the equal signs. I also append the
* name of the last conflicting chunk after the ending
* greater-than signs. If somebody knows a better notation to
* present non-three-way merges - feel free to correct here.
*/
lastConflictingName = seqName.get(chunk.getSequenceIndex());
out.write((threeWayMerge ? "=======\n" : "======= " //$NON-NLS-1$ //$NON-NLS-2$
+ lastConflictingName + "\n").getBytes(charsetName)); //$NON-NLS-1$
}
// the lines with conflict-metadata are written. Now write the chunk
for (int i = chunk.getBegin(); i < chunk.getEnd(); i++) {
seq.writeLine(out, i);
out.write('\n');
}
}
// one possible leftover: if the merge result ended with a conflict we
// have to close the last conflict here
if (lastConflictingName != null) {
out.write((">>>>>>> " + lastConflictingName + "\n").getBytes(charsetName)); //$NON-NLS-1$ //$NON-NLS-2$
}
} }
/** /**

View File

@ -0,0 +1,146 @@
/*
* Copyright (C) 2009, Christian Halstrick <christian.halstrick@sap.com>
* Copyright (C) 2014, André de Oliveira <andre.oliveira@liferay.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.merge;
import java.io.IOException;
import java.io.OutputStream;
import java.util.List;
import org.eclipse.jgit.diff.RawText;
import org.eclipse.jgit.merge.MergeChunk.ConflictState;
class MergeFormatterPass {
private final EolAwareOutputStream out;
private final MergeResult<RawText> res;
private final List<String> seqName;
private final String charsetName;
private final boolean threeWayMerge;
private String lastConflictingName; // is set to non-null whenever we are in
// a conflict
MergeFormatterPass(OutputStream out, MergeResult<RawText> res, List<String> seqName,
String charsetName) {
this.out = new EolAwareOutputStream(out);
this.res = res;
this.seqName = seqName;
this.charsetName = charsetName;
this.threeWayMerge = (res.getSequences().size() == 3);
}
void formatMerge() throws IOException {
boolean missingNewlineAtEnd = false;
for (MergeChunk chunk : res) {
RawText seq = res.getSequences().get(chunk.getSequenceIndex());
writeConflictMetadata(chunk);
// the lines with conflict-metadata are written. Now write the chunk
for (int i = chunk.getBegin(); i < chunk.getEnd(); i++)
writeLine(seq, i);
missingNewlineAtEnd = seq.isMissingNewlineAtEnd();
}
// one possible leftover: if the merge result ended with a conflict we
// have to close the last conflict here
if (lastConflictingName != null)
writeConflictEnd();
if (!missingNewlineAtEnd)
out.beginln();
}
private void writeConflictMetadata(MergeChunk chunk) throws IOException {
if (lastConflictingName != null
&& chunk.getConflictState() != ConflictState.NEXT_CONFLICTING_RANGE) {
// found the end of an conflict
writeConflictEnd();
}
if (chunk.getConflictState() == ConflictState.FIRST_CONFLICTING_RANGE) {
// found the start of an conflict
writeConflictStart(chunk);
} else if (chunk.getConflictState() == ConflictState.NEXT_CONFLICTING_RANGE) {
// found another conflicting chunk
writeConflictChange(chunk);
}
}
private void writeConflictEnd() throws IOException {
writeln(">>>>>>> " + lastConflictingName); //$NON-NLS-1$
lastConflictingName = null;
}
private void writeConflictStart(MergeChunk chunk) throws IOException {
lastConflictingName = seqName.get(chunk.getSequenceIndex());
writeln("<<<<<<< " + lastConflictingName); //$NON-NLS-1$
}
private void writeConflictChange(MergeChunk chunk) throws IOException {
/*
* In case of a non-three-way merge I'll add the name of the conflicting
* chunk behind the equal signs. I also append the name of the last
* conflicting chunk after the ending greater-than signs. If somebody
* knows a better notation to present non-three-way merges - feel free
* to correct here.
*/
lastConflictingName = seqName.get(chunk.getSequenceIndex());
writeln(threeWayMerge ? "=======" : "======= " //$NON-NLS-1$ //$NON-NLS-2$
+ lastConflictingName);
}
private void writeln(String s) throws IOException {
out.beginln();
out.write((s + "\n").getBytes(charsetName)); //$NON-NLS-1$
}
private void writeLine(RawText seq, int i) throws IOException {
out.beginln();
seq.writeLine(out, i);
// still BOL? It was a blank line. But writeLine won't lf, so we do.
if (out.isBeginln())
out.write('\n');
}
}

View File

@ -125,9 +125,9 @@ public ObjectInserter getObjectInserter() {
* repository instance returned by {@link #getRepository()}. * repository instance returned by {@link #getRepository()}.
*/ */
public void setObjectInserter(ObjectInserter oi) { public void setObjectInserter(ObjectInserter oi) {
walk.release(); walk.close();
reader.release(); reader.close();
inserter.release(); inserter.close();
inserter = oi; inserter = oi;
reader = oi.newReader(); reader = oi.newReader();
walk = new RevWalk(reader); walk = new RevWalk(reader);
@ -206,8 +206,8 @@ public boolean merge(final boolean flush, final AnyObjectId... tips)
return ok; return ok;
} finally { } finally {
if (flush) if (flush)
inserter.release(); inserter.close();
reader.release(); reader.close();
} }
} }

View File

@ -68,6 +68,8 @@
import org.eclipse.jgit.lib.Repository; import org.eclipse.jgit.lib.Repository;
import org.eclipse.jgit.revwalk.RevCommit; import org.eclipse.jgit.revwalk.RevCommit;
import org.eclipse.jgit.revwalk.filter.RevFilter; import org.eclipse.jgit.revwalk.filter.RevFilter;
import org.eclipse.jgit.treewalk.AbstractTreeIterator;
import org.eclipse.jgit.treewalk.EmptyTreeIterator;
import org.eclipse.jgit.treewalk.TreeWalk; import org.eclipse.jgit.treewalk.TreeWalk;
import org.eclipse.jgit.treewalk.WorkingTreeIterator; import org.eclipse.jgit.treewalk.WorkingTreeIterator;
@ -194,10 +196,12 @@ protected RevCommit getBaseCommit(RevCommit a, RevCommit b, int callDepth)
Integer.valueOf(MAX_BASES), a.name(), b.name(), Integer.valueOf(MAX_BASES), a.name(), b.name(),
Integer.valueOf(baseCommits.size()))); Integer.valueOf(baseCommits.size())));
parents.add(nextBase); parents.add(nextBase);
if (mergeTrees( RevCommit bc = getBaseCommit(currentBase, nextBase,
openTree(getBaseCommit(currentBase, nextBase, callDepth + 1);
callDepth + 1).getTree()), AbstractTreeIterator bcTree = (bc == null) ? new EmptyTreeIterator()
currentBase.getTree(), nextBase.getTree(), true)) : openTree(bc.getTree());
if (mergeTrees(bcTree, currentBase.getTree(),
nextBase.getTree(), true))
currentBase = createCommitForTree(resultTree, parents); currentBase = createCommitForTree(resultTree, parents);
else else
throw new NoMergeBaseException( throw new NoMergeBaseException(

View File

@ -47,12 +47,14 @@
import static org.eclipse.jgit.lib.Constants.CHARACTER_ENCODING; import static org.eclipse.jgit.lib.Constants.CHARACTER_ENCODING;
import static org.eclipse.jgit.lib.Constants.OBJ_BLOB; import static org.eclipse.jgit.lib.Constants.OBJ_BLOB;
import java.io.BufferedOutputStream;
import java.io.File; import java.io.File;
import java.io.FileInputStream; import java.io.FileInputStream;
import java.io.FileNotFoundException; import java.io.FileNotFoundException;
import java.io.FileOutputStream; import java.io.FileOutputStream;
import java.io.IOException; import java.io.IOException;
import java.io.InputStream; import java.io.InputStream;
import java.io.OutputStream;
import java.util.ArrayList; import java.util.ArrayList;
import java.util.Arrays; import java.util.Arrays;
import java.util.Collections; import java.util.Collections;
@ -791,25 +793,25 @@ private File writeMergedFile(MergeResult<RawText> result)
File parentFolder = of.getParentFile(); File parentFolder = of.getParentFile();
if (!fs.exists(parentFolder)) if (!fs.exists(parentFolder))
parentFolder.mkdirs(); parentFolder.mkdirs();
FileOutputStream fos = new FileOutputStream(of); try (OutputStream os = new BufferedOutputStream(
try { new FileOutputStream(of))) {
new MergeFormatter().formatMerge(fos, result, new MergeFormatter().formatMerge(os, result,
Arrays.asList(commitNames), CHARACTER_ENCODING); Arrays.asList(commitNames), CHARACTER_ENCODING);
} finally {
fos.close();
} }
return of; return of;
} }
private ObjectId insertMergeResult(MergeResult<RawText> result) private ObjectId insertMergeResult(MergeResult<RawText> result)
throws IOException { throws IOException {
TemporaryBuffer.LocalFile buf = new TemporaryBuffer.LocalFile(10 << 20); TemporaryBuffer.LocalFile buf = new TemporaryBuffer.LocalFile(
db.getDirectory(), 10 << 20);
try { try {
new MergeFormatter().formatMerge(buf, result, new MergeFormatter().formatMerge(buf, result,
Arrays.asList(commitNames), CHARACTER_ENCODING); Arrays.asList(commitNames), CHARACTER_ENCODING);
buf.close(); buf.close();
return getObjectInserter().insert(OBJ_BLOB, buf.length(), try (InputStream in = buf.openInputStream()) {
buf.openInputStream()); return getObjectInserter().insert(OBJ_BLOB, buf.length(), in);
}
} finally { } finally {
buf.destroy(); buf.destroy();
} }

View File

@ -135,8 +135,8 @@ public NoteMap merge(NoteMap base, NoteMap ours, NoteMap theirs)
inserter.flush(); inserter.flush();
return NoteMap.newMap(mergedBucket, reader); return NoteMap.newMap(mergedBucket, reader);
} finally { } finally {
reader.release(); reader.close();
inserter.release(); inserter.close();
} }
} }

View File

@ -232,7 +232,7 @@ public void markUninteresting(RevObject o) throws MissingObjectException,
} }
if (o instanceof RevCommit) if (o instanceof RevCommit)
markUninteresting((RevCommit) o); super.markUninteresting((RevCommit) o);
else if (o instanceof RevTree) else if (o instanceof RevTree)
markTreeUninteresting((RevTree) o); markTreeUninteresting((RevTree) o);
else else
@ -242,18 +242,6 @@ else if (o instanceof RevTree)
addObject(o); addObject(o);
} }
@Override
public void markUninteresting(RevCommit c) throws MissingObjectException,
IncorrectObjectTypeException, IOException {
super.markUninteresting(c);
try {
markTreeUninteresting(c.getTree());
} catch (MissingObjectException e) {
// we don't care if the tree of the commit does not exist locally
}
}
@Override
public void sort(RevSort s) { public void sort(RevSort s) {
super.sort(s); super.sort(s);
boundary = hasRevSort(RevSort.BOUNDARY); boundary = hasRevSort(RevSort.BOUNDARY);

View File

@ -65,6 +65,8 @@
public abstract class BaseConnection implements Connection { public abstract class BaseConnection implements Connection {
private Map<String, Ref> advertisedRefs = Collections.emptyMap(); private Map<String, Ref> advertisedRefs = Collections.emptyMap();
private String peerUserAgent;
private boolean startedOperation; private boolean startedOperation;
private Writer messageWriter; private Writer messageWriter;
@ -85,6 +87,28 @@ public String getMessages() {
return messageWriter != null ? messageWriter.toString() : ""; //$NON-NLS-1$ return messageWriter != null ? messageWriter.toString() : ""; //$NON-NLS-1$
} }
/**
* User agent advertised by the remote server.
*
* @return agent (version of Git) running on the remote server. Null if the
* server does not advertise this version.
* @since 4.0
*/
public String getPeerUserAgent() {
return peerUserAgent;
}
/**
* Remember the remote peer's agent.
*
* @param agent
* remote peer agent string.
* @since 4.0
*/
protected void setPeerUserAgent(String agent) {
peerUserAgent = agent;
}
public abstract void close(); public abstract void close();
/** /**

View File

@ -46,6 +46,8 @@
package org.eclipse.jgit.transport; package org.eclipse.jgit.transport;
import static org.eclipse.jgit.transport.GitProtocolConstants.OPTION_AGENT;
import java.io.EOFException; import java.io.EOFException;
import java.io.IOException; import java.io.IOException;
import java.io.InputStream; import java.io.InputStream;
@ -275,6 +277,18 @@ protected boolean wantCapability(final StringBuilder b, final String option) {
return true; return true;
} }
protected void addUserAgentCapability(StringBuilder b) {
String a = UserAgent.get();
if (a != null && UserAgent.hasAgent(remoteCapablities)) {
b.append(' ').append(OPTION_AGENT).append('=').append(a);
}
}
@Override
public String getPeerUserAgent() {
return UserAgent.getAgent(remoteCapablities, super.getPeerUserAgent());
}
private PackProtocolException duplicateAdvertisement(final String name) { private PackProtocolException duplicateAdvertisement(final String name) {
return new PackProtocolException(uri, MessageFormat.format(JGitText.get().duplicateAdvertisementsOf, name)); return new PackProtocolException(uri, MessageFormat.format(JGitText.get().duplicateAdvertisementsOf, name));
} }

View File

@ -377,7 +377,7 @@ protected void doFetch(final ProgressMonitor monitor,
@Override @Override
public void close() { public void close() {
if (walk != null) if (walk != null)
walk.release(); walk.close();
super.close(); super.close();
} }
@ -521,6 +521,7 @@ else if (wantCapability(line, OPTION_SIDE_BAND))
OPTION_MULTI_ACK_DETAILED)); OPTION_MULTI_ACK_DETAILED));
} }
addUserAgentCapability(line);
return line.toString(); return line.toString();
} }
@ -753,16 +754,13 @@ private void receivePack(final ProgressMonitor monitor,
input = new SideBandInputStream(input, monitor, getMessageWriter(), input = new SideBandInputStream(input, monitor, getMessageWriter(),
outputStream); outputStream);
ObjectInserter ins = local.newObjectInserter(); try (ObjectInserter ins = local.newObjectInserter()) {
try {
PackParser parser = ins.newPackParser(input); PackParser parser = ins.newPackParser(input);
parser.setAllowThin(thinPack); parser.setAllowThin(thinPack);
parser.setObjectChecker(transport.getObjectChecker()); parser.setObjectChecker(transport.getObjectChecker());
parser.setLockMessage(lockMessage); parser.setLockMessage(lockMessage);
packLock = parser.parse(monitor); packLock = parser.parse(monitor);
ins.flush(); ins.flush();
} finally {
ins.release();
} }
} }

View File

@ -268,6 +268,7 @@ private String enableCapabilities(final ProgressMonitor monitor,
outputStream); outputStream);
pckIn = new PacketLineIn(in); pckIn = new PacketLineIn(in);
} }
addUserAgentCapability(line);
if (line.length() > 0) if (line.length() > 0)
line.setCharAt(0, '\0'); line.setCharAt(0, '\0');
@ -279,9 +280,8 @@ private void writePack(final Map<String, RemoteRefUpdate> refUpdates,
Set<ObjectId> remoteObjects = new HashSet<ObjectId>(); Set<ObjectId> remoteObjects = new HashSet<ObjectId>();
Set<ObjectId> newObjects = new HashSet<ObjectId>(); Set<ObjectId> newObjects = new HashSet<ObjectId>();
final PackWriter writer = new PackWriter(transport.getPackConfig(), try (final PackWriter writer = new PackWriter(transport.getPackConfig(),
local.newObjectReader()); local.newObjectReader())) {
try {
for (final Ref r : getRefs()) { for (final Ref r : getRefs()) {
// only add objects that we actually have // only add objects that we actually have
@ -303,10 +303,9 @@ private void writePack(final Map<String, RemoteRefUpdate> refUpdates,
writer.setDeltaBaseAsOffset(capableOfsDelta); writer.setDeltaBaseAsOffset(capableOfsDelta);
writer.preparePack(monitor, newObjects, remoteObjects); writer.preparePack(monitor, newObjects, remoteObjects);
writer.writePack(monitor, monitor, out); writer.writePack(monitor, monitor, out);
} finally {
writer.release(); packTransferTime = writer.getStatistics().getTimeWriting();
} }
packTransferTime = writer.getStatistics().getTimeWriting();
} }
private void readStatusReport(final Map<String, RemoteRefUpdate> refUpdates) private void readStatusReport(final Map<String, RemoteRefUpdate> refUpdates)

View File

@ -48,6 +48,7 @@
import static org.eclipse.jgit.transport.GitProtocolConstants.CAPABILITY_OFS_DELTA; import static org.eclipse.jgit.transport.GitProtocolConstants.CAPABILITY_OFS_DELTA;
import static org.eclipse.jgit.transport.GitProtocolConstants.CAPABILITY_REPORT_STATUS; import static org.eclipse.jgit.transport.GitProtocolConstants.CAPABILITY_REPORT_STATUS;
import static org.eclipse.jgit.transport.GitProtocolConstants.CAPABILITY_SIDE_BAND_64K; import static org.eclipse.jgit.transport.GitProtocolConstants.CAPABILITY_SIDE_BAND_64K;
import static org.eclipse.jgit.transport.GitProtocolConstants.OPTION_AGENT;
import static org.eclipse.jgit.transport.SideBandOutputStream.CH_DATA; import static org.eclipse.jgit.transport.SideBandOutputStream.CH_DATA;
import static org.eclipse.jgit.transport.SideBandOutputStream.CH_PROGRESS; import static org.eclipse.jgit.transport.SideBandOutputStream.CH_PROGRESS;
import static org.eclipse.jgit.transport.SideBandOutputStream.MAX_BUF; import static org.eclipse.jgit.transport.SideBandOutputStream.MAX_BUF;
@ -224,6 +225,7 @@ public Set<String> getCapabilities() {
/** Capabilities requested by the client. */ /** Capabilities requested by the client. */
private Set<String> enabledCapabilities; private Set<String> enabledCapabilities;
String userAgent;
private Set<ObjectId> clientShallowCommits; private Set<ObjectId> clientShallowCommits;
private List<ReceiveCommand> commands; private List<ReceiveCommand> commands;
@ -289,6 +291,7 @@ public ReceiveConfig parse(final Config cfg) {
final boolean checkReceivedObjects; final boolean checkReceivedObjects;
final boolean allowLeadingZeroFileMode; final boolean allowLeadingZeroFileMode;
final boolean allowInvalidPersonIdent;
final boolean safeForWindows; final boolean safeForWindows;
final boolean safeForMacOS; final boolean safeForMacOS;
@ -306,6 +309,8 @@ public ReceiveConfig parse(final Config cfg) {
config.getBoolean("transfer", "fsckobjects", false)); //$NON-NLS-1$ //$NON-NLS-2$ config.getBoolean("transfer", "fsckobjects", false)); //$NON-NLS-1$ //$NON-NLS-2$
allowLeadingZeroFileMode = checkReceivedObjects allowLeadingZeroFileMode = checkReceivedObjects
&& config.getBoolean("fsck", "allowLeadingZeroFileMode", false); //$NON-NLS-1$ //$NON-NLS-2$ && config.getBoolean("fsck", "allowLeadingZeroFileMode", false); //$NON-NLS-1$ //$NON-NLS-2$
allowInvalidPersonIdent = checkReceivedObjects
&& config.getBoolean("fsck", "allowInvalidPersonIdent", false); //$NON-NLS-1$ //$NON-NLS-2$
safeForWindows = checkReceivedObjects safeForWindows = checkReceivedObjects
&& config.getBoolean("fsck", "safeForWindows", false); //$NON-NLS-1$ //$NON-NLS-2$ && config.getBoolean("fsck", "safeForWindows", false); //$NON-NLS-1$ //$NON-NLS-2$
safeForMacOS = checkReceivedObjects safeForMacOS = checkReceivedObjects
@ -317,7 +322,7 @@ public ReceiveConfig parse(final Config cfg) {
"denynonfastforwards", false); //$NON-NLS-1$ "denynonfastforwards", false); //$NON-NLS-1$
allowOfsDelta = config.getBoolean("repack", "usedeltabaseoffset", //$NON-NLS-1$ //$NON-NLS-2$ allowOfsDelta = config.getBoolean("repack", "usedeltabaseoffset", //$NON-NLS-1$ //$NON-NLS-2$
true); true);
certNonceSeed = config.getString("receive", null, "certnonceseed"); //$NON-NLS-1$ //$NON-NLS-2$ //$NON-NLS-3$ certNonceSeed = config.getString("receive", null, "certnonceseed"); //$NON-NLS-1$ //$NON-NLS-2$
certNonceSlopLimit = config.getInt("receive", "certnonceslop", 0); //$NON-NLS-1$ //$NON-NLS-2$ certNonceSlopLimit = config.getInt("receive", "certnonceslop", 0); //$NON-NLS-1$ //$NON-NLS-2$
} }
@ -326,6 +331,7 @@ ObjectChecker newObjectChecker() {
return null; return null;
return new ObjectChecker() return new ObjectChecker()
.setAllowLeadingZeroFileMode(allowLeadingZeroFileMode) .setAllowLeadingZeroFileMode(allowLeadingZeroFileMode)
.setAllowInvalidPersonIdent(allowInvalidPersonIdent)
.setSafeForWindows(safeForWindows) .setSafeForWindows(safeForWindows)
.setSafeForMacOS(safeForMacOS); .setSafeForMacOS(safeForMacOS);
} }
@ -734,6 +740,25 @@ public boolean isSideBand() throws RequestNotYetReadException {
return enabledCapabilities.contains(CAPABILITY_SIDE_BAND_64K); return enabledCapabilities.contains(CAPABILITY_SIDE_BAND_64K);
} }
/**
* Get the user agent of the client.
* <p>
* If the client is new enough to use {@code agent=} capability that value
* will be returned. Older HTTP clients may also supply their version using
* the HTTP {@code User-Agent} header. The capability overrides the HTTP
* header if both are available.
* <p>
* When an HTTP request has been received this method returns the HTTP
* {@code User-Agent} header value until capabilities have been parsed.
*
* @return user agent supplied by the client. Available only if the client
* is new enough to advertise its user agent.
* @since 4.0
*/
public String getPeerUserAgent() {
return UserAgent.getAgent(enabledCapabilities, userAgent);
}
/** @return all of the command received by the current request. */ /** @return all of the command received by the current request. */
public List<ReceiveCommand> getAllCommands() { public List<ReceiveCommand> getAllCommands() {
return Collections.unmodifiableList(commands); return Collections.unmodifiableList(commands);
@ -951,6 +976,7 @@ public void sendAdvertisedRefs(final RefAdvertiser adv)
adv.advertiseCapability(CAPABILITY_ATOMIC); adv.advertiseCapability(CAPABILITY_ATOMIC);
if (allowOfsDelta) if (allowOfsDelta)
adv.advertiseCapability(CAPABILITY_OFS_DELTA); adv.advertiseCapability(CAPABILITY_OFS_DELTA);
adv.advertiseCapability(OPTION_AGENT, UserAgent.get());
adv.send(getAdvertisedOrDefaultRefs()); adv.send(getAdvertisedOrDefaultRefs());
for (ObjectId obj : advertisedHaves) for (ObjectId obj : advertisedHaves)
adv.advertiseHave(obj); adv.advertiseHave(obj);
@ -1070,8 +1096,7 @@ private void receivePack() throws IOException {
if (sideBand) if (sideBand)
resolving = new SideBandProgressMonitor(msgOut); resolving = new SideBandProgressMonitor(msgOut);
ObjectInserter ins = db.newObjectInserter(); try (ObjectInserter ins = db.newObjectInserter()) {
try {
String lockMsg = "jgit receive-pack"; //$NON-NLS-1$ String lockMsg = "jgit receive-pack"; //$NON-NLS-1$
if (getRefLogIdent() != null) if (getRefLogIdent() != null)
lockMsg += " from " + getRefLogIdent().toExternalString(); //$NON-NLS-1$ lockMsg += " from " + getRefLogIdent().toExternalString(); //$NON-NLS-1$
@ -1089,8 +1114,6 @@ private void receivePack() throws IOException {
packLock = parser.parse(receiving, resolving); packLock = parser.parse(receiving, resolving);
packSize = Long.valueOf(parser.getPackSize()); packSize = Long.valueOf(parser.getPackSize());
ins.flush(); ins.flush();
} finally {
ins.release();
} }
if (timeoutIn != null) if (timeoutIn != null)
@ -1119,67 +1142,69 @@ private void checkConnectivity() throws IOException {
} }
parser = null; parser = null;
final ObjectWalk ow = new ObjectWalk(db); try (final ObjectWalk ow = new ObjectWalk(db)) {
ow.setRetainBody(false); ow.setRetainBody(false);
if (baseObjects != null) { if (baseObjects != null) {
ow.sort(RevSort.TOPO); ow.sort(RevSort.TOPO);
if (!baseObjects.isEmpty()) if (!baseObjects.isEmpty())
ow.sort(RevSort.BOUNDARY, true); ow.sort(RevSort.BOUNDARY, true);
}
for (final ReceiveCommand cmd : commands) {
if (cmd.getResult() != Result.NOT_ATTEMPTED)
continue;
if (cmd.getType() == ReceiveCommand.Type.DELETE)
continue;
ow.markStart(ow.parseAny(cmd.getNewId()));
}
for (final ObjectId have : advertisedHaves) {
RevObject o = ow.parseAny(have);
ow.markUninteresting(o);
if (baseObjects != null && !baseObjects.isEmpty()) {
o = ow.peel(o);
if (o instanceof RevCommit)
o = ((RevCommit) o).getTree();
if (o instanceof RevTree)
ow.markUninteresting(o);
} }
}
checking.beginTask(JGitText.get().countingObjects, ProgressMonitor.UNKNOWN); for (final ReceiveCommand cmd : commands) {
RevCommit c; if (cmd.getResult() != Result.NOT_ATTEMPTED)
while ((c = ow.next()) != null) {
checking.update(1);
if (providedObjects != null //
&& !c.has(RevFlag.UNINTERESTING) //
&& !providedObjects.contains(c))
throw new MissingObjectException(c, Constants.TYPE_COMMIT);
}
RevObject o;
while ((o = ow.nextObject()) != null) {
checking.update(1);
if (o.has(RevFlag.UNINTERESTING))
continue;
if (providedObjects != null) {
if (providedObjects.contains(o))
continue; continue;
else if (cmd.getType() == ReceiveCommand.Type.DELETE)
throw new MissingObjectException(o, o.getType()); continue;
ow.markStart(ow.parseAny(cmd.getNewId()));
}
for (final ObjectId have : advertisedHaves) {
RevObject o = ow.parseAny(have);
ow.markUninteresting(o);
if (baseObjects != null && !baseObjects.isEmpty()) {
o = ow.peel(o);
if (o instanceof RevCommit)
o = ((RevCommit) o).getTree();
if (o instanceof RevTree)
ow.markUninteresting(o);
}
} }
if (o instanceof RevBlob && !db.hasObject(o)) checking.beginTask(JGitText.get().countingObjects,
throw new MissingObjectException(o, Constants.TYPE_BLOB); ProgressMonitor.UNKNOWN);
} RevCommit c;
checking.endTask(); while ((c = ow.next()) != null) {
checking.update(1);
if (providedObjects != null //
&& !c.has(RevFlag.UNINTERESTING) //
&& !providedObjects.contains(c))
throw new MissingObjectException(c, Constants.TYPE_COMMIT);
}
if (baseObjects != null) { RevObject o;
for (ObjectId id : baseObjects) { while ((o = ow.nextObject()) != null) {
o = ow.parseAny(id); checking.update(1);
if (!o.has(RevFlag.UNINTERESTING)) if (o.has(RevFlag.UNINTERESTING))
throw new MissingObjectException(o, o.getType()); continue;
if (providedObjects != null) {
if (providedObjects.contains(o))
continue;
else
throw new MissingObjectException(o, o.getType());
}
if (o instanceof RevBlob && !db.hasObject(o))
throw new MissingObjectException(o, Constants.TYPE_BLOB);
}
checking.endTask();
if (baseObjects != null) {
for (ObjectId id : baseObjects) {
o = ow.parseAny(id);
if (!o.has(RevFlag.UNINTERESTING))
throw new MissingObjectException(o, o.getType());
}
} }
} }
} }
@ -1502,7 +1527,7 @@ protected void close() throws IOException {
* the pack could not be unlocked. * the pack could not be unlocked.
*/ */
protected void release() throws IOException { protected void release() throws IOException {
walk.release(); walk.close();
unlockPack(); unlockPack();
timeoutIn = null; timeoutIn = null;
rawIn = null; rawIn = null;

View File

@ -183,16 +183,13 @@ protected void doFetch(final ProgressMonitor monitor,
throws TransportException { throws TransportException {
verifyPrerequisites(); verifyPrerequisites();
try { try {
ObjectInserter ins = transport.local.newObjectInserter(); try (ObjectInserter ins = transport.local.newObjectInserter()) {
try {
PackParser parser = ins.newPackParser(bin); PackParser parser = ins.newPackParser(bin);
parser.setAllowThin(true); parser.setAllowThin(true);
parser.setObjectChecker(transport.getObjectChecker()); parser.setObjectChecker(transport.getObjectChecker());
parser.setLockMessage(lockMessage); parser.setLockMessage(lockMessage);
packLock = parser.parse(NullProgressMonitor.INSTANCE); packLock = parser.parse(NullProgressMonitor.INSTANCE);
ins.flush(); ins.flush();
} finally {
ins.release();
} }
} catch (IOException err) { } catch (IOException err) {
close(); close();
@ -217,8 +214,7 @@ private void verifyPrerequisites() throws TransportException {
if (prereqs.isEmpty()) if (prereqs.isEmpty())
return; return;
final RevWalk rw = new RevWalk(transport.local); try (final RevWalk rw = new RevWalk(transport.local)) {
try {
final RevFlag PREREQ = rw.newFlag("PREREQ"); //$NON-NLS-1$ final RevFlag PREREQ = rw.newFlag("PREREQ"); //$NON-NLS-1$
final RevFlag SEEN = rw.newFlag("SEEN"); //$NON-NLS-1$ final RevFlag SEEN = rw.newFlag("SEEN"); //$NON-NLS-1$
@ -281,8 +277,6 @@ private void verifyPrerequisites() throws TransportException {
throw new MissingBundlePrerequisiteException(transport.uri, throw new MissingBundlePrerequisiteException(transport.uri,
missing); missing);
} }
} finally {
rw.release();
} }
} }

View File

@ -194,8 +194,7 @@ public void writeBundle(ProgressMonitor monitor, OutputStream os)
PackConfig pc = packConfig; PackConfig pc = packConfig;
if (pc == null) if (pc == null)
pc = new PackConfig(db); pc = new PackConfig(db);
PackWriter packWriter = new PackWriter(pc, db.newObjectReader()); try (PackWriter packWriter = new PackWriter(pc, db.newObjectReader())) {
try {
final HashSet<ObjectId> inc = new HashSet<ObjectId>(); final HashSet<ObjectId> inc = new HashSet<ObjectId>();
final HashSet<ObjectId> exc = new HashSet<ObjectId>(); final HashSet<ObjectId> exc = new HashSet<ObjectId>();
inc.addAll(include.values()); inc.addAll(include.values());
@ -233,8 +232,6 @@ public void writeBundle(ProgressMonitor monitor, OutputStream os)
w.write('\n'); w.write('\n');
w.flush(); w.flush();
packWriter.writePack(monitor, monitor, os); packWriter.writePack(monitor, monitor, os);
} finally {
packWriter.release();
} }
} }
} }

View File

@ -127,4 +127,13 @@ public interface Connection {
* remote produced no additional messages. * remote produced no additional messages.
*/ */
public String getMessages(); public String getMessages();
/**
* User agent advertised by the remote server.
*
* @return agent (version of Git) running on the remote server. Null if the
* server does not advertise this version.
* @since 4.0
*/
public String getPeerUserAgent();
} }

View File

@ -136,6 +136,7 @@ private void executeImp(final ProgressMonitor monitor,
conn = transport.openFetch(); conn = transport.openFetch();
try { try {
result.setAdvertisedRefs(transport.getURI(), conn.getRefsMap()); result.setAdvertisedRefs(transport.getURI(), conn.getRefsMap());
result.peerUserAgent = conn.getPeerUserAgent();
final Set<Ref> matched = new HashSet<Ref>(); final Set<Ref> matched = new HashSet<Ref>();
for (final RefSpec spec : toFetch) { for (final RefSpec spec : toFetch) {
if (spec.getSource() == null) if (spec.getSource() == null)
@ -196,8 +197,7 @@ else if (tagopt == TagOpt.FETCH_TAGS)
.newBatchUpdate() .newBatchUpdate()
.setAllowNonFastForwards(true) .setAllowNonFastForwards(true)
.setRefLogMessage("fetch", true); //$NON-NLS-1$ .setRefLogMessage("fetch", true); //$NON-NLS-1$
final RevWalk walk = new RevWalk(transport.local); try (final RevWalk walk = new RevWalk(transport.local)) {
try {
if (monitor instanceof BatchingProgressMonitor) { if (monitor instanceof BatchingProgressMonitor) {
((BatchingProgressMonitor) monitor).setDelayStart( ((BatchingProgressMonitor) monitor).setDelayStart(
250, TimeUnit.MILLISECONDS); 250, TimeUnit.MILLISECONDS);
@ -226,8 +226,6 @@ else if (tagopt == TagOpt.FETCH_TAGS)
throw new TransportException(MessageFormat.format( throw new TransportException(MessageFormat.format(
JGitText.get().failureUpdatingTrackingRef, JGitText.get().failureUpdatingTrackingRef,
getFirstFailedRefName(batch), err.getMessage()), err); getFirstFailedRefName(batch), err.getMessage()), err);
} finally {
walk.release();
} }
if (!fetchHeadUpdates.isEmpty()) { if (!fetchHeadUpdates.isEmpty()) {
@ -338,15 +336,12 @@ private void updateFETCH_HEAD(final FetchResult result) throws IOException {
private boolean askForIsComplete() throws TransportException { private boolean askForIsComplete() throws TransportException {
try { try {
final ObjectWalk ow = new ObjectWalk(transport.local); try (final ObjectWalk ow = new ObjectWalk(transport.local)) {
try {
for (final ObjectId want : askFor.keySet()) for (final ObjectId want : askFor.keySet())
ow.markStart(ow.parseAny(want)); ow.markStart(ow.parseAny(want));
for (final Ref ref : localRefs().values()) for (final Ref ref : localRefs().values())
ow.markUninteresting(ow.parseAny(ref.getObjectId())); ow.markUninteresting(ow.parseAny(ref.getObjectId()));
ow.checkConnectivity(); ow.checkConnectivity();
} finally {
ow.release();
} }
return true; return true;
} catch (MissingObjectException e) { } catch (MissingObjectException e) {

View File

@ -186,6 +186,13 @@ public class GitProtocolConstants {
*/ */
public static final String CAPABILITY_PUSH_CERT = "push-cert"; //$NON-NLS-1$ public static final String CAPABILITY_PUSH_CERT = "push-cert"; //$NON-NLS-1$
/**
* Implementation name and version of the client or server.
*
* @since 4.0
*/
public static final String OPTION_AGENT = "agent"; //$NON-NLS-1$
static enum MultiAck { static enum MultiAck {
OFF, CONTINUE, DETAILED; OFF, CONTINUE, DETAILED;
} }

View File

@ -0,0 +1,80 @@
/*
* Copyright (C) 2015, 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.transport;
/**
* Internal API to to assist {@code org.eclipse.jgit.http.server}.
* <p>
* <b>Do not call.</b>
*
* @since 4.0
*/
public class InternalHttpServerGlue {
/**
* Apply a default user agent for a request.
*
* @param up
* current UploadPack instance.
* @param agent
* user agent string from the HTTP headers.
*/
public static void setPeerUserAgent(UploadPack up, String agent) {
up.userAgent = agent;
}
/**
* Apply a default user agent for a request.
*
* @param rp
* current ReceivePack instance.
* @param agent
* user agent string from the HTTP headers.
*/
public static void setPeerUserAgent(ReceivePack rp, String agent) {
rp.userAgent = agent;
}
private InternalHttpServerGlue() {
}
}

View File

@ -68,6 +68,8 @@ public abstract class OperationResult {
StringBuilder messageBuffer; StringBuilder messageBuffer;
String peerUserAgent;
/** /**
* Get the URI this result came from. * Get the URI this result came from.
* <p> * <p>
@ -165,4 +167,15 @@ void addMessages(final String msg) {
messageBuffer.append('\n'); messageBuffer.append('\n');
} }
} }
/**
* Get the user agent advertised by the peer server, if available.
*
* @return advertised user agent, e.g. {@code "JGit/4.0"}. Null if the peer
* did not advertise version information.
* @since 4.0
*/
public String getPeerUserAgent() {
return peerUserAgent;
}
} }

View File

@ -529,7 +529,7 @@ public PackLock parse(ProgressMonitor receiving, ProgressMonitor resolving)
} finally { } finally {
try { try {
if (readCurs != null) if (readCurs != null)
readCurs.release(); readCurs.close();
} finally { } finally {
readCurs = null; readCurs = null;
} }
@ -812,7 +812,7 @@ private void resolveDeltasWithExternalBases(final ProgressMonitor progress)
for (final DeltaChain base : missing) { for (final DeltaChain base : missing) {
if (base.head != null) if (base.head != null)
throw new MissingObjectException(base, "delta base"); throw new MissingObjectException(base, "delta base"); //$NON-NLS-1$
} }
onEndThinPack(); onEndThinPack();

View File

@ -155,6 +155,7 @@ PushResult execute(final ProgressMonitor monitor)
try { try {
res.setAdvertisedRefs(transport.getURI(), connection res.setAdvertisedRefs(transport.getURI(), connection
.getRefsMap()); .getRefsMap());
res.peerUserAgent = connection.getPeerUserAgent();
res.setRemoteUpdates(toPush); res.setRemoteUpdates(toPush);
monitor.endTask(); monitor.endTask();

View File

@ -147,6 +147,21 @@ public void advertiseCapability(String name) {
capablities.add(name); capablities.add(name);
} }
/**
* Add one protocol capability with a value ({@code "name=value"}).
*
* @param name
* name of the capability.
* @param value
* value. If null the capability will not be added.
* @since 4.0
*/
public void advertiseCapability(String name, String value) {
if (value != null) {
capablities.add(name + '=' + value);
}
}
/** /**
* Add a symbolic ref to capabilities. * Add a symbolic ref to capabilities.
* <p> * <p>
@ -164,8 +179,7 @@ public void advertiseCapability(String name) {
* @since 3.6 * @since 3.6
*/ */
public void addSymref(String from, String to) { public void addSymref(String from, String to) {
String symref = String.format("%s=%s:%s", OPTION_SYMREF, from, to); //$NON-NLS-1$ advertiseCapability(OPTION_SYMREF, from + ':' + to);
advertiseCapability(symref);
} }
/** /**

View File

@ -67,6 +67,7 @@ public TransferConfig parse(final Config cfg) {
private final boolean checkReceivedObjects; private final boolean checkReceivedObjects;
private final boolean allowLeadingZeroFileMode; private final boolean allowLeadingZeroFileMode;
private final boolean allowInvalidPersonIdent;
private final boolean safeForWindows; private final boolean safeForWindows;
private final boolean safeForMacOS; private final boolean safeForMacOS;
private final boolean allowTipSha1InWant; private final boolean allowTipSha1InWant;
@ -82,6 +83,8 @@ private TransferConfig(final Config rc) {
rc.getBoolean("transfer", "fsckobjects", false)); //$NON-NLS-1$ //$NON-NLS-2$ rc.getBoolean("transfer", "fsckobjects", false)); //$NON-NLS-1$ //$NON-NLS-2$
allowLeadingZeroFileMode = checkReceivedObjects allowLeadingZeroFileMode = checkReceivedObjects
&& rc.getBoolean("fsck", "allowLeadingZeroFileMode", false); //$NON-NLS-1$ //$NON-NLS-2$ && rc.getBoolean("fsck", "allowLeadingZeroFileMode", false); //$NON-NLS-1$ //$NON-NLS-2$
allowInvalidPersonIdent = checkReceivedObjects
&& rc.getBoolean("fsck", "allowInvalidPersonIdent", false); //$NON-NLS-1$ //$NON-NLS-2$
safeForWindows = checkReceivedObjects safeForWindows = checkReceivedObjects
&& rc.getBoolean("fsck", "safeForWindows", //$NON-NLS-1$ //$NON-NLS-2$ && rc.getBoolean("fsck", "safeForWindows", //$NON-NLS-1$ //$NON-NLS-2$
SystemReader.getInstance().isWindows()); SystemReader.getInstance().isWindows());
@ -113,6 +116,7 @@ public ObjectChecker newObjectChecker() {
return null; return null;
return new ObjectChecker() return new ObjectChecker()
.setAllowLeadingZeroFileMode(allowLeadingZeroFileMode) .setAllowLeadingZeroFileMode(allowLeadingZeroFileMode)
.setAllowInvalidPersonIdent(allowInvalidPersonIdent)
.setSafeForWindows(safeForWindows) .setSafeForWindows(safeForWindows)
.setSafeForMacOS(safeForMacOS); .setSafeForMacOS(safeForMacOS);
} }

View File

@ -134,8 +134,6 @@ public class TransportHttp extends HttpTransport implements WalkTransport,
private static final String SVC_RECEIVE_PACK = "git-receive-pack"; //$NON-NLS-1$ private static final String SVC_RECEIVE_PACK = "git-receive-pack"; //$NON-NLS-1$
private static final String userAgent = computeUserAgent();
static final TransportProtocol PROTO_HTTP = new TransportProtocol() { static final TransportProtocol PROTO_HTTP = new TransportProtocol() {
private final String[] schemeNames = { "http", "https" }; //$NON-NLS-1$ //$NON-NLS-2$ private final String[] schemeNames = { "http", "https" }; //$NON-NLS-1$ //$NON-NLS-2$
@ -204,17 +202,6 @@ public Transport open(URIish uri, Repository local, String remoteName)
} }
}; };
private static String computeUserAgent() {
String version;
final Package pkg = TransportHttp.class.getPackage();
if (pkg != null && pkg.getImplementationVersion() != null) {
version = pkg.getImplementationVersion();
} else {
version = "unknown"; //$NON-NLS-1$
}
return "JGit/" + version; //$NON-NLS-1$
}
private static final Config.SectionParser<HttpConfig> HTTP_KEY = new SectionParser<HttpConfig>() { private static final Config.SectionParser<HttpConfig> HTTP_KEY = new SectionParser<HttpConfig>() {
public HttpConfig parse(final Config cfg) { public HttpConfig parse(final Config cfg) {
return new HttpConfig(cfg); return new HttpConfig(cfg);
@ -309,16 +296,17 @@ public FetchConnection openFetch() throws TransportException,
final HttpConnection c = connect(service); final HttpConnection c = connect(service);
final InputStream in = openInputStream(c); final InputStream in = openInputStream(c);
try { try {
BaseConnection f;
if (isSmartHttp(c, service)) { if (isSmartHttp(c, service)) {
readSmartHeaders(in, service); readSmartHeaders(in, service);
return new SmartHttpFetchConnection(in); f = new SmartHttpFetchConnection(in);
} else { } else {
// Assume this server doesn't support smart HTTP fetch // Assume this server doesn't support smart HTTP fetch
// and fall back on dumb object walking. // and fall back on dumb object walking.
// f = newDumbConnection(in);
return newDumbConnection(in);
} }
f.setPeerUserAgent(c.getHeaderField(HttpSupport.HDR_SERVER));
return (FetchConnection) f;
} finally { } finally {
in.close(); in.close();
} }
@ -331,7 +319,7 @@ public FetchConnection openFetch() throws TransportException,
} }
} }
private FetchConnection newDumbConnection(InputStream in) private WalkFetchConnection newDumbConnection(InputStream in)
throws IOException, PackProtocolException { throws IOException, PackProtocolException {
HttpObjectDB d = new HttpObjectDB(objectsUrl); HttpObjectDB d = new HttpObjectDB(objectsUrl);
BufferedReader br = toBufferedReader(in); BufferedReader br = toBufferedReader(in);
@ -400,9 +388,7 @@ public PushConnection openPush() throws NotSupportedException,
final InputStream in = openInputStream(c); final InputStream in = openInputStream(c);
try { try {
if (isSmartHttp(c, service)) { if (isSmartHttp(c, service)) {
readSmartHeaders(in, service); return smartPush(service, c, in);
return new SmartHttpPushConnection(in);
} else if (!useSmartHttp) { } else if (!useSmartHttp) {
final String msg = JGitText.get().smartHTTPPushDisabled; final String msg = JGitText.get().smartHTTPPushDisabled;
throw new NotSupportedException(msg); throw new NotSupportedException(msg);
@ -423,6 +409,14 @@ public PushConnection openPush() throws NotSupportedException,
} }
} }
private PushConnection smartPush(String service, HttpConnection c,
InputStream in) throws IOException, TransportException {
readSmartHeaders(in, service);
SmartHttpPushConnection p = new SmartHttpPushConnection(in);
p.setPeerUserAgent(c.getHeaderField(HttpSupport.HDR_SERVER));
return p;
}
@Override @Override
public void close() { public void close() {
// No explicit connections are maintained. // No explicit connections are maintained.
@ -551,7 +545,9 @@ protected HttpConnection httpOpen(String method, URL u)
conn.setUseCaches(false); conn.setUseCaches(false);
conn.setRequestProperty(HDR_ACCEPT_ENCODING, ENCODING_GZIP); conn.setRequestProperty(HDR_ACCEPT_ENCODING, ENCODING_GZIP);
conn.setRequestProperty(HDR_PRAGMA, "no-cache"); //$NON-NLS-1$ conn.setRequestProperty(HDR_PRAGMA, "no-cache"); //$NON-NLS-1$
conn.setRequestProperty(HDR_USER_AGENT, userAgent); if (UserAgent.get() != null) {
conn.setRequestProperty(HDR_USER_AGENT, UserAgent.get());
}
int timeOut = getTimeout(); int timeOut = getTimeout();
if (timeOut != -1) { if (timeOut != -1) {
int effTimeOut = timeOut * 1000; int effTimeOut = timeOut * 1000;

View File

@ -44,6 +44,7 @@
package org.eclipse.jgit.transport; package org.eclipse.jgit.transport;
import static org.eclipse.jgit.lib.RefDatabase.ALL; import static org.eclipse.jgit.lib.RefDatabase.ALL;
import static org.eclipse.jgit.transport.GitProtocolConstants.OPTION_AGENT;
import static org.eclipse.jgit.transport.GitProtocolConstants.OPTION_ALLOW_TIP_SHA1_IN_WANT; import static org.eclipse.jgit.transport.GitProtocolConstants.OPTION_ALLOW_TIP_SHA1_IN_WANT;
import static org.eclipse.jgit.transport.GitProtocolConstants.OPTION_INCLUDE_TAG; import static org.eclipse.jgit.transport.GitProtocolConstants.OPTION_INCLUDE_TAG;
import static org.eclipse.jgit.transport.GitProtocolConstants.OPTION_MULTI_ACK; import static org.eclipse.jgit.transport.GitProtocolConstants.OPTION_MULTI_ACK;
@ -253,6 +254,7 @@ public Set<String> getOptions() {
/** Capabilities requested by the client. */ /** Capabilities requested by the client. */
private Set<String> options; private Set<String> options;
String userAgent;
/** Raw ObjectIds the client has asked for, before validating them. */ /** Raw ObjectIds the client has asked for, before validating them. */
private final Set<ObjectId> wantIds = new HashSet<ObjectId>(); private final Set<ObjectId> wantIds = new HashSet<ObjectId>();
@ -806,6 +808,7 @@ public void sendAdvertisedRefs(final RefAdvertiser adv) throws IOException,
|| policy == RequestPolicy.REACHABLE_COMMIT_TIP || policy == RequestPolicy.REACHABLE_COMMIT_TIP
|| policy == null) || policy == null)
adv.advertiseCapability(OPTION_ALLOW_TIP_SHA1_IN_WANT); adv.advertiseCapability(OPTION_ALLOW_TIP_SHA1_IN_WANT);
adv.advertiseCapability(OPTION_AGENT, UserAgent.get());
adv.setDerefTags(true); adv.setDerefTags(true);
Map<String, Ref> refs = getAdvertisedOrDefaultRefs(); Map<String, Ref> refs = getAdvertisedOrDefaultRefs();
findSymrefs(adv, refs); findSymrefs(adv, refs);
@ -884,6 +887,37 @@ private void recvWants() throws IOException {
} }
} }
/**
* Returns the clone/fetch depth. Valid only after calling recvWants().
*
* @return the depth requested by the client, or 0 if unbounded.
* @since 4.0
*/
public int getDepth() {
if (options == null)
throw new RequestNotYetReadException();
return depth;
}
/**
* Get the user agent of the client.
* <p>
* If the client is new enough to use {@code agent=} capability that value
* will be returned. Older HTTP clients may also supply their version using
* the HTTP {@code User-Agent} header. The capability overrides the HTTP
* header if both are available.
* <p>
* When an HTTP request has been received this method returns the HTTP
* {@code User-Agent} header value until capabilities have been parsed.
*
* @return user agent supplied by the client. Available only if the client
* is new enough to advertise its user agent.
* @since 4.0
*/
public String getPeerUserAgent() {
return UserAgent.getAgent(options, userAgent);
}
private boolean negotiate() throws IOException { private boolean negotiate() throws IOException {
okToGiveUp = Boolean.FALSE; okToGiveUp = Boolean.FALSE;

View File

@ -0,0 +1,147 @@
/*
* Copyright (C) 2015, 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.transport;
import static org.eclipse.jgit.transport.GitProtocolConstants.OPTION_AGENT;
import java.util.Set;
import org.eclipse.jgit.util.StringUtils;
/**
* User agent to be reported by this JGit client and server on the network.
* <p>
* On HTTP transports this user agent string is always supplied by the JGit
* client in the {@code User-Agent} HTTP header.
* <p>
* On native transports this user agent string is always sent when JGit is a
* server. When JGit is a client the user agent string will be supplied to the
* remote server only if the remote server advertises its own agent identity.
*
* @since 4.0
*/
public class UserAgent {
private static volatile String userAgent = computeUserAgent();
private static String computeUserAgent() {
return clean("JGit/" + computeVersion()); //$NON-NLS-1$
}
private static String computeVersion() {
Package pkg = UserAgent.class.getPackage();
if (pkg != null) {
String ver = pkg.getImplementationVersion();
if (!StringUtils.isEmptyOrNull(ver)) {
return ver;
}
}
return "unknown"; //$NON-NLS-1$
}
private static String clean(String s) {
s = s.trim();
StringBuilder b = new StringBuilder(s.length());
for (int i = 0; i < s.length(); i++) {
char c = s.charAt(i);
if (c <= 32 || c >= 127) {
if (b.length() > 0 && b.charAt(b.length() - 1) == '.')
continue;
c = '.';
}
b.append(c);
}
return b.length() > 0 ? b.toString() : null;
}
/**
* Get the user agent string advertised by JGit.
*
* @return a string similar to {@code "JGit/4.0"}; null if the agent has
* been cleared and should not be shared with a peer.
*/
public static String get() {
return userAgent;
}
/**
* Change the user agent string advertised by JGit.
* <p>
* The new string should start with {@code "JGit/"} (for example
* {@code "JGit/4.0"}) to advertise the implementation as JGit based.
* <p>
* Spaces and other whitespace should be avoided as these will be
* automatically converted to {@code "."}.
* <p>
* User agent strings are restricted to printable ASCII.
*
* @param agent
* new user agent string for this running JGit library. Setting
* to null or empty string will avoid sending any identification
* to the peer.
*/
public static void set(String agent) {
userAgent = StringUtils.isEmptyOrNull(agent) ? null : clean(agent);
}
static String getAgent(Set<String> options, String transportAgent) {
if (options == null || options.isEmpty()) {
return transportAgent;
}
for (String o : options) {
if (o.startsWith(OPTION_AGENT)
&& o.length() > OPTION_AGENT.length()
&& o.charAt(OPTION_AGENT.length()) == '=') {
return o.substring(OPTION_AGENT.length() + 1);
}
}
return transportAgent;
}
static boolean hasAgent(Set<String> options) {
return getAgent(options, null) != null;
}
private UserAgent() {
}
}

View File

@ -115,8 +115,10 @@ public static ObjectId computeChangeId(final ObjectId treeId,
b.append(committer.toExternalString()); b.append(committer.toExternalString());
b.append("\n\n"); //$NON-NLS-1$ b.append("\n\n"); //$NON-NLS-1$
b.append(cleanMessage); b.append(cleanMessage);
return new ObjectInserter.Formatter().idFor(Constants.OBJ_COMMIT, // try (ObjectInserter f = new ObjectInserter.Formatter()) {
b.toString().getBytes(Constants.CHARACTER_ENCODING)); return f.idFor(Constants.OBJ_COMMIT, //
b.toString().getBytes(Constants.CHARACTER_ENCODING));
}
} }
private static final Pattern issuePattern = Pattern private static final Pattern issuePattern = Pattern

View File

@ -481,7 +481,7 @@ public void run() {
} }
} }
} catch (IOException e) { } catch (IOException e) {
LOG.error("Caught exception in FS.readPipe()", e); //$NON-NLS-1$ LOG.debug("Caught exception in FS.readPipe()", e); //$NON-NLS-1$
} }
if (debug) { if (debug) {
LOG.debug("readpipe returns null"); //$NON-NLS-1$ LOG.debug("readpipe returns null"); //$NON-NLS-1$

View File

@ -74,6 +74,12 @@ public class HttpSupport {
/** The {@code User-Agent} header. */ /** The {@code User-Agent} header. */
public static final String HDR_USER_AGENT = "User-Agent"; //$NON-NLS-1$ public static final String HDR_USER_AGENT = "User-Agent"; //$NON-NLS-1$
/**
* The {@code Server} header.
* @since 4.0
*/
public static final String HDR_SERVER = "Server"; //$NON-NLS-1$
/** The {@code Date} header. */ /** The {@code Date} header. */
public static final String HDR_DATE = "Date"; //$NON-NLS-1$ public static final String HDR_DATE = "Date"; //$NON-NLS-1$

View File

@ -44,6 +44,7 @@
package org.eclipse.jgit.util; package org.eclipse.jgit.util;
import java.io.BufferedOutputStream;
import java.io.File; import java.io.File;
import java.io.FileInputStream; import java.io.FileInputStream;
import java.io.FileOutputStream; import java.io.FileOutputStream;
@ -435,7 +436,7 @@ public LocalFile(final File directory, final int inCoreLimit) {
protected OutputStream overflow() throws IOException { protected OutputStream overflow() throws IOException {
onDiskFile = File.createTempFile("jgit_", ".buf", directory); //$NON-NLS-1$ //$NON-NLS-2$ onDiskFile = File.createTempFile("jgit_", ".buf", directory); //$NON-NLS-1$ //$NON-NLS-2$
return new FileOutputStream(onDiskFile); return new BufferedOutputStream(new FileOutputStream(onDiskFile));
} }
public long length() { public long length() {

View File

@ -210,6 +210,10 @@
<id>repo.eclipse.org.cbi-releases</id> <id>repo.eclipse.org.cbi-releases</id>
<url>https://repo.eclipse.org/content/repositories/cbi-releases/</url> <url>https://repo.eclipse.org/content/repositories/cbi-releases/</url>
</pluginRepository> </pluginRepository>
<pluginRepository>
<id>repo.eclipse.org.cbi-snapshots</id>
<url>https://repo.eclipse.org/content/repositories/cbi-snapshots/</url>
</pluginRepository>
</pluginRepositories> </pluginRepositories>
<build> <build>
@ -346,7 +350,7 @@
<plugin> <plugin>
<groupId>org.eclipse.cbi.maven.plugins</groupId> <groupId>org.eclipse.cbi.maven.plugins</groupId>
<artifactId>eclipse-jarsigner-plugin</artifactId> <artifactId>eclipse-jarsigner-plugin</artifactId>
<version>1.1.1</version> <version>1.1.2-SNAPSHOT</version>
</plugin> </plugin>
<plugin> <plugin>
<groupId>org.eclipse.tycho.extras</groupId> <groupId>org.eclipse.tycho.extras</groupId>