Change-Id: I56699b7bf9a71f673cb308d3015f51de5b06c1d9 Signed-off-by: Matthias Sohn <matthias.sohn@sap.com>tags/v3.6.0.201412230720-r
@@ -0,0 +1,136 @@ | |||
/* | |||
* Copyright (C) 2014 Matthias Sohn <matthias.sohn@sap.com> | |||
* and other copyright owners as documented in the project's IP log. | |||
* | |||
* This program and the accompanying materials are made available | |||
* under the terms of the Eclipse Distribution License v1.0 which | |||
* accompanies this distribution, is reproduced below, and is | |||
* available at http://www.eclipse.org/org/documents/edl-v10.php | |||
* | |||
* All rights reserved. | |||
* | |||
* Redistribution and use in source and binary forms, with or | |||
* without modification, are permitted provided that the following | |||
* conditions are met: | |||
* | |||
* - Redistributions of source code must retain the above copyright | |||
* notice, this list of conditions and the following disclaimer. | |||
* | |||
* - Redistributions in binary form must reproduce the above | |||
* copyright notice, this list of conditions and the following | |||
* disclaimer in the documentation and/or other materials provided | |||
* with the distribution. | |||
* | |||
* - Neither the name of the Eclipse Foundation, Inc. nor the | |||
* names of its contributors may be used to endorse or promote | |||
* products derived from this software without specific prior | |||
* written permission. | |||
* | |||
* THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND | |||
* CONTRIBUTORS "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, | |||
* INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES | |||
* OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE | |||
* ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR | |||
* CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, | |||
* SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT | |||
* NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; | |||
* LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER | |||
* CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, | |||
* STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) | |||
* ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF | |||
* ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. | |||
*/ | |||
package org.eclipse.jgit.pgm; | |||
import static org.junit.Assert.assertArrayEquals; | |||
import static org.junit.Assert.assertEquals; | |||
import java.io.File; | |||
import java.util.List; | |||
import org.eclipse.jgit.api.Git; | |||
import org.eclipse.jgit.junit.JGitTestUtil; | |||
import org.eclipse.jgit.junit.MockSystemReader; | |||
import org.eclipse.jgit.lib.CLIRepositoryTestCase; | |||
import org.eclipse.jgit.lib.Constants; | |||
import org.eclipse.jgit.lib.Ref; | |||
import org.eclipse.jgit.transport.URIish; | |||
import org.eclipse.jgit.util.SystemReader; | |||
import org.junit.Before; | |||
import org.junit.Test; | |||
public class CloneTest extends CLIRepositoryTestCase { | |||
private Git git; | |||
@Override | |||
@Before | |||
public void setUp() throws Exception { | |||
super.setUp(); | |||
git = new Git(db); | |||
} | |||
@Test | |||
public void testClone() throws Exception { | |||
createInitialCommit(); | |||
File gitDir = db.getDirectory(); | |||
String sourceURI = gitDir.toURI().toString(); | |||
File target = createTempDirectory("target"); | |||
StringBuilder cmd = new StringBuilder("git clone ").append(sourceURI | |||
+ " " + target.getPath()); | |||
String[] result = execute(cmd.toString()); | |||
assertArrayEquals(new String[] { | |||
"Cloning into '" + target.getPath() + "'...", | |||
"", "" }, result); | |||
Git git2 = Git.open(target); | |||
List<Ref> branches = git2.branchList().call(); | |||
assertEquals("expected 1 branch", 1, branches.size()); | |||
} | |||
private void createInitialCommit() throws Exception { | |||
JGitTestUtil.writeTrashFile(db, "hello.txt", "world"); | |||
git.add().addFilepattern("hello.txt").call(); | |||
git.commit().setMessage("Initial commit").call(); | |||
} | |||
@Test | |||
public void testCloneEmpty() throws Exception { | |||
File gitDir = db.getDirectory(); | |||
String sourceURI = gitDir.toURI().toString(); | |||
File target = createTempDirectory("target"); | |||
StringBuilder cmd = new StringBuilder("git clone ").append(sourceURI | |||
+ " " + target.getPath()); | |||
String[] result = execute(cmd.toString()); | |||
assertArrayEquals(new String[] { | |||
"Cloning into '" + target.getPath() + "'...", | |||
"warning: You appear to have cloned an empty repository.", "", | |||
"" }, result); | |||
Git git2 = Git.open(target); | |||
List<Ref> branches = git2.branchList().call(); | |||
assertEquals("expected 0 branch", 0, branches.size()); | |||
} | |||
@Test | |||
public void testCloneIntoCurrentDir() throws Exception { | |||
createInitialCommit(); | |||
File target = createTempDirectory("target"); | |||
MockSystemReader sr = (MockSystemReader) SystemReader.getInstance(); | |||
sr.setProperty(Constants.OS_USER_DIR, target.getAbsolutePath()); | |||
File gitDir = db.getDirectory(); | |||
String sourceURI = gitDir.toURI().toString(); | |||
String name = new URIish(sourceURI).getHumanishName(); | |||
StringBuilder cmd = new StringBuilder("git clone ").append(sourceURI); | |||
String[] result = execute(cmd.toString()); | |||
assertArrayEquals(new String[] { | |||
"Cloning into '" + new File(target, name).getName() + "'...", | |||
"", "" }, result); | |||
Git git2 = Git.open(new File(target, name)); | |||
List<Ref> branches = git2.branchList().call(); | |||
assertEquals("expected 1 branch", 1, branches.size()); | |||
} | |||
} |
@@ -43,6 +43,8 @@ changesNotStagedForCommit=Changes not staged for commit: | |||
changesToBeCommitted=Changes to be committed: | |||
checkoutConflict=error: Your local changes to the following files would be overwritten by checkout: | |||
checkoutConflictPathLine=\t{0} | |||
clonedEmptyRepository=warning: You appear to have cloned an empty repository. | |||
cloningInto=Cloning into ''{0}''... | |||
commitLabel=commit | |||
configFileNotFound=configuration file {0} not found | |||
conflictingUsageOf_git_dir_andArguments=conflicting usage of --git-dir and arguments |
@@ -44,30 +44,15 @@ | |||
package org.eclipse.jgit.pgm; | |||
import java.io.File; | |||
import java.io.IOException; | |||
import java.net.URISyntaxException; | |||
import java.text.MessageFormat; | |||
import org.eclipse.jgit.dircache.DirCache; | |||
import org.eclipse.jgit.dircache.DirCacheCheckout; | |||
import org.eclipse.jgit.errors.IncorrectObjectTypeException; | |||
import org.eclipse.jgit.errors.MissingObjectException; | |||
import org.eclipse.jgit.api.CloneCommand; | |||
import org.eclipse.jgit.api.Git; | |||
import org.eclipse.jgit.api.errors.InvalidRemoteException; | |||
import org.eclipse.jgit.lib.Constants; | |||
import org.eclipse.jgit.lib.Ref; | |||
import org.eclipse.jgit.lib.RefUpdate; | |||
import org.eclipse.jgit.lib.Repository; | |||
import org.eclipse.jgit.lib.StoredConfig; | |||
import org.eclipse.jgit.lib.TextProgressMonitor; | |||
import org.eclipse.jgit.pgm.internal.CLIText; | |||
import org.eclipse.jgit.revwalk.RevCommit; | |||
import org.eclipse.jgit.revwalk.RevWalk; | |||
import org.eclipse.jgit.storage.file.FileRepositoryBuilder; | |||
import org.eclipse.jgit.transport.FetchResult; | |||
import org.eclipse.jgit.transport.RefSpec; | |||
import org.eclipse.jgit.transport.RemoteConfig; | |||
import org.eclipse.jgit.transport.TagOpt; | |||
import org.eclipse.jgit.transport.Transport; | |||
import org.eclipse.jgit.transport.URIish; | |||
import org.eclipse.jgit.util.SystemReader; | |||
import org.kohsuke.args4j.Argument; | |||
import org.kohsuke.args4j.Option; | |||
@@ -88,8 +73,6 @@ class Clone extends AbstractFetchCommand { | |||
@Argument(index = 1, metaVar = "metaVar_directory") | |||
private String localName; | |||
private Repository dst; | |||
@Override | |||
protected final boolean requiresRepository() { | |||
return false; | |||
@@ -101,116 +84,42 @@ class Clone extends AbstractFetchCommand { | |||
throw die(CLIText.get().conflictingUsageOf_git_dir_andArguments); | |||
final URIish uri = new URIish(sourceUri); | |||
File localNameF; | |||
if (localName == null) { | |||
try { | |||
localName = uri.getHumanishName(); | |||
localNameF = new File(SystemReader.getInstance().getProperty( | |||
Constants.OS_USER_DIR), localName); | |||
} catch (IllegalArgumentException e) { | |||
throw die(MessageFormat.format(CLIText.get().cannotGuessLocalNameFrom, sourceUri)); | |||
} | |||
} | |||
if (gitdir == null) | |||
gitdir = new File(localName, Constants.DOT_GIT).getAbsolutePath(); | |||
dst = new FileRepositoryBuilder().setGitDir(new File(gitdir)).build(); | |||
dst.create(); | |||
final StoredConfig dstcfg = dst.getConfig(); | |||
dstcfg.setBoolean("core", null, "bare", false); //$NON-NLS-1$ //$NON-NLS-2$ | |||
dstcfg.save(); | |||
db = dst; | |||
outw.print(MessageFormat.format( | |||
CLIText.get().initializedEmptyGitRepositoryIn, gitdir)); | |||
outw.println(); | |||
outw.flush(); | |||
saveRemote(uri); | |||
final FetchResult r = runFetch(); | |||
if (!noCheckout) { | |||
final Ref checkoutRef; | |||
if (branch == null) | |||
checkoutRef = guessHEAD(r); | |||
else { | |||
checkoutRef = r.getAdvertisedRef(Constants.R_HEADS + branch); | |||
if (checkoutRef == null) | |||
throw die(MessageFormat.format( | |||
CLIText.get().noSuchRemoteRef, branch)); | |||
throw die(MessageFormat.format( | |||
CLIText.get().cannotGuessLocalNameFrom, sourceUri)); | |||
} | |||
doCheckout(checkoutRef); | |||
} | |||
} | |||
} else | |||
localNameF = new File(localName); | |||
private void saveRemote(final URIish uri) throws URISyntaxException, | |||
IOException { | |||
final StoredConfig dstcfg = dst.getConfig(); | |||
final RemoteConfig rc = new RemoteConfig(dstcfg, remoteName); | |||
rc.addURI(uri); | |||
rc.addFetchRefSpec(new RefSpec().setForceUpdate(true) | |||
.setSourceDestination(Constants.R_HEADS + "*", //$NON-NLS-1$ | |||
Constants.R_REMOTES + remoteName + "/*")); //$NON-NLS-1$ | |||
rc.update(dstcfg); | |||
dstcfg.save(); | |||
} | |||
private FetchResult runFetch() throws URISyntaxException, IOException { | |||
final Transport tn = Transport.open(db, remoteName); | |||
final FetchResult r; | |||
try { | |||
tn.setTagOpt(TagOpt.FETCH_TAGS); | |||
r = tn.fetch(new TextProgressMonitor(), null); | |||
} finally { | |||
tn.close(); | |||
} | |||
showFetchResult(r); | |||
return r; | |||
} | |||
private static Ref guessHEAD(final FetchResult result) { | |||
final Ref idHEAD = result.getAdvertisedRef(Constants.HEAD); | |||
Ref head = null; | |||
for (final Ref r : result.getAdvertisedRefs()) { | |||
final String n = r.getName(); | |||
if (!n.startsWith(Constants.R_HEADS)) | |||
continue; | |||
if (idHEAD == null || head != null) | |||
continue; | |||
if (r.getObjectId().equals(idHEAD.getObjectId())) | |||
head = r; | |||
} | |||
if (idHEAD != null && head == null) | |||
head = idHEAD; | |||
return head; | |||
} | |||
private void doCheckout(final Ref branch) throws IOException { | |||
if (branch == null) | |||
throw die(CLIText.get().cannotChekoutNoHeadsAdvertisedByRemote); | |||
if (!Constants.HEAD.equals(branch.getName())) { | |||
RefUpdate u = db.updateRef(Constants.HEAD); | |||
u.disableRefLog(); | |||
u.link(branch.getName()); | |||
} | |||
final RevCommit commit = parseCommit(branch); | |||
final RefUpdate u = db.updateRef(Constants.HEAD); | |||
u.setNewObjectId(commit); | |||
u.forceUpdate(); | |||
branch = Constants.HEAD; | |||
DirCache dc = db.lockDirCache(); | |||
DirCacheCheckout co = new DirCacheCheckout(db, dc, commit.getTree()); | |||
co.checkout(); | |||
} | |||
CloneCommand command = Git.cloneRepository(); | |||
command.setURI(sourceUri).setRemote(remoteName) | |||
.setNoCheckout(noCheckout).setBranch(branch); | |||
private RevCommit parseCommit(final Ref branch) | |||
throws MissingObjectException, IncorrectObjectTypeException, | |||
IOException { | |||
final RevWalk rw = new RevWalk(db); | |||
final RevCommit commit; | |||
command.setGitDir(gitdir == null ? null : new File(gitdir)); | |||
command.setDirectory(localNameF); | |||
outw.println(MessageFormat.format(CLIText.get().cloningInto, localName)); | |||
try { | |||
commit = rw.parseCommit(branch.getObjectId()); | |||
db = command.call().getRepository(); | |||
if (db.resolve(Constants.HEAD) == null) | |||
outw.println(CLIText.get().clonedEmptyRepository); | |||
} catch (InvalidRemoteException e) { | |||
throw die(MessageFormat.format(CLIText.get().doesNotExist, | |||
sourceUri)); | |||
} finally { | |||
rw.release(); | |||
if (db != null) | |||
db.close(); | |||
} | |||
return commit; | |||
outw.println(); | |||
outw.flush(); | |||
} | |||
} |
@@ -109,6 +109,8 @@ public class CLIText extends TranslationBundle { | |||
/***/ public String changesToBeCommitted; | |||
/***/ public String checkoutConflict; | |||
/***/ public String checkoutConflictPathLine; | |||
/***/ public String clonedEmptyRepository; | |||
/***/ public String cloningInto; | |||
/***/ public String commitLabel; | |||
/***/ public String conflictingUsageOf_git_dir_andArguments; | |||
/***/ public String couldNotCreateBranch; |