From c02839c3e16e4dd7b2076ff09e93848e298042fd Mon Sep 17 00:00:00 2001 From: Robin Rosenberg Date: Tue, 25 Mar 2008 01:50:15 +0100 Subject: [PATCH] Add generic full fetch class and protocol dependant subclasses Signed-off-by: Robin Rosenberg --- .../tst/org/spearce/jgit/fetch/FetchTest.java | 111 +++++++++------------ .../org/spearce/jgit/lib/RepositoryTestCase.java | 18 ++++ .../org/spearce/jgit/fetch/FullFetchClient.java | 83 +++++++++++++++ .../spearce/jgit/fetch/GitProtocolFetchClient.java | 65 ++++++++++++ .../jgit/fetch/GitSshProtocolFetchClient.java | 107 ++++++++++++++++++++ .../jgit/fetch/LocalGitProtocolFetchClient.java | 67 +++++++++++++ 6 files changed, 388 insertions(+), 63 deletions(-) rewrite org.spearce.jgit.test/tst/org/spearce/jgit/fetch/FetchTest.java (69%) create mode 100644 org.spearce.jgit/src/org/spearce/jgit/fetch/FullFetchClient.java create mode 100644 org.spearce.jgit/src/org/spearce/jgit/fetch/GitProtocolFetchClient.java create mode 100644 org.spearce.jgit/src/org/spearce/jgit/fetch/GitSshProtocolFetchClient.java create mode 100644 org.spearce.jgit/src/org/spearce/jgit/fetch/LocalGitProtocolFetchClient.java diff --git a/org.spearce.jgit.test/tst/org/spearce/jgit/fetch/FetchTest.java b/org.spearce.jgit.test/tst/org/spearce/jgit/fetch/FetchTest.java dissimilarity index 69% index 5a8d60a4..e09533d8 100644 --- a/org.spearce.jgit.test/tst/org/spearce/jgit/fetch/FetchTest.java +++ b/org.spearce.jgit.test/tst/org/spearce/jgit/fetch/FetchTest.java @@ -1,63 +1,48 @@ -package org.spearce.jgit.fetch; - -import java.io.File; -import java.io.IOException; -import java.io.InputStream; -import java.io.OutputStream; -import java.io.PipedInputStream; -import java.io.PipedOutputStream; - -import org.spearce.jgit.lib.Repository; -import org.spearce.jgit.lib.RepositoryTestCase; -import org.spearce.jgit.lib.TextProgressMonitor; - -public class FetchTest extends RepositoryTestCase { - public void testSimpleFullClone() throws IOException, InterruptedException { - File newTestRepo = new File(trashParent, "new"+System.currentTimeMillis()+"/.git"); - assertFalse(newTestRepo.exists()); - - File unusedDir = new File(trashParent, "tmp"+System.currentTimeMillis()); - assertTrue(unusedDir.mkdirs()); - - final Repository newRepo = new Repository(newTestRepo); - newRepo.create(); - - Process process = Runtime.getRuntime().exec("git-upload-pack "+trash_git.getAbsolutePath(), null, unusedDir); - InputStream inputStream = process.getInputStream(); - OutputStream outpuStream = process.getOutputStream(); - final PipedInputStream pi = new PipedInputStream(); - PipedOutputStream po = new PipedOutputStream(pi); - FetchClient client = new FetchClient(newRepo, null, outpuStream, inputStream, po); - final Thread fetchThread = Thread.currentThread(); - final Throwable[] threadError = new Throwable[1]; - Thread indexThread = new Thread() { - @Override - public void run() { - IndexPack pack; - try { - pack = new IndexPack(pi, new File("tmp_pack1")); - pack.index(new TextProgressMonitor()); - pack.renamePack(newRepo); - } catch (Throwable e) { - // TODO Auto-generated catch block - e.printStackTrace(); - fetchThread.interrupt(); - threadError[0] = e; - } - } - }; - indexThread.start(); - client.run(); - po.close(); - indexThread.join(); - if (threadError[0] != null) - throw new Error(threadError[0]); - newRepo.scanForPacks(); - client.updateRemoteRefs("origintest"); - assertEquals("6db9c2ebf75590eef973081736730a9ea169a0c4", newRepo.mapCommit("remotes/origintest/a").getCommitId().toString()); - assertEquals("17768080a2318cd89bba4c8b87834401e2095703", newRepo.mapTag("refs/tags/B").getTagId().toString()); - assertEquals("d86a2aada2f5e7ccf6f11880bfb9ab404e8a8864", newRepo.mapTag("refs/tags/B").getObjId().toString()); - assertEquals("6db9c2ebf75590eef973081736730a9ea169a0c4", newRepo.mapTag("refs/tags/A").getObjId().toString()); - assertNull(newRepo.mapTag("refs/tags/A").getTagId()); - } -} +package org.spearce.jgit.fetch; + +import java.io.IOException; + +import org.spearce.jgit.lib.Commit; +import org.spearce.jgit.lib.ObjectWriter; +import org.spearce.jgit.lib.PersonIdent; +import org.spearce.jgit.lib.RefLock; +import org.spearce.jgit.lib.Repository; +import org.spearce.jgit.lib.RepositoryTestCase; + +public class FetchTest extends RepositoryTestCase { + + public void testSimpleFullLocalClone() throws IOException { + final Repository newRepo = createNewEmptyRepo(); + FetchClient client = LocalGitProtocolFetchClient.create(newRepo, "origintest", trash_git.getAbsolutePath()); + client.run(); + + assertEquals("6db9c2ebf75590eef973081736730a9ea169a0c4", newRepo.mapCommit("remotes/origintest/a").getCommitId().toString()); + assertEquals("17768080a2318cd89bba4c8b87834401e2095703", newRepo.mapTag("refs/tags/B").getTagId().toString()); + assertEquals("d86a2aada2f5e7ccf6f11880bfb9ab404e8a8864", newRepo.mapTag("refs/tags/B").getObjId().toString()); + assertEquals("6db9c2ebf75590eef973081736730a9ea169a0c4", newRepo.mapTag("refs/tags/A").getObjId().toString()); + assertNull(newRepo.mapTag("refs/tags/A").getTagId()); + + // Now do an incremental update test too, a non-fast-forward + Commit commit = new Commit(db); + commit.setAuthor(new PersonIdent(jauthor, 100, 1)); + commit.setCommitter(new PersonIdent(jcommitter, 101,2)); + commit.setMessage("Update"); + commit.setTree(db.mapTree("refs/heads/a")); + ObjectWriter ow = new ObjectWriter(db); + commit.setCommitId(ow.writeCommit(commit)); + RefLock lockRef = db.lockRef("refs/heads/a"); + lockRef.write(commit.getCommitId()); + if (!lockRef.commit()) + throw new IllegalStateException("Could not update refs/heafs/a in test"); + + FetchClient client2 = LocalGitProtocolFetchClient.create(newRepo, "origintest", trash_git.getAbsolutePath()); + client2.run(); + + assertEquals("9bac73222088373f5a41ed64994adc881a0a27b6", newRepo.mapCommit("remotes/origintest/a").getCommitId().toString()); + // the rest is unchanged + assertEquals("17768080a2318cd89bba4c8b87834401e2095703", newRepo.mapTag("refs/tags/B").getTagId().toString()); + assertEquals("d86a2aada2f5e7ccf6f11880bfb9ab404e8a8864", newRepo.mapTag("refs/tags/B").getObjId().toString()); + assertEquals("6db9c2ebf75590eef973081736730a9ea169a0c4", newRepo.mapTag("refs/tags/A").getObjId().toString()); + assertNull(newRepo.mapTag("refs/tags/A").getTagId()); + } +} diff --git a/org.spearce.jgit.test/tst/org/spearce/jgit/lib/RepositoryTestCase.java b/org.spearce.jgit.test/tst/org/spearce/jgit/lib/RepositoryTestCase.java index e5daab01..4c4e7279 100644 --- a/org.spearce.jgit.test/tst/org/spearce/jgit/lib/RepositoryTestCase.java +++ b/org.spearce.jgit.test/tst/org/spearce/jgit/lib/RepositoryTestCase.java @@ -137,4 +137,22 @@ public abstract class RepositoryTestCase extends TestCase { db.close(); super.tearDown(); } + + /** + * Helper for creating extra empty repos + * + * @return a new empty git repository for testing purposes + * + * @throws IOException + */ + protected Repository createNewEmptyRepo() throws IOException { + File newTestRepo = new File(trashParent, "new"+System.currentTimeMillis()+"/.git"); + assertFalse(newTestRepo.exists()); + File unusedDir = new File(trashParent, "tmp"+System.currentTimeMillis()); + assertTrue(unusedDir.mkdirs()); + final Repository newRepo = new Repository(newTestRepo); + newRepo.create(); + return newRepo; + } + } diff --git a/org.spearce.jgit/src/org/spearce/jgit/fetch/FullFetchClient.java b/org.spearce.jgit/src/org/spearce/jgit/fetch/FullFetchClient.java new file mode 100644 index 00000000..0fc28124 --- /dev/null +++ b/org.spearce.jgit/src/org/spearce/jgit/fetch/FullFetchClient.java @@ -0,0 +1,83 @@ +package org.spearce.jgit.fetch; + +import java.io.File; +import java.io.IOException; +import java.io.InputStream; +import java.io.OutputStream; +import java.io.PipedInputStream; +import java.io.PipedOutputStream; + +import org.spearce.jgit.fetch.FetchClient; +import org.spearce.jgit.fetch.IndexPack; +import org.spearce.jgit.lib.Repository; +import org.spearce.jgit.lib.TextProgressMonitor; + +/** + * This is does-it-all fetch implementation. + */ +public class FullFetchClient extends FetchClient { + + private PipedInputStream pi = new PipedInputStream(); + Throwable[] threadError = new Throwable[1]; + Thread fetchThread; + private String remote; + + Thread indexThread = new Thread() { + @Override + public void run() { + IndexPack pack; + try { + pack = new IndexPack(pi, new File("tmp_pack1")); + pack.index(new TextProgressMonitor()); + pack.renamePack(repository); + } catch (Throwable e) { + e.printStackTrace(); + fetchThread.interrupt(); + threadError[0] = e; + } + } + }; + + public void run() throws IOException { + try { + fetchThread = Thread.currentThread(); + indexThread.start(); + super.run(); + os.close(); + indexThread.join(); + repository.scanForPacks(); + updateRemoteRefs(remote); + } catch (InterruptedException e) { + if (threadError[0] != null) + if (threadError[0] instanceof IOException) + throw (IOException)threadError[0]; + throw new Error(threadError[0]); + } finally { + if (indexThread.isAlive()) { + indexThread.interrupt(); + try { + indexThread.join(); + } catch (InterruptedException e) { + // nothing here + } + } + } + } + + /** + * Construct a {@link FullFetchClient} + * + * @param repository + * @param remoteName + * @param initialCommand + * @param toServer + * @param fromServer + * @throws IOException + */ + public FullFetchClient(Repository repository, String remoteName, String initialCommand, + OutputStream toServer, InputStream fromServer) throws IOException { + super(repository, initialCommand, toServer, fromServer, null); + remote = remoteName; + os = new PipedOutputStream(pi); + } +} diff --git a/org.spearce.jgit/src/org/spearce/jgit/fetch/GitProtocolFetchClient.java b/org.spearce.jgit/src/org/spearce/jgit/fetch/GitProtocolFetchClient.java new file mode 100644 index 00000000..81fccb5c --- /dev/null +++ b/org.spearce.jgit/src/org/spearce/jgit/fetch/GitProtocolFetchClient.java @@ -0,0 +1,65 @@ +/* + * Copyright (C) 2008 Robin Rosenberg + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public + * License, version 2, as published by the Free Software Foundation. + * + * This library is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * General Public License for more details. + * + * You should have received a copy of the GNU General Public + * License along with this library; if not, write to the Free Software + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 + */ +package org.spearce.jgit.fetch; + +import java.io.IOException; +import java.io.InputStream; +import java.io.OutputStream; +import java.net.InetAddress; +import java.net.Socket; + +import org.spearce.jgit.lib.Repository; + + +/** + * A {@link FetchClient} for local cloning using git protocol + */ +public class GitProtocolFetchClient extends FullFetchClient { + + /** + * IANA assigned port number for Git + */ + public static final int GIT_PROTO_PORT = 9418; + + private GitProtocolFetchClient(final Repository repository, + final String remoteName, final String initialCommand, + final OutputStream toServer, final InputStream fromServer) + throws IOException { + super(repository, remoteName, initialCommand, toServer, fromServer); + } + + /** + * Create a FetchClient for local cloning using local git protocol + * + * @param repository + * @param remoteName + * @param host + * @param port IANA standard 9418 + * @param remoteGitDir + * @return a {@link FetchClient} set up for cloning + * @throws IOException + */ + public static FetchClient create(final Repository repository, + final String remoteName, final String host, final int port, + final String remoteGitDir) throws IOException { + final Socket socket = new Socket(InetAddress.getByName(host), port); + final InputStream inputStream = socket.getInputStream(); + final OutputStream outpuStream = socket.getOutputStream(); + final String remoteCommand = "git-upload-pack "+remoteGitDir+"\0host="+host+"\0"; + return new GitProtocolFetchClient(repository, remoteName, remoteCommand, outpuStream, inputStream); + } +} diff --git a/org.spearce.jgit/src/org/spearce/jgit/fetch/GitSshProtocolFetchClient.java b/org.spearce.jgit/src/org/spearce/jgit/fetch/GitSshProtocolFetchClient.java new file mode 100644 index 00000000..2a3d1395 --- /dev/null +++ b/org.spearce.jgit/src/org/spearce/jgit/fetch/GitSshProtocolFetchClient.java @@ -0,0 +1,107 @@ +/* + * Copyright (C) 2008 Robin Rosenberg + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public + * License, version 2, as published by the Free Software Foundation. + * + * This library is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * General Public License for more details. + * + * You should have received a copy of the GNU General Public + * License along with this library; if not, write to the Free Software + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 + */ +package org.spearce.jgit.fetch; + +import java.io.IOException; +import java.io.InputStream; +import java.io.OutputStream; + +import org.spearce.jgit.lib.Repository; + +import com.jcraft.jsch.ChannelExec; +import com.jcraft.jsch.JSch; +import com.jcraft.jsch.JSchException; +import com.jcraft.jsch.Session; +import com.jcraft.jsch.UserInfo; + +/** + * A {@link FetchClient} for local cloning using git protocol + */ +public class GitSshProtocolFetchClient extends FullFetchClient { + + /** + * Default port for Git over SSH, same as SSH default port + */ + public static final int GIT_SSH_PROTO_PORT = 22; + + private GitSshProtocolFetchClient(final Repository repository, + final String remoteName, final String initialCommand, + final OutputStream toServer, final InputStream fromServer) + throws IOException { + super(repository, remoteName, initialCommand, toServer, fromServer); + } + + /** + * Create a FetchClient for cloning using git + ssh protocol + * + * @param repository + * @param remoteName + * @param host + * @param port standard is SSH port 22 + * @param username + * @param password + * @param remoteGitDir + * @return a {@link FetchClient} set up for cloning + * @throws IOException + * @throws JSchException + */ + public static FetchClient create(final Repository repository, + final String remoteName, final String host, final int port, + final String username, final String password, + final String remoteGitDir) throws IOException, JSchException { + final JSch sch = new JSch(); + final Session session = sch.getSession(username, host, port); + final UserInfo userInfo = new UserInfo() { + + public void showMessage(String arg0) { + System.out.println("Userinfo message:" + arg0); + } + + public boolean promptYesNo(String arg0) { + System.out.println("Userinfo promptYesNo:" + arg0); + return true; // TODO - do not answer yes lightly + } + + public boolean promptPassword(String arg0) { + return true; + } + + public boolean promptPassphrase(String arg0) { + return false; + } + + public String getPassword() { + return password; + } + + public String getPassphrase() { + return null; + } + + }; + session.setUserInfo(userInfo); + session.connect(); + ChannelExec channel = (ChannelExec)session.openChannel("exec"); + channel.setCommand("git-upload-pack \""+remoteGitDir+"\""); + final InputStream inputStream = channel.getInputStream(); + final OutputStream outpuStream = channel.getOutputStream(); + channel.setErrStream(System.err); + channel.connect(); + + return new GitSshProtocolFetchClient(repository, remoteName, null, outpuStream, inputStream); + } +} diff --git a/org.spearce.jgit/src/org/spearce/jgit/fetch/LocalGitProtocolFetchClient.java b/org.spearce.jgit/src/org/spearce/jgit/fetch/LocalGitProtocolFetchClient.java new file mode 100644 index 00000000..e8fd67e3 --- /dev/null +++ b/org.spearce.jgit/src/org/spearce/jgit/fetch/LocalGitProtocolFetchClient.java @@ -0,0 +1,67 @@ +/* + * Copyright (C) 2008 Robin Rosenberg + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public + * License, version 2, as published by the Free Software Foundation. + * + * This library is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * General Public License for more details. + * + * You should have received a copy of the GNU General Public + * License along with this library; if not, write to the Free Software + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 + */ +package org.spearce.jgit.fetch; + +import java.io.File; +import java.io.IOException; +import java.io.InputStream; +import java.io.OutputStream; + +import org.spearce.jgit.fetch.FetchClient; +import org.spearce.jgit.fetch.FullFetchClient; +import org.spearce.jgit.lib.Repository; + + +/** + * A {@link FetchClient} for local cloning using git protocol. + *

+ * The main use of this class is for testing the git protocol. Exception + * for the initialization, i.e. an optional command to send at the very + * beginning, the protocol is the same regardless of whether it is over + * pipes, plain TCP sockets or an SSH connection. + *

+ * Local can include network drives. + */ +public class LocalGitProtocolFetchClient extends FullFetchClient { + + private LocalGitProtocolFetchClient(final Repository repository, + final String remoteName, final String initialCommand, + final OutputStream toServer, final InputStream fromServer) + throws IOException { + super(repository, remoteName, initialCommand, toServer, fromServer); + } + + /** + * Create a FetchClient for local cloning using local git protocol + * + * @param repository + * @param remoteName + * @param remoteGitDir + * @return a {@link FetchClient} set up for cloning + * @throws IOException + */ + public static FetchClient create(final Repository repository, + final String remoteName, final String remoteGitDir) + throws IOException { + final File tmpDir = new File("tmppack"); + tmpDir.mkdirs(); + final Process process = Runtime.getRuntime().exec("git-upload-pack "+remoteGitDir, null, tmpDir); + final InputStream inputStream = process.getInputStream(); + final OutputStream outpuStream = process.getOutputStream(); + return new LocalGitProtocolFetchClient(repository, remoteName, null, outpuStream, inputStream); + } +} -- 2.11.4.GIT