From 563c1ad514ada9af13f2a1955a10000137804527 Mon Sep 17 00:00:00 2001 From: Matthias Sohn Date: Thu, 4 Dec 2014 01:43:23 +0100 Subject: [PATCH] [pgm] Implement clone using CloneCommand Change-Id: I56699b7bf9a71f673cb308d3015f51de5b06c1d9 Signed-off-by: Matthias Sohn --- .../tst/org/eclipse/jgit/pgm/CloneTest.java | 136 +++++++++++++++ .../jgit/pgm/internal/CLIText.properties | 2 + .../src/org/eclipse/jgit/pgm/Clone.java | 155 ++++-------------- .../eclipse/jgit/pgm/internal/CLIText.java | 2 + 4 files changed, 172 insertions(+), 123 deletions(-) create mode 100644 org.eclipse.jgit.pgm.test/tst/org/eclipse/jgit/pgm/CloneTest.java diff --git a/org.eclipse.jgit.pgm.test/tst/org/eclipse/jgit/pgm/CloneTest.java b/org.eclipse.jgit.pgm.test/tst/org/eclipse/jgit/pgm/CloneTest.java new file mode 100644 index 000000000..c65e16238 --- /dev/null +++ b/org.eclipse.jgit.pgm.test/tst/org/eclipse/jgit/pgm/CloneTest.java @@ -0,0 +1,136 @@ +/* + * Copyright (C) 2014 Matthias Sohn + * and other copyright owners as documented in the project's IP log. + * + * This program and the accompanying materials are made available + * under the terms of the Eclipse Distribution License v1.0 which + * accompanies this distribution, is reproduced below, and is + * available at http://www.eclipse.org/org/documents/edl-v10.php + * + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or + * without modification, are permitted provided that the following + * conditions are met: + * + * - Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * + * - Redistributions in binary form must reproduce the above + * copyright notice, this list of conditions and the following + * disclaimer in the documentation and/or other materials provided + * with the distribution. + * + * - Neither the name of the Eclipse Foundation, Inc. nor the + * names of its contributors may be used to endorse or promote + * products derived from this software without specific prior + * written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND + * CONTRIBUTORS "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, + * INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES + * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE + * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR + * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, + * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT + * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; + * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER + * CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, + * STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) + * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF + * ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + */ +package org.eclipse.jgit.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 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 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 branches = git2.branchList().call(); + assertEquals("expected 1 branch", 1, branches.size()); + } +} diff --git a/org.eclipse.jgit.pgm/resources/org/eclipse/jgit/pgm/internal/CLIText.properties b/org.eclipse.jgit.pgm/resources/org/eclipse/jgit/pgm/internal/CLIText.properties index 7ed77ee04..c0ecc8391 100644 --- a/org.eclipse.jgit.pgm/resources/org/eclipse/jgit/pgm/internal/CLIText.properties +++ b/org.eclipse.jgit.pgm/resources/org/eclipse/jgit/pgm/internal/CLIText.properties @@ -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 diff --git a/org.eclipse.jgit.pgm/src/org/eclipse/jgit/pgm/Clone.java b/org.eclipse.jgit.pgm/src/org/eclipse/jgit/pgm/Clone.java index d9a877cd6..88ea1202d 100644 --- a/org.eclipse.jgit.pgm/src/org/eclipse/jgit/pgm/Clone.java +++ b/org.eclipse.jgit.pgm/src/org/eclipse/jgit/pgm/Clone.java @@ -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 @@ protected void run() throws Exception { 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)); + throw die(MessageFormat.format( + CLIText.get().cannotGuessLocalNameFrom, sourceUri)); } + } else + localNameF = new File(localName); + + if (branch == null) + branch = Constants.HEAD; + + CloneCommand command = Git.cloneRepository(); + command.setURI(sourceUri).setRemote(remoteName) + .setNoCheckout(noCheckout).setBranch(branch); + + command.setGitDir(gitdir == null ? null : new File(gitdir)); + command.setDirectory(localNameF); + outw.println(MessageFormat.format(CLIText.get().cloningInto, localName)); + try { + 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 { + if (db != null) + db.close(); } - 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)); - } - doCheckout(checkoutRef); - } - } - - 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(); - - DirCache dc = db.lockDirCache(); - DirCacheCheckout co = new DirCacheCheckout(db, dc, commit.getTree()); - co.checkout(); - } - - private RevCommit parseCommit(final Ref branch) - throws MissingObjectException, IncorrectObjectTypeException, - IOException { - final RevWalk rw = new RevWalk(db); - final RevCommit commit; - try { - commit = rw.parseCommit(branch.getObjectId()); - } finally { - rw.release(); - } - return commit; } } diff --git a/org.eclipse.jgit.pgm/src/org/eclipse/jgit/pgm/internal/CLIText.java b/org.eclipse.jgit.pgm/src/org/eclipse/jgit/pgm/internal/CLIText.java index c42e5fb59..e7d995e6a 100644 --- a/org.eclipse.jgit.pgm/src/org/eclipse/jgit/pgm/internal/CLIText.java +++ b/org.eclipse.jgit.pgm/src/org/eclipse/jgit/pgm/internal/CLIText.java @@ -109,6 +109,8 @@ public static String formatLine(String line) { /***/ 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;