Merge "Archive: Add the ability to select one or more paths."
diff --git a/org.eclipse.jgit.archive/META-INF/SOURCE-MANIFEST.MF b/org.eclipse.jgit.archive/META-INF/SOURCE-MANIFEST.MF
index c16455c..66c467d 100644
--- a/org.eclipse.jgit.archive/META-INF/SOURCE-MANIFEST.MF
+++ b/org.eclipse.jgit.archive/META-INF/SOURCE-MANIFEST.MF
@@ -1,7 +1,7 @@
Manifest-Version: 1.0
Bundle-ManifestVersion: 2
Bundle-Name: org.eclipse.jgit.archive - Sources
-Bundle-SymbolicName: org.eclipse.jgit.archive.source;singleton:=true
+Bundle-SymbolicName: org.eclipse.jgit.archive.source
Bundle-Vendor: Eclipse.org - JGit
Bundle-Version: 3.4.0.qualifier
-Eclipse-SourceBundle: org.eclipse.jgit.archive;version="3.4.0";roots="."
+Eclipse-SourceBundle: org.eclipse.jgit.archive;version="3.4.0.qualifier";roots="."
diff --git a/org.eclipse.jgit.java7/META-INF/SOURCE-MANIFEST.MF b/org.eclipse.jgit.java7/META-INF/SOURCE-MANIFEST.MF
index 51d3f50..02d41a3 100644
--- a/org.eclipse.jgit.java7/META-INF/SOURCE-MANIFEST.MF
+++ b/org.eclipse.jgit.java7/META-INF/SOURCE-MANIFEST.MF
@@ -1,8 +1,8 @@
Manifest-Version: 1.0
Bundle-ManifestVersion: 2
Bundle-Name: org.eclipse.jgit.java7 - Sources
-Bundle-SymbolicName: org.eclipse.jgit.java7.source;singleton:=true
+Bundle-SymbolicName: org.eclipse.jgit.java7.source
Bundle-Vendor: Eclipse.org - JGit
Bundle-Version: 3.4.0.qualifier
-Eclipse-SourceBundle: org.eclipse.jgit.java7;version="3.4.0";roots="."
+Eclipse-SourceBundle: org.eclipse.jgit.java7;version="3.4.0.qualifier";roots="."
diff --git a/org.eclipse.jgit.pgm.test/src/org/eclipse/jgit/lib/CLIRepositoryTestCase.java b/org.eclipse.jgit.pgm.test/src/org/eclipse/jgit/lib/CLIRepositoryTestCase.java
index a328bae..50ddfe0 100644
--- a/org.eclipse.jgit.pgm.test/src/org/eclipse/jgit/lib/CLIRepositoryTestCase.java
+++ b/org.eclipse.jgit.pgm.test/src/org/eclipse/jgit/lib/CLIRepositoryTestCase.java
@@ -81,6 +81,10 @@ protected File writeTrashFile(final String name, final String data)
return JGitTestUtil.writeTrashFile(db, name, data);
}
+ protected String read(final File file) throws IOException {
+ return JGitTestUtil.read(file);
+ }
+
protected void deleteTrashFile(final String name) throws IOException {
JGitTestUtil.deleteTrashFile(db, name);
}
diff --git a/org.eclipse.jgit.pgm.test/tst/org/eclipse/jgit/pgm/CheckoutTest.java b/org.eclipse.jgit.pgm.test/tst/org/eclipse/jgit/pgm/CheckoutTest.java
index 8012893..2c1f59f 100644
--- a/org.eclipse.jgit.pgm.test/tst/org/eclipse/jgit/pgm/CheckoutTest.java
+++ b/org.eclipse.jgit.pgm.test/tst/org/eclipse/jgit/pgm/CheckoutTest.java
@@ -42,6 +42,8 @@
*/
package org.eclipse.jgit.pgm;
+import static org.junit.Assert.assertArrayEquals;
+
import java.io.File;
import org.eclipse.jgit.api.Git;
@@ -191,4 +193,26 @@ static private void assertEquals(String expected, String[] actual) {
: actual.length);
Assert.assertEquals(expected, actual[0]);
}
+
+ @Test
+ public void testCheckoutPath() throws Exception {
+ Git git = new Git(db);
+ writeTrashFile("a", "Hello world a");
+ git.add().addFilepattern(".").call();
+ git.commit().setMessage("commit file a").call();
+ git.branchCreate().setName("branch_1").call();
+ git.checkout().setName("branch_1").call();
+ File b = writeTrashFile("b", "Hello world b");
+ git.add().addFilepattern("b").call();
+ git.commit().setMessage("commit file b").call();
+ File a = writeTrashFile("a", "New Hello world a");
+ git.add().addFilepattern(".").call();
+ git.commit().setMessage("modified a").call();
+ assertArrayEquals(new String[] { "" },
+ execute("git checkout HEAD~2 -- a"));
+ assertEquals("Hello world a", read(a));
+ assertArrayEquals(new String[] { "* branch_1", " master", "" },
+ execute("git branch"));
+ assertEquals("Hello world b", read(b));
+ }
}
diff --git a/org.eclipse.jgit.pgm.test/tst/org/eclipse/jgit/pgm/ConfigTest.java b/org.eclipse.jgit.pgm.test/tst/org/eclipse/jgit/pgm/ConfigTest.java
index 3c62e85..aefdff1 100644
--- a/org.eclipse.jgit.pgm.test/tst/org/eclipse/jgit/pgm/ConfigTest.java
+++ b/org.eclipse.jgit.pgm.test/tst/org/eclipse/jgit/pgm/ConfigTest.java
@@ -42,15 +42,15 @@
*/
package org.eclipse.jgit.pgm;
-import static org.junit.Assert.assertArrayEquals;
+import static org.junit.Assert.assertEquals;
import java.util.ArrayList;
+import java.util.Arrays;
import java.util.List;
-import java.util.regex.Matcher;
-import java.util.regex.Pattern;
import org.eclipse.jgit.api.Git;
import org.eclipse.jgit.lib.CLIRepositoryTestCase;
+import org.eclipse.jgit.util.FS;
import org.eclipse.jgit.util.SystemReader;
import org.junit.Before;
import org.junit.Test;
@@ -77,28 +77,12 @@ public void testListConfig() throws Exception {
if (isMac)
expect.add("core.precomposeunicode=true");
expect.add("core.repositoryformatversion=0");
- if (SystemReader.getInstance().isWindows() && osVersion() < 6
- || javaVersion() < 1.7) {
+ if (!FS.DETECTED.supportsSymlinks())
expect.add("core.symlinks=false");
- }
expect.add(""); // ends with LF (last line empty)
- assertArrayEquals("expected default configuration", expect.toArray(),
- output);
+ assertEquals("expected default configuration",
+ Arrays.asList(expect.toArray()).toString(),
+ Arrays.asList(output).toString());
}
- private static float javaVersion() {
- String versionString = System.getProperty("java.version");
- Matcher matcher = Pattern.compile("(\\d+\\.\\d+).*").matcher(
- versionString);
- matcher.matches();
- return Float.parseFloat(matcher.group(1));
- }
-
- private static float osVersion() {
- String versionString = System.getProperty("os.version");
- Matcher matcher = Pattern.compile("(\\d+\\.\\d+).*").matcher(
- versionString);
- matcher.matches();
- return Float.parseFloat(matcher.group(1));
- }
}
diff --git a/org.eclipse.jgit.pgm/META-INF/SOURCE-MANIFEST.MF b/org.eclipse.jgit.pgm/META-INF/SOURCE-MANIFEST.MF
index 7bf0c26..6d29757 100644
--- a/org.eclipse.jgit.pgm/META-INF/SOURCE-MANIFEST.MF
+++ b/org.eclipse.jgit.pgm/META-INF/SOURCE-MANIFEST.MF
@@ -1,7 +1,7 @@
Manifest-Version: 1.0
Bundle-ManifestVersion: 2
Bundle-Name: org.eclipse.jgit.pgm - Sources
-Bundle-SymbolicName: org.eclipse.jgit.pgm.source;singleton:=true
+Bundle-SymbolicName: org.eclipse.jgit.pgm.source
Bundle-Vendor: Eclipse.org - JGit
Bundle-Version: 3.4.0.qualifier
-Eclipse-SourceBundle: org.eclipse.jgit.pgm;version="3.4.0";roots="."
+Eclipse-SourceBundle: org.eclipse.jgit.pgm;version="3.4.0.qualifier";roots="."
diff --git a/org.eclipse.jgit.pgm/src/org/eclipse/jgit/pgm/Checkout.java b/org.eclipse.jgit.pgm/src/org/eclipse/jgit/pgm/Checkout.java
index 2f35ecb..8f911fd 100644
--- a/org.eclipse.jgit.pgm/src/org/eclipse/jgit/pgm/Checkout.java
+++ b/org.eclipse.jgit.pgm/src/org/eclipse/jgit/pgm/Checkout.java
@@ -45,6 +45,8 @@
package org.eclipse.jgit.pgm;
import java.text.MessageFormat;
+import java.util.ArrayList;
+import java.util.List;
import org.eclipse.jgit.api.CheckoutCommand;
import org.eclipse.jgit.api.Git;
@@ -58,6 +60,7 @@
import org.eclipse.jgit.pgm.internal.CLIText;
import org.kohsuke.args4j.Argument;
import org.kohsuke.args4j.Option;
+import org.kohsuke.args4j.spi.StopOptionHandler;
@Command(common = true, usage = "usage_checkout")
class Checkout extends TextBuiltin {
@@ -68,9 +71,13 @@ class Checkout extends TextBuiltin {
@Option(name = "--force", aliases = { "-f" }, usage = "usage_forceCheckout")
private boolean force = false;
- @Argument(required = true, metaVar = "metaVar_name", usage = "usage_checkout")
+ @Argument(required = true, index = 0, metaVar = "metaVar_name", usage = "usage_checkout")
private String name;
+ @Argument(index = 1)
+ @Option(name = "--", metaVar = "metaVar_paths", multiValued = true, handler = StopOptionHandler.class)
+ private List<String> paths = new ArrayList<String>();
+
@Override
protected void run() throws Exception {
if (createBranch) {
@@ -80,9 +87,15 @@ protected void run() throws Exception {
}
CheckoutCommand command = new Git(db).checkout();
- command.setCreateBranch(createBranch);
- command.setName(name);
- command.setForce(force);
+ if (paths.size() > 0) {
+ command.setStartPoint(name);
+ for (String path : paths)
+ command.addPath(path);
+ } else {
+ command.setCreateBranch(createBranch);
+ command.setName(name);
+ command.setForce(force);
+ }
try {
String oldBranch = db.getBranch();
Ref ref = command.call();
diff --git a/org.eclipse.jgit.pgm/src/org/eclipse/jgit/pgm/LsTree.java b/org.eclipse.jgit.pgm/src/org/eclipse/jgit/pgm/LsTree.java
index e444d21..4b16ed8 100644
--- a/org.eclipse.jgit.pgm/src/org/eclipse/jgit/pgm/LsTree.java
+++ b/org.eclipse.jgit.pgm/src/org/eclipse/jgit/pgm/LsTree.java
@@ -53,6 +53,7 @@
import org.eclipse.jgit.treewalk.AbstractTreeIterator;
import org.eclipse.jgit.treewalk.TreeWalk;
import org.eclipse.jgit.treewalk.filter.PathFilterGroup;
+import org.eclipse.jgit.util.QuotedString;
import org.kohsuke.args4j.Argument;
import org.kohsuke.args4j.Option;
import org.kohsuke.args4j.spi.StopOptionHandler;
@@ -90,7 +91,7 @@ protected void run() throws Exception {
outw.print(walk.getObjectId(0).name());
outw.print('\t');
- outw.print(walk.getPathString());
+ outw.print(QuotedString.GIT_PATH.quote(walk.getPathString()));
outw.println();
}
}
diff --git a/org.eclipse.jgit.test/tst/org/eclipse/jgit/gitrepo/RepoCommandTest.java b/org.eclipse.jgit.test/tst/org/eclipse/jgit/gitrepo/RepoCommandTest.java
index 27d3220..f6dea74 100644
--- a/org.eclipse.jgit.test/tst/org/eclipse/jgit/gitrepo/RepoCommandTest.java
+++ b/org.eclipse.jgit.test/tst/org/eclipse/jgit/gitrepo/RepoCommandTest.java
@@ -50,14 +50,20 @@
import java.io.File;
import java.io.FileReader;
+import org.eclipse.jgit.api.CloneCommand;
import org.eclipse.jgit.api.Git;
import org.eclipse.jgit.junit.JGitTestUtil;
import org.eclipse.jgit.junit.RepositoryTestCase;
+import org.eclipse.jgit.lib.Constants;
+import org.eclipse.jgit.lib.ObjectId;
import org.eclipse.jgit.lib.Repository;
import org.junit.Test;
public class RepoCommandTest extends RepositoryTestCase {
+ private static final String BRANCH = "branch";
+ private static final String TAG = "release";
+
private Repository defaultDb;
private Repository notDefaultDb;
private Repository groupADb;
@@ -69,14 +75,22 @@ public class RepoCommandTest extends RepositoryTestCase {
private String groupAUri;
private String groupBUri;
+ private ObjectId oldCommitId;
+
public void setUp() throws Exception {
super.setUp();
defaultDb = createWorkRepository();
Git git = new Git(defaultDb);
- JGitTestUtil.writeTrashFile(defaultDb, "hello.txt", "world");
+ JGitTestUtil.writeTrashFile(defaultDb, "hello.txt", "branch world");
git.add().addFilepattern("hello.txt").call();
- git.commit().setMessage("Initial commit").call();
+ oldCommitId = git.commit().setMessage("Initial commit").call().getId();
+ git.checkout().setName(BRANCH).setCreateBranch(true).call();
+ git.checkout().setName("master").call();
+ git.tag().setName(TAG).call();
+ JGitTestUtil.writeTrashFile(defaultDb, "hello.txt", "master world");
+ git.add().addFilepattern("hello.txt").call();
+ git.commit().setMessage("Second commit").call();
notDefaultDb = createWorkRepository();
git = new Git(notDefaultDb);
@@ -120,7 +134,7 @@ public void testAddRepoManifest() throws Exception {
BufferedReader reader = new BufferedReader(new FileReader(hello));
String content = reader.readLine();
reader.close();
- assertEquals("submodule content is as expected.", "world", content);
+ assertEquals("submodule content is as expected.", "master world", content);
}
@Test
@@ -179,7 +193,7 @@ public void testRepoManifestGroups() throws Exception {
}
@Test
- public void testRepoManifestCopyfile() throws Exception {
+ public void testRepoManifestCopyFile() throws Exception {
Repository localDb = createWorkRepository();
StringBuilder xmlContent = new StringBuilder();
xmlContent.append("<?xml version=\"1.0\" encoding=\"UTF-8\"?>\n")
@@ -203,14 +217,198 @@ public void testRepoManifestCopyfile() throws Exception {
BufferedReader reader = new BufferedReader(new FileReader(hello));
String content = reader.readLine();
reader.close();
- assertEquals("The original file has expected content", "world", content);
+ assertEquals("The original file has expected content", "master world", content);
// The dest file should also exist
hello = new File(localDb.getWorkTree(), "Hello");
assertTrue("The destination file exists", hello.exists());
reader = new BufferedReader(new FileReader(hello));
content = reader.readLine();
reader.close();
- assertEquals("The destination file has expected content", "world", content);
+ assertEquals("The destination file has expected content", "master world", content);
+ }
+
+ @Test
+ public void testBareRepo() throws Exception {
+ Repository remoteDb = createBareRepository();
+ Repository tempDb = createWorkRepository();
+ StringBuilder xmlContent = new StringBuilder();
+ xmlContent.append("<?xml version=\"1.0\" encoding=\"UTF-8\"?>\n")
+ .append("<manifest>")
+ .append("<remote name=\"remote1\" fetch=\".\" />")
+ .append("<default revision=\"master\" remote=\"remote1\" />")
+ .append("<project path=\"foo\" name=\"")
+ .append(defaultUri)
+ .append("\" />")
+ .append("</manifest>");
+ JGitTestUtil.writeTrashFile(tempDb, "manifest.xml", xmlContent.toString());
+ RepoCommand command = new RepoCommand(remoteDb);
+ command.setPath(tempDb.getWorkTree().getAbsolutePath() + "/manifest.xml")
+ .setURI(rootUri)
+ .call();
+ // Clone it
+ File directory = createTempDirectory("testBareRepo");
+ CloneCommand clone = Git.cloneRepository();
+ clone.setDirectory(directory);
+ clone.setURI(remoteDb.getDirectory().toURI().toString());
+ Repository localDb = clone.call().getRepository();
+ // The .gitmodules file should exist
+ File gitmodules = new File(localDb.getWorkTree(), ".gitmodules");
+ assertTrue("The .gitmodules file exists", gitmodules.exists());
+ // The first line of .gitmodules file should be expected
+ BufferedReader reader = new BufferedReader(new FileReader(gitmodules));
+ String content = reader.readLine();
+ reader.close();
+ assertEquals("The first line of .gitmodules file is as expected.",
+ "[submodule \"foo\"]", content);
+ // The gitlink should be the same as remote head sha1
+ String gitlink = localDb.resolve(Constants.HEAD + ":foo").name();
+ String remote = defaultDb.resolve(Constants.HEAD).name();
+ assertEquals("The gitlink is same as remote head", remote, gitlink);
+ }
+
+ @Test
+ public void testRevision() throws Exception {
+ StringBuilder xmlContent = new StringBuilder();
+ xmlContent.append("<?xml version=\"1.0\" encoding=\"UTF-8\"?>\n")
+ .append("<manifest>")
+ .append("<remote name=\"remote1\" fetch=\".\" />")
+ .append("<default revision=\"master\" remote=\"remote1\" />")
+ .append("<project path=\"foo\" name=\"")
+ .append(defaultUri)
+ .append("\" revision=\"")
+ .append(oldCommitId.name())
+ .append("\" />")
+ .append("</manifest>");
+ writeTrashFile("manifest.xml", xmlContent.toString());
+ RepoCommand command = new RepoCommand(db);
+ command.setPath(db.getWorkTree().getAbsolutePath() + "/manifest.xml")
+ .setURI(rootUri)
+ .call();
+ File hello = new File(db.getWorkTree(), "foo/hello.txt");
+ BufferedReader reader = new BufferedReader(new FileReader(hello));
+ String content = reader.readLine();
+ reader.close();
+ assertEquals("submodule content is as expected.", "branch world", content);
+ }
+
+ @Test
+ public void testRevisionBranch() throws Exception {
+ StringBuilder xmlContent = new StringBuilder();
+ xmlContent.append("<?xml version=\"1.0\" encoding=\"UTF-8\"?>\n")
+ .append("<manifest>")
+ .append("<remote name=\"remote1\" fetch=\".\" />")
+ .append("<default revision=\"")
+ .append(BRANCH)
+ .append("\" remote=\"remote1\" />")
+ .append("<project path=\"foo\" name=\"")
+ .append(defaultUri)
+ .append("\" />")
+ .append("</manifest>");
+ writeTrashFile("manifest.xml", xmlContent.toString());
+ RepoCommand command = new RepoCommand(db);
+ command.setPath(db.getWorkTree().getAbsolutePath() + "/manifest.xml")
+ .setURI(rootUri)
+ .call();
+ File hello = new File(db.getWorkTree(), "foo/hello.txt");
+ BufferedReader reader = new BufferedReader(new FileReader(hello));
+ String content = reader.readLine();
+ reader.close();
+ assertEquals("submodule content is as expected.", "branch world", content);
+ }
+
+ @Test
+ public void testRevisionTag() throws Exception {
+ StringBuilder xmlContent = new StringBuilder();
+ xmlContent.append("<?xml version=\"1.0\" encoding=\"UTF-8\"?>\n")
+ .append("<manifest>")
+ .append("<remote name=\"remote1\" fetch=\".\" />")
+ .append("<default revision=\"master\" remote=\"remote1\" />")
+ .append("<project path=\"foo\" name=\"")
+ .append(defaultUri)
+ .append("\" revision=\"")
+ .append(TAG)
+ .append("\" />")
+ .append("</manifest>");
+ writeTrashFile("manifest.xml", xmlContent.toString());
+ RepoCommand command = new RepoCommand(db);
+ command.setPath(db.getWorkTree().getAbsolutePath() + "/manifest.xml")
+ .setURI(rootUri)
+ .call();
+ File hello = new File(db.getWorkTree(), "foo/hello.txt");
+ BufferedReader reader = new BufferedReader(new FileReader(hello));
+ String content = reader.readLine();
+ reader.close();
+ assertEquals("submodule content is as expected.", "branch world", content);
+ }
+
+ @Test
+ public void testRevisionBare() throws Exception {
+ Repository remoteDb = createBareRepository();
+ Repository tempDb = createWorkRepository();
+ StringBuilder xmlContent = new StringBuilder();
+ xmlContent.append("<?xml version=\"1.0\" encoding=\"UTF-8\"?>\n")
+ .append("<manifest>")
+ .append("<remote name=\"remote1\" fetch=\".\" />")
+ .append("<default revision=\"")
+ .append(BRANCH)
+ .append("\" remote=\"remote1\" />")
+ .append("<project path=\"foo\" name=\"")
+ .append(defaultUri)
+ .append("\" />")
+ .append("</manifest>");
+ JGitTestUtil.writeTrashFile(tempDb, "manifest.xml", xmlContent.toString());
+ RepoCommand command = new RepoCommand(remoteDb);
+ command.setPath(tempDb.getWorkTree().getAbsolutePath() + "/manifest.xml")
+ .setURI(rootUri)
+ .call();
+ // Clone it
+ File directory = createTempDirectory("testRevisionBare");
+ CloneCommand clone = Git.cloneRepository();
+ clone.setDirectory(directory);
+ clone.setURI(remoteDb.getDirectory().toURI().toString());
+ Repository localDb = clone.call().getRepository();
+ // The gitlink should be the same as oldCommitId
+ String gitlink = localDb.resolve(Constants.HEAD + ":foo").name();
+ assertEquals("The gitlink is same as remote head",
+ oldCommitId.name(), gitlink);
+ }
+
+ @Test
+ public void testCopyFileBare() throws Exception {
+ Repository remoteDb = createBareRepository();
+ Repository tempDb = createWorkRepository();
+ StringBuilder xmlContent = new StringBuilder();
+ xmlContent.append("<?xml version=\"1.0\" encoding=\"UTF-8\"?>\n")
+ .append("<manifest>")
+ .append("<remote name=\"remote1\" fetch=\".\" />")
+ .append("<default revision=\"master\" remote=\"remote1\" />")
+ .append("<project path=\"foo\" name=\"")
+ .append(defaultUri)
+ .append("\" revision=\"")
+ .append(BRANCH)
+ .append("\" >")
+ .append("<copyfile src=\"hello.txt\" dest=\"Hello\" />")
+ .append("</project>")
+ .append("</manifest>");
+ JGitTestUtil.writeTrashFile(tempDb, "manifest.xml", xmlContent.toString());
+ RepoCommand command = new RepoCommand(remoteDb);
+ command.setPath(tempDb.getWorkTree().getAbsolutePath() + "/manifest.xml")
+ .setURI(rootUri)
+ .call();
+ // Clone it
+ File directory = createTempDirectory("testCopyFileBare");
+ CloneCommand clone = Git.cloneRepository();
+ clone.setDirectory(directory);
+ clone.setURI(remoteDb.getDirectory().toURI().toString());
+ Repository localDb = clone.call().getRepository();
+ // The Hello file should exist
+ File hello = new File(localDb.getWorkTree(), "Hello");
+ assertTrue("The Hello file exists", hello.exists());
+ // The content of Hello file should be expected
+ BufferedReader reader = new BufferedReader(new FileReader(hello));
+ String content = reader.readLine();
+ reader.close();
+ assertEquals("The Hello file has expected content", "branch world", content);
}
private void resolveRelativeUris() {
diff --git a/org.eclipse.jgit/META-INF/SOURCE-MANIFEST.MF b/org.eclipse.jgit/META-INF/SOURCE-MANIFEST.MF
index 3a2e556..dbbbbb0 100644
--- a/org.eclipse.jgit/META-INF/SOURCE-MANIFEST.MF
+++ b/org.eclipse.jgit/META-INF/SOURCE-MANIFEST.MF
@@ -1,7 +1,7 @@
Manifest-Version: 1.0
Bundle-ManifestVersion: 2
Bundle-Name: org.eclipse.jgit - Sources
-Bundle-SymbolicName: org.eclipse.jgit.source;singleton:=true
+Bundle-SymbolicName: org.eclipse.jgit.source
Bundle-Vendor: Eclipse.org - JGit
Bundle-Version: 3.4.0.qualifier
-Eclipse-SourceBundle: org.eclipse.jgit;version="3.4.0";roots="."
+Eclipse-SourceBundle: org.eclipse.jgit;version="3.4.0.qualifier";roots="."
diff --git a/org.eclipse.jgit/resources/org/eclipse/jgit/gitrepo/internal/RepoText.properties b/org.eclipse.jgit/resources/org/eclipse/jgit/gitrepo/internal/RepoText.properties
index 29aa51c..33fdc2e 100644
--- a/org.eclipse.jgit/resources/org/eclipse/jgit/gitrepo/internal/RepoText.properties
+++ b/org.eclipse.jgit/resources/org/eclipse/jgit/gitrepo/internal/RepoText.properties
@@ -1,5 +1,6 @@
copyFileFailed=Error occurred during execution of copyfile rule.
errorNoDefault=Error: no default remote in file {0}.
errorParsingManifestFile=Error occurred during parsing manifest file {0}.
+errorRemoteUnavailable=Error remote {0} is unavailable.
invalidManifest=Invalid manifest.
repoCommitMessage=Added repo manifest.
diff --git a/org.eclipse.jgit/resources/org/eclipse/jgit/internal/JGitText.properties b/org.eclipse.jgit/resources/org/eclipse/jgit/internal/JGitText.properties
index bb95fa8..b3049b0 100644
--- a/org.eclipse.jgit/resources/org/eclipse/jgit/internal/JGitText.properties
+++ b/org.eclipse.jgit/resources/org/eclipse/jgit/internal/JGitText.properties
@@ -51,6 +51,7 @@
cannotCreateDirectory=Cannot create directory {0}
cannotCreateHEAD=cannot create HEAD
cannotCreateIndexfile=Cannot create an index file with name {0}
+cannotCreateTempDir=Cannot create a temp dir
cannotDeleteCheckedOutBranch=Branch {0} is checked out and can not be deleted
cannotDeleteFile=Cannot delete file: {0}
cannotDeleteStaleTrackingRef=Cannot delete stale tracking ref {0}
diff --git a/org.eclipse.jgit/src/org/eclipse/jgit/api/RebaseCommand.java b/org.eclipse.jgit/src/org/eclipse/jgit/api/RebaseCommand.java
index 95551c3..8277ead 100644
--- a/org.eclipse.jgit/src/org/eclipse/jgit/api/RebaseCommand.java
+++ b/org.eclipse.jgit/src/org/eclipse/jgit/api/RebaseCommand.java
@@ -145,6 +145,8 @@ public class RebaseCommand extends GitCommand<RebaseResult> {
private static final String INTERACTIVE = "interactive"; //$NON-NLS-1$
+ private static final String QUIET = "quiet"; //$NON-NLS-1$
+
private static final String MESSAGE = "message"; //$NON-NLS-1$
private static final String ONTO = "onto"; //$NON-NLS-1$
@@ -928,6 +930,7 @@ else if (!isInteractive() && walk.isMergedInto(headCommit, upstream)) {
rebaseState.createFile(ONTO, upstreamCommit.name());
rebaseState.createFile(ONTO_NAME, upstreamCommitName);
rebaseState.createFile(INTERACTIVE, ""); //$NON-NLS-1$
+ rebaseState.createFile(QUIET, ""); //$NON-NLS-1$
ArrayList<RebaseTodoLine> toDoSteps = new ArrayList<RebaseTodoLine>();
toDoSteps.add(new RebaseTodoLine("# Created by EGit: rebasing " + headId.name() //$NON-NLS-1$
diff --git a/org.eclipse.jgit/src/org/eclipse/jgit/api/StashDropCommand.java b/org.eclipse.jgit/src/org/eclipse/jgit/api/StashDropCommand.java
index e4431c3..6cbcd06 100644
--- a/org.eclipse.jgit/src/org/eclipse/jgit/api/StashDropCommand.java
+++ b/org.eclipse.jgit/src/org/eclipse/jgit/api/StashDropCommand.java
@@ -219,7 +219,7 @@ public ObjectId call() throws GitAPIException {
FileUtils.delete(stashFile);
if (!stashLockFile.renameTo(stashFile))
throw new JGitInternalException(MessageFormat.format(
- JGitText.get().couldNotWriteFile,
+ JGitText.get().renameFileFailed,
stashLockFile.getPath(), stashFile.getPath()));
}
} catch (IOException e) {
diff --git a/org.eclipse.jgit/src/org/eclipse/jgit/dircache/DirCacheCheckout.java b/org.eclipse.jgit/src/org/eclipse/jgit/dircache/DirCacheCheckout.java
index 5275b4c..fdf8c05 100644
--- a/org.eclipse.jgit/src/org/eclipse/jgit/dircache/DirCacheCheckout.java
+++ b/org.eclipse.jgit/src/org/eclipse/jgit/dircache/DirCacheCheckout.java
@@ -1154,7 +1154,7 @@ public static void checkoutEntry(final Repository repo, File f,
FileUtils.rename(tmpFile, f);
} catch (IOException e) {
throw new IOException(MessageFormat.format(
- JGitText.get().couldNotWriteFile, tmpFile.getPath(),
+ JGitText.get().renameFileFailed, tmpFile.getPath(),
f.getPath()));
}
}
diff --git a/org.eclipse.jgit/src/org/eclipse/jgit/gitrepo/RepoCommand.java b/org.eclipse.jgit/src/org/eclipse/jgit/gitrepo/RepoCommand.java
index 475fbac..e90abb5 100644
--- a/org.eclipse.jgit/src/org/eclipse/jgit/gitrepo/RepoCommand.java
+++ b/org.eclipse.jgit/src/org/eclipse/jgit/gitrepo/RepoCommand.java
@@ -42,6 +42,7 @@
*/
package org.eclipse.jgit.gitrepo;
+import java.io.File;
import java.io.FileInputStream;
import java.io.FileOutputStream;
import java.io.IOException;
@@ -51,22 +52,41 @@
import java.text.MessageFormat;
import java.util.ArrayList;
import java.util.Arrays;
+import java.util.Collection;
import java.util.HashMap;
import java.util.HashSet;
import java.util.List;
import java.util.Map;
import java.util.Set;
-import org.eclipse.jgit.api.AddCommand;
import org.eclipse.jgit.api.Git;
import org.eclipse.jgit.api.GitCommand;
import org.eclipse.jgit.api.SubmoduleAddCommand;
+import org.eclipse.jgit.api.errors.ConcurrentRefUpdateException;
import org.eclipse.jgit.api.errors.GitAPIException;
+import org.eclipse.jgit.api.errors.JGitInternalException;
+import org.eclipse.jgit.dircache.DirCache;
+import org.eclipse.jgit.dircache.DirCacheBuilder;
+import org.eclipse.jgit.dircache.DirCacheEntry;
import org.eclipse.jgit.gitrepo.internal.RepoText;
import org.eclipse.jgit.internal.JGitText;
+import org.eclipse.jgit.lib.CommitBuilder;
+import org.eclipse.jgit.lib.Config;
+import org.eclipse.jgit.lib.Constants;
+import org.eclipse.jgit.lib.FileMode;
+import org.eclipse.jgit.lib.ObjectId;
+import org.eclipse.jgit.lib.ObjectInserter;
+import org.eclipse.jgit.lib.ObjectReader;
+import org.eclipse.jgit.lib.PersonIdent;
import org.eclipse.jgit.lib.ProgressMonitor;
+import org.eclipse.jgit.lib.Ref;
+import org.eclipse.jgit.lib.RefDatabase;
+import org.eclipse.jgit.lib.RefUpdate;
+import org.eclipse.jgit.lib.RefUpdate.Result;
import org.eclipse.jgit.lib.Repository;
import org.eclipse.jgit.revwalk.RevCommit;
+import org.eclipse.jgit.revwalk.RevWalk;
+import org.eclipse.jgit.util.FileUtils;
import org.xml.sax.Attributes;
import org.xml.sax.InputSource;
@@ -89,25 +109,114 @@ public class RepoCommand extends GitCommand<RevCommit> {
private String path;
private String uri;
private String groups;
+ private String branch;
+ private PersonIdent author;
+ private RemoteReader callback;
+ private List<Project> bareProjects;
private Git git;
private ProgressMonitor monitor;
+ /**
+ * A callback to get ref sha1 of a repository from its uri.
+ *
+ * We provided a default implementation {@link DefaultRemoteReader} to
+ * use ls-remote command to read the sha1 from the repository and clone the
+ * repository to read the file. Callers may have their own quicker
+ * implementation.
+ *
+ * @since 3.4
+ */
+ public interface RemoteReader {
+ /**
+ * Read a remote ref sha1.
+ *
+ * @param uri
+ * The URI of the remote repository
+ * @param ref
+ * The ref (branch/tag/etc.) to read
+ * @return the sha1 of the remote repository
+ * @throws GitAPIException
+ */
+ public ObjectId sha1(String uri, String ref) throws GitAPIException;
+
+ /**
+ * Read a file from a remote repository.
+ *
+ * @param uri
+ * The URI of the remote repository
+ * @param ref
+ * The ref (branch/tag/etc.) to read
+ * @param path
+ * The relative path (inside the repo) to the file to read
+ * @return the file content.
+ * @throws GitAPIException
+ * @throws IOException
+ */
+ public byte[] readFile(String uri, String ref, String path)
+ throws GitAPIException, IOException;
+ }
+
+ /** A default implementation of {@link RemoteReader} callback. */
+ public static class DefaultRemoteReader implements RemoteReader {
+ public ObjectId sha1(String uri, String ref) throws GitAPIException {
+ Collection<Ref> refs = Git
+ .lsRemoteRepository()
+ .setRemote(uri)
+ .call();
+ // Since LsRemoteCommand.call() only returned Map.values() to us, we
+ // have to rebuild the map here.
+ Map<String, Ref> map = new HashMap<String, Ref>(refs.size());
+ for (Ref r : refs)
+ map.put(r.getName(), r);
+
+ Ref r = RefDatabase.findRef(map, ref);
+ return r != null ? r.getObjectId() : null;
+ }
+
+ public byte[] readFile(String uri, String ref, String path)
+ throws GitAPIException, IOException {
+ File dir = FileUtils.createTempDir("jgit_", ".git", null); //$NON-NLS-1$ //$NON-NLS-2$
+ Repository repo = Git
+ .cloneRepository()
+ .setBare(true)
+ .setDirectory(dir)
+ .setURI(uri)
+ .call()
+ .getRepository();
+ ObjectReader reader = repo.newObjectReader();
+ byte[] result;
+ try {
+ ObjectId oid = repo.resolve(ref + ":" + path); //$NON-NLS-1$
+ result = reader.open(oid).getBytes(Integer.MAX_VALUE);
+ } finally {
+ reader.release();
+ FileUtils.delete(dir, FileUtils.RECURSIVE);
+ }
+ return result;
+ }
+ }
+
private static class CopyFile {
+ final Repository repo;
+ final String path;
final String src;
final String dest;
- final String relativeDest;
CopyFile(Repository repo, String path, String src, String dest) {
- this.src = repo.getWorkTree() + "/" + path + "/" + src; //$NON-NLS-1$ //$NON-NLS-2$
- this.relativeDest = dest;
- this.dest = repo.getWorkTree() + "/" + dest; //$NON-NLS-1$
+ this.repo = repo;
+ this.path = path;
+ this.src = src;
+ this.dest = dest;
}
void copy() throws IOException {
- FileInputStream input = new FileInputStream(src);
+ File srcFile = new File(repo.getWorkTree(),
+ path + "/" + src); //$NON-NLS-1$
+ File destFile = new File(repo.getWorkTree(), dest);
+ FileInputStream input = new FileInputStream(srcFile);
try {
- FileOutputStream output = new FileOutputStream(dest);
+ FileOutputStream output = new FileOutputStream(destFile);
try {
FileChannel channel = input.getChannel();
output.getChannel().transferFrom(channel, 0, channel.size());
@@ -123,12 +232,14 @@ void copy() throws IOException {
private static class Project {
final String name;
final String path;
+ final String revision;
final Set<String> groups;
final List<CopyFile> copyfiles;
- Project(String name, String path, String groups) {
+ Project(String name, String path, String revision, String groups) {
this.name = name;
this.path = path;
+ this.revision = revision;
this.groups = new HashSet<String>();
if (groups != null && groups.length() > 0)
this.groups.addAll(Arrays.asList(groups.split(","))); //$NON-NLS-1$
@@ -149,6 +260,7 @@ private static class XmlManifest extends DefaultHandler {
private final Set<String> plusGroups;
private final Set<String> minusGroups;
private String defaultRemote;
+ private String defaultRevision;
private Project currentProject;
XmlManifest(RepoCommand command, String filename, String baseUrl, String groups) {
@@ -203,12 +315,16 @@ public void startElement(
currentProject = new Project( //$NON-NLS-1$
attributes.getValue("name"), //$NON-NLS-1$
attributes.getValue("path"), //$NON-NLS-1$
+ attributes.getValue("revision"), //$NON-NLS-1$
attributes.getValue("groups")); //$NON-NLS-1$
} else if ("remote".equals(qName)) { //$NON-NLS-1$
remotes.put(attributes.getValue("name"), //$NON-NLS-1$
attributes.getValue("fetch")); //$NON-NLS-1$
} else if ("default".equals(qName)) { //$NON-NLS-1$
defaultRemote = attributes.getValue("remote"); //$NON-NLS-1$
+ defaultRevision = attributes.getValue("revision"); //$NON-NLS-1$
+ if (defaultRevision == null)
+ defaultRevision = command.branch;
} else if ("copyfile".equals(qName)) { //$NON-NLS-1$
if (currentProject == null)
throw new SAXException(RepoText.get().invalidManifest);
@@ -246,24 +362,11 @@ public void endDocument() throws SAXException {
}
for (Project proj : projects) {
if (inGroups(proj)) {
- String url = remoteUrl + proj.name;
- command.addSubmodule(url, proj.path);
- for (CopyFile copyfile : proj.copyfiles) {
- try {
- copyfile.copy();
- } catch (IOException e) {
- throw new SAXException(
- RepoText.get().copyFileFailed, e);
- }
- AddCommand add = command.git
- .add()
- .addFilepattern(copyfile.relativeDest);
- try {
- add.call();
- } catch (GitAPIException e) {
- throw new SAXException(e);
- }
- }
+ command.addSubmodule(remoteUrl + proj.name,
+ proj.path,
+ proj.revision == null ?
+ defaultRevision : proj.revision,
+ proj.copyfiles);
}
}
}
@@ -293,6 +396,12 @@ private static class ManifestErrorException extends GitAPIException {
}
}
+ private static class RemoteUnavailableException extends GitAPIException {
+ RemoteUnavailableException(String uri) {
+ super(MessageFormat.format(RepoText.get().errorRemoteUnavailable, uri));
+ }
+ }
+
/**
* @param repo
*/
@@ -335,6 +444,21 @@ public RepoCommand setGroups(final String groups) {
}
/**
+ * Set default branch.
+ *
+ * This is generally the name of the branch the manifest file was in. If
+ * there's no default revision (branch) specified in manifest and no
+ * revision specified in project, this branch will be used.
+ *
+ * @param branch
+ * @return this command
+ */
+ public RepoCommand setBranch(final String branch) {
+ this.branch = branch;
+ return this;
+ }
+
+ /**
* The progress monitor associated with the clone operation. By default,
* this is set to <code>NullProgressMonitor</code>
*
@@ -347,6 +471,32 @@ public RepoCommand setProgressMonitor(final ProgressMonitor monitor) {
return this;
}
+ /**
+ * Set the author/committer for the bare repository commit.
+ *
+ * For non-bare repositories, the current user will be used and this will be ignored.
+ *
+ * @param author
+ * @return this command
+ */
+ public RepoCommand setAuthor(final PersonIdent author) {
+ this.author = author;
+ return this;
+ }
+
+ /**
+ * Set the GetHeadFromUri callback.
+ *
+ * This is only used in bare repositories.
+ *
+ * @param callback
+ * @return this command
+ */
+ public RepoCommand setRemoteReader(final RemoteReader callback) {
+ this.callback = callback;
+ return this;
+ }
+
@Override
public RevCommit call() throws GitAPIException {
checkCallable();
@@ -355,7 +505,15 @@ public RevCommit call() throws GitAPIException {
if (uri == null || uri.length() == 0)
throw new IllegalArgumentException(JGitText.get().uriNotConfigured);
- git = new Git(repo);
+ if (repo.isBare()) {
+ bareProjects = new ArrayList<Project>();
+ if (author == null)
+ author = new PersonIdent(repo);
+ if (callback == null)
+ callback = new DefaultRemoteReader();
+ } else
+ git = new Git(repo);
+
XmlManifest manifest = new XmlManifest(this, path, uri, groups);
try {
manifest.read();
@@ -363,23 +521,146 @@ public RevCommit call() throws GitAPIException {
throw new ManifestErrorException(e);
}
- return git
- .commit()
- .setMessage(RepoText.get().repoCommitMessage)
- .call();
+ if (repo.isBare()) {
+ DirCache index = DirCache.newInCore();
+ DirCacheBuilder builder = index.builder();
+ ObjectInserter inserter = repo.newObjectInserter();
+ RevWalk rw = new RevWalk(repo);
+
+ try {
+ Config cfg = new Config();
+ for (Project proj : bareProjects) {
+ String name = proj.path;
+ String uri = proj.name;
+ cfg.setString("submodule", name, "path", name); //$NON-NLS-1$ //$NON-NLS-2$
+ cfg.setString("submodule", name, "url", uri); //$NON-NLS-1$ //$NON-NLS-2$
+ // create gitlink
+ DirCacheEntry dcEntry = new DirCacheEntry(name);
+ ObjectId objectId;
+ if (ObjectId.isId(proj.revision))
+ objectId = ObjectId.fromString(proj.revision);
+ else {
+ objectId = callback.sha1(uri, proj.revision);
+ }
+ if (objectId == null)
+ throw new RemoteUnavailableException(uri);
+ dcEntry.setObjectId(objectId);
+ dcEntry.setFileMode(FileMode.GITLINK);
+ builder.add(dcEntry);
+
+ for (CopyFile copyfile : proj.copyfiles) {
+ byte[] src = callback.readFile(
+ uri, proj.revision, copyfile.src);
+ objectId = inserter.insert(Constants.OBJ_BLOB, src);
+ dcEntry = new DirCacheEntry(copyfile.dest);
+ dcEntry.setObjectId(objectId);
+ dcEntry.setFileMode(FileMode.REGULAR_FILE);
+ builder.add(dcEntry);
+ }
+ }
+ String content = cfg.toText();
+
+ // create a new DirCacheEntry for .gitmodules file.
+ final DirCacheEntry dcEntry = new DirCacheEntry(Constants.DOT_GIT_MODULES);
+ ObjectId objectId = inserter.insert(Constants.OBJ_BLOB,
+ content.getBytes(Constants.CHARACTER_ENCODING));
+ dcEntry.setObjectId(objectId);
+ dcEntry.setFileMode(FileMode.REGULAR_FILE);
+ builder.add(dcEntry);
+
+ builder.finish();
+ ObjectId treeId = index.writeTree(inserter);
+
+ // Create a Commit object, populate it and write it
+ ObjectId headId = repo.resolve(Constants.HEAD + "^{commit}"); //$NON-NLS-1$
+ CommitBuilder commit = new CommitBuilder();
+ commit.setTreeId(treeId);
+ if (headId != null)
+ commit.setParentIds(headId);
+ commit.setAuthor(author);
+ commit.setCommitter(author);
+ commit.setMessage(RepoText.get().repoCommitMessage);
+
+ ObjectId commitId = inserter.insert(commit);
+ inserter.flush();
+
+ RefUpdate ru = repo.updateRef(Constants.HEAD);
+ ru.setNewObjectId(commitId);
+ ru.setExpectedOldObjectId(headId != null ? headId : ObjectId.zeroId());
+ Result rc = ru.update(rw);
+
+ switch (rc) {
+ case NEW:
+ case FORCED:
+ case FAST_FORWARD:
+ // Successful. Do nothing.
+ break;
+ 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.name(), rc));
+ }
+
+ return rw.parseCommit(commitId);
+ } catch (IOException e) {
+ throw new ManifestErrorException(e);
+ } finally {
+ rw.release();
+ }
+ } else {
+ return git
+ .commit()
+ .setMessage(RepoText.get().repoCommitMessage)
+ .call();
+ }
}
- private void addSubmodule(String url, String name) throws SAXException {
- SubmoduleAddCommand add = git
- .submoduleAdd()
- .setPath(name)
- .setURI(url);
- if (monitor != null)
- add.setProgressMonitor(monitor);
- try {
- add.call();
- } catch (GitAPIException e) {
- throw new SAXException(e);
+ private void addSubmodule(String url, String name, String revision,
+ List<CopyFile> copyfiles) throws SAXException {
+ if (repo.isBare()) {
+ Project proj = new Project(url, name, revision, null);
+ proj.copyfiles.addAll(copyfiles);
+ bareProjects.add(proj);
+ } else {
+ SubmoduleAddCommand add = git
+ .submoduleAdd()
+ .setPath(name)
+ .setURI(url);
+ if (monitor != null)
+ add.setProgressMonitor(monitor);
+
+ try {
+ Repository subRepo = add.call();
+ if (revision != null) {
+ Git sub = new Git(subRepo);
+ sub.checkout().setName(findRef(revision, subRepo)).call();
+ git.add().addFilepattern(name).call();
+ }
+ for (CopyFile copyfile : copyfiles) {
+ copyfile.copy();
+ git.add().addFilepattern(copyfile.dest).call();
+ }
+ } catch (GitAPIException e) {
+ throw new SAXException(e);
+ } catch (IOException e) {
+ throw new SAXException(e);
+ }
}
}
+
+ private static String findRef(String ref, Repository repo)
+ throws IOException {
+ if (!ObjectId.isId(ref)) {
+ Ref r = repo.getRef(
+ Constants.DEFAULT_REMOTE_NAME + "/" + ref);
+ if (r != null)
+ return r.getName();
+ }
+ return ref;
+ }
}
diff --git a/org.eclipse.jgit/src/org/eclipse/jgit/gitrepo/internal/RepoText.java b/org.eclipse.jgit/src/org/eclipse/jgit/gitrepo/internal/RepoText.java
index 1313fff..34db723 100644
--- a/org.eclipse.jgit/src/org/eclipse/jgit/gitrepo/internal/RepoText.java
+++ b/org.eclipse.jgit/src/org/eclipse/jgit/gitrepo/internal/RepoText.java
@@ -62,6 +62,7 @@ public static RepoText get() {
/***/ public String copyFileFailed;
/***/ public String errorNoDefault;
/***/ public String errorParsingManifestFile;
+ /***/ public String errorRemoteUnavailable;
/***/ public String invalidManifest;
/***/ public String repoCommitMessage;
}
diff --git a/org.eclipse.jgit/src/org/eclipse/jgit/internal/JGitText.java b/org.eclipse.jgit/src/org/eclipse/jgit/internal/JGitText.java
index 40e74ed..576ba0f 100644
--- a/org.eclipse.jgit/src/org/eclipse/jgit/internal/JGitText.java
+++ b/org.eclipse.jgit/src/org/eclipse/jgit/internal/JGitText.java
@@ -113,6 +113,7 @@ public static JGitText get() {
/***/ public String cannotCreateDirectory;
/***/ public String cannotCreateHEAD;
/***/ public String cannotCreateIndexfile;
+ /***/ public String cannotCreateTempDir;
/***/ public String cannotDeleteCheckedOutBranch;
/***/ public String cannotDeleteFile;
/***/ public String cannotDeleteStaleTrackingRef;
diff --git a/org.eclipse.jgit/src/org/eclipse/jgit/lib/RefDatabase.java b/org.eclipse.jgit/src/org/eclipse/jgit/lib/RefDatabase.java
index 2ee63f18e..682cac1 100644
--- a/org.eclipse.jgit/src/org/eclipse/jgit/lib/RefDatabase.java
+++ b/org.eclipse.jgit/src/org/eclipse/jgit/lib/RefDatabase.java
@@ -271,4 +271,26 @@ public BatchRefUpdate newBatchUpdate() {
public void refresh() {
// nothing
}
+
+ /**
+ * Try to find the specified name in the ref map using {@link #SEARCH_PATH}.
+ *
+ * @param map
+ * map of refs to search within. Names should be fully qualified,
+ * e.g. "refs/heads/master".
+ * @param name
+ * short name of ref to find, e.g. "master" to find
+ * "refs/heads/master" in map.
+ * @return The first ref matching the name, or null if not found.
+ * @since 3.4
+ */
+ public static Ref findRef(Map<String, Ref> map, String name) {
+ for (String prefix : SEARCH_PATH) {
+ String fullname = prefix + name;
+ Ref ref = map.get(fullname);
+ if (ref != null)
+ return ref;
+ }
+ return null;
+ }
}
diff --git a/org.eclipse.jgit/src/org/eclipse/jgit/util/FileUtils.java b/org.eclipse.jgit/src/org/eclipse/jgit/util/FileUtils.java
index b77807c..330cac1 100644
--- a/org.eclipse.jgit/src/org/eclipse/jgit/util/FileUtils.java
+++ b/org.eclipse.jgit/src/org/eclipse/jgit/util/FileUtils.java
@@ -362,4 +362,29 @@ public static void createSymLink(File path, String target)
public static String readSymLink(File path) throws IOException {
return FS.DETECTED.readSymLink(path);
}
+
+ /**
+ * Create a temporary directory.
+ *
+ * @param prefix
+ * @param suffix
+ * @param dir
+ * The parent dir, can be null to use system default temp dir.
+ * @return the temp dir created.
+ * @throws IOException
+ * @since 3.4
+ */
+ public static File createTempDir(String prefix, String suffix, File dir)
+ throws IOException {
+ final int RETRY = 1; // When something bad happens, retry once.
+ for (int i = 0; i < RETRY; i++) {
+ File tmp = File.createTempFile(prefix, suffix, dir);
+ if (!tmp.delete())
+ continue;
+ if (!tmp.mkdir())
+ continue;
+ return tmp;
+ }
+ throw new IOException(JGitText.get().cannotCreateTempDir);
+ }
}