{"thread":{"id":"14389","subject":"[JGIT PATCH 0/5] Yet another round of transport fixes","startedAt":"2008-07-10T06:13:18Z","lastAt":"2008-07-11T08:30:31Z","messageCount":12,"participants":["Shawn O. Pearce","Robin Rosenberg","Johannes Schindelin"],"isPatch":true,"patchVersion":1,"patchTotal":5},"messages":[{"id":"82779","messageId":"1215670403-19191-1-git-send-email-spearce@spearce.org","threadId":"14389","inReplyTo":null,"subject":"[JGIT PATCH 0/5] Yet another round of transport fixes","fromName":"Shawn O. Pearce","fromEmail":"spearce@spearce.org","sentAt":"2008-07-10T06:13:18Z","receivedAt":"2008-07-10T06:13:18Z","isPatch":true,"sender":{"key":"spearce@spearce.org","avatar":"https://avatars.githubusercontent.com/u/34844?v=4"},"body":"Apparently tweaking minor details is never done.  This short\nseries improves minor issues I have with the transport code\nin relation to error and progress reporting.  We also get a\nsmall performance boost for tag fetching over SSH.\n\nShawn O. Pearce (5):\n  Include a progress meter for large uploads to Amazon S3\n  Don't display passwords on the console in fetch/push output\n  Reuse the same SSH connection when automatically fetching tags\n  Report remote SSH execution errors during push via TransportException\n  Explicitly capture the stderr from a failed SSH fetch or push\n\n .../org/spearce/egit/core/op/CloneOperation.java   |   10 ++-\n .../spearce/egit/ui/EclipseSshSessionFactory.java  |    5 +-\n .../egit/ui/internal/clone/SourceBranchPage.java   |    1 +\n .../spearce/jgit/transport/PushProcessTest.java    |    5 +\n .../org/spearce/jgit/transport/TransportTest.java  |   10 ++\n .../jgit/errors/NoRemoteRepositoryException.java   |   59 +++++++++++\n .../src/org/spearce/jgit/pgm/Fetch.java            |   19 +++--\n .../src/org/spearce/jgit/pgm/LsRemote.java         |    1 +\n .../src/org/spearce/jgit/pgm/Push.java             |    2 +-\n .../src/org/spearce/jgit/transport/AmazonS3.java   |   29 +++++-\n .../spearce/jgit/transport/BasePackConnection.java |    3 +-\n .../jgit/transport/DefaultSshSessionFactory.java   |   30 ++++++-\n .../src/org/spearce/jgit/transport/Transport.java  |   10 ++\n .../spearce/jgit/transport/TransportAmazonS3.java  |   12 ++-\n .../spearce/jgit/transport/TransportBundle.java    |    5 +\n .../spearce/jgit/transport/TransportGitAnon.java   |    5 +\n .../spearce/jgit/transport/TransportGitSsh.java    |  103 +++++++++++++-------\n .../org/spearce/jgit/transport/TransportHttp.java  |    5 +\n .../org/spearce/jgit/transport/TransportLocal.java |    5 +\n .../org/spearce/jgit/transport/TransportSftp.java  |   57 ++++++-----\n .../spearce/jgit/transport/WalkPushConnection.java |    5 +-\n .../jgit/transport/WalkRemoteObjectDatabase.java   |   11 ++-\n 22 files changed, 303 insertions(+), 89 deletions(-)\n create mode 100644 org.spearce.jgit/src/org/spearce/jgit/errors/NoRemoteRepositoryException.java\n"},{"id":"82782","messageId":"1215670403-19191-2-git-send-email-spearce@spearce.org","threadId":"14389","inReplyTo":"1215670403-19191-1-git-send-email-spearce@spearce.org","subject":"[JGIT PATCH 1/5] Include a progress meter for large uploads to Amazon S3","fromName":"Shawn O. Pearce","fromEmail":"spearce@spearce.org","sentAt":"2008-07-10T06:13:19Z","receivedAt":"2008-07-10T06:13:19Z","isPatch":true,"sender":{"key":"spearce@spearce.org","avatar":"https://avatars.githubusercontent.com/u/34844?v=4"},"body":"If we are uploading a sizable pack file or idx file to the S3\nservice the upload happens after we have finished writing the pack\nto a local temporary file.  This causes a long pause for the user\nwhile they wait for the data transfer to complete, with no real\nindication of progress happening.\n\nSigned-off-by: Shawn O. Pearce <spearce@spearce.org>\n---\n .../src/org/spearce/jgit/transport/AmazonS3.java   |   29 +++++++++++++++----\n .../spearce/jgit/transport/TransportAmazonS3.java  |    7 +++-\n .../org/spearce/jgit/transport/TransportSftp.java  |    5 +++-\n .../spearce/jgit/transport/WalkPushConnection.java |    5 ++-\n .../jgit/transport/WalkRemoteObjectDatabase.java   |   11 ++++++-\n 5 files changed, 44 insertions(+), 13 deletions(-)\n\ndiff --git a/org.spearce.jgit/src/org/spearce/jgit/transport/AmazonS3.java b/org.spearce.jgit/src/org/spearce/jgit/transport/AmazonS3.java\nindex e8575aa..59337f8 100644\n--- a/org.spearce.jgit/src/org/spearce/jgit/transport/AmazonS3.java\n+++ b/org.spearce.jgit/src/org/spearce/jgit/transport/AmazonS3.java\n@@ -75,6 +75,8 @@ import javax.crypto.spec.SecretKeySpec;\n \n import org.spearce.jgit.awtui.AwtAuthenticator;\n import org.spearce.jgit.lib.Constants;\n+import org.spearce.jgit.lib.NullProgressMonitor;\n+import org.spearce.jgit.lib.ProgressMonitor;\n import org.spearce.jgit.util.Base64;\n import org.spearce.jgit.util.HttpSupport;\n import org.spearce.jgit.util.TemporaryBuffer;\n@@ -376,7 +378,7 @@ public class AmazonS3 {\n \t\t\t// We have to copy to produce the cipher text anyway so use\n \t\t\t// the large object code path as it supports that behavior.\n \t\t\t//\n-\t\t\tfinal OutputStream os = beginPut(bucket, key);\n+\t\t\tfinal OutputStream os = beginPut(bucket, key, null, null);\n \t\t\tos.write(data);\n \t\t\tos.close();\n \t\t\treturn;\n@@ -430,11 +432,17 @@ public class AmazonS3 {\n \t *            name of the bucket storing the object.\n \t * @param key\n \t *            key of the object within its bucket.\n+\t * @param monitor\n+\t *            (optional) progress monitor to post upload completion to\n+\t *            during the stream's close method.\n+\t * @param monitorTask\n+\t *            (optional) task name to display during the close method.\n \t * @return a stream which accepts the new data, and transmits once closed.\n \t * @throws IOException\n \t *             if encryption was enabled it could not be configured.\n \t */\n-\tpublic OutputStream beginPut(final String bucket, final String key)\n+\tpublic OutputStream beginPut(final String bucket, final String key,\n+\t\t\tfinal ProgressMonitor monitor, final String monitorTask)\n \t\t\tthrows IOException {\n \t\tfinal MessageDigest md5 = newMD5();\n \t\tfinal TemporaryBuffer buffer = new TemporaryBuffer() {\n@@ -442,7 +450,8 @@ public class AmazonS3 {\n \t\t\tpublic void close() throws IOException {\n \t\t\t\tsuper.close();\n \t\t\t\ttry {\n-\t\t\t\t\tputImpl(bucket, key, md5.digest(), this);\n+\t\t\t\t\tputImpl(bucket, key, md5.digest(), this, monitor,\n+\t\t\t\t\t\t\tmonitorTask);\n \t\t\t\t} finally {\n \t\t\t\t\tdestroy();\n \t\t\t\t}\n@@ -452,7 +461,13 @@ public class AmazonS3 {\n \t}\n \n \tprivate void putImpl(final String bucket, final String key,\n-\t\t\tfinal byte[] csum, final TemporaryBuffer buf) throws IOException {\n+\t\t\tfinal byte[] csum, final TemporaryBuffer buf,\n+\t\t\tProgressMonitor monitor, String monitorTask) throws IOException {\n+\t\tif (monitor == null)\n+\t\t\tmonitor = NullProgressMonitor.INSTANCE;\n+\t\tif (monitorTask == null)\n+\t\t\tmonitorTask = \"Uploading \" + key;\n+\n \t\tfinal String md5str = Base64.encodeBytes(csum);\n \t\tfinal long len = buf.length();\n \t\tfinal String lenstr = String.valueOf(len);\n@@ -465,10 +480,12 @@ public class AmazonS3 {\n \t\t\tauthorize(c);\n \t\t\tc.setDoOutput(true);\n \t\t\tc.setFixedLengthStreamingMode((int) len);\n+\t\t\tmonitor.beginTask(monitorTask, (int) (len / 1024));\n \t\t\tfinal OutputStream os = c.getOutputStream();\n \t\t\ttry {\n-\t\t\t\tbuf.writeTo(os, null);\n+\t\t\t\tbuf.writeTo(os, monitor);\n \t\t\t} finally {\n+\t\t\t\tmonitor.endTask();\n \t\t\t\tos.close();\n \t\t\t}\n \n@@ -641,7 +658,7 @@ public class AmazonS3 {\n \t\t} else if (\"rm\".equals(op) || \"delete\".equals(op)) {\n \t\t\ts3.delete(bucket, key);\n \t\t} else if (\"put\".equals(op)) {\n-\t\t\tfinal OutputStream os = s3.beginPut(bucket, key);\n+\t\t\tfinal OutputStream os = s3.beginPut(bucket, key, null, null);\n \t\t\tfinal byte[] tmp = new byte[2048];\n \t\t\tint n;\n \t\t\twhile ((n = System.in.read(tmp)) > 0)\ndiff --git a/org.spearce.jgit/src/org/spearce/jgit/transport/TransportAmazonS3.java b/org.spearce.jgit/src/org/spearce/jgit/transport/TransportAmazonS3.java\nindex d2f4c83..cd62c5b 100644\n--- a/org.spearce.jgit/src/org/spearce/jgit/transport/TransportAmazonS3.java\n+++ b/org.spearce.jgit/src/org/spearce/jgit/transport/TransportAmazonS3.java\n@@ -54,6 +54,7 @@ import java.util.TreeMap;\n import org.spearce.jgit.errors.NotSupportedException;\n import org.spearce.jgit.errors.TransportException;\n import org.spearce.jgit.lib.ObjectId;\n+import org.spearce.jgit.lib.ProgressMonitor;\n import org.spearce.jgit.lib.Ref;\n import org.spearce.jgit.lib.Repository;\n import org.spearce.jgit.lib.Ref.Storage;\n@@ -236,8 +237,10 @@ class TransportAmazonS3 extends WalkTransport {\n \t\t}\n \n \t\t@Override\n-\t\tOutputStream writeFile(final String path) throws IOException {\n-\t\t\treturn s3.beginPut(bucket, resolveKey(path));\n+\t\tOutputStream writeFile(final String path,\n+\t\t\t\tfinal ProgressMonitor monitor, final String monitorTask)\n+\t\t\t\tthrows IOException {\n+\t\t\treturn s3.beginPut(bucket, resolveKey(path), monitor, monitorTask);\n \t\t}\n \n \t\t@Override\ndiff --git a/org.spearce.jgit/src/org/spearce/jgit/transport/TransportSftp.java b/org.spearce.jgit/src/org/spearce/jgit/transport/TransportSftp.java\nindex a33406b..c2cbe6a 100644\n--- a/org.spearce.jgit/src/org/spearce/jgit/transport/TransportSftp.java\n+++ b/org.spearce.jgit/src/org/spearce/jgit/transport/TransportSftp.java\n@@ -54,6 +54,7 @@ import java.util.TreeMap;\n \n import org.spearce.jgit.errors.TransportException;\n import org.spearce.jgit.lib.ObjectId;\n+import org.spearce.jgit.lib.ProgressMonitor;\n import org.spearce.jgit.lib.Ref;\n import org.spearce.jgit.lib.Repository;\n import org.spearce.jgit.lib.Ref.Storage;\n@@ -294,7 +295,9 @@ class TransportSftp extends WalkTransport {\n \t\t}\n \n \t\t@Override\n-\t\tOutputStream writeFile(final String path) throws IOException {\n+\t\tOutputStream writeFile(final String path,\n+\t\t\t\tfinal ProgressMonitor monitor, final String monitorTask)\n+\t\t\t\tthrows IOException {\n \t\t\ttry {\n \t\t\t\treturn ftp.put(path);\n \t\t\t} catch (SftpException je) {\ndiff --git a/org.spearce.jgit/src/org/spearce/jgit/transport/WalkPushConnection.java b/org.spearce.jgit/src/org/spearce/jgit/transport/WalkPushConnection.java\nindex bb5a653..904a699 100644\n--- a/org.spearce.jgit/src/org/spearce/jgit/transport/WalkPushConnection.java\n+++ b/org.spearce.jgit/src/org/spearce/jgit/transport/WalkPushConnection.java\n@@ -230,14 +230,15 @@ class WalkPushConnection extends BaseConnection implements PushConnection {\n \t\t\t// Write the pack file, then the index, as readers look the\n \t\t\t// other direction (index, then pack file).\n \t\t\t//\n-\t\t\tOutputStream os = dest.writeFile(pathPack);\n+\t\t\tfinal String wt = \"Put \" + base.substring(0, 12);\n+\t\t\tOutputStream os = dest.writeFile(pathPack, monitor, wt + \"..pack\");\n \t\t\ttry {\n \t\t\t\tpw.writePack(os);\n \t\t\t} finally {\n \t\t\t\tos.close();\n \t\t\t}\n \n-\t\t\tos = dest.writeFile(pathIdx);\n+\t\t\tos = dest.writeFile(pathIdx, monitor, wt + \"..idx\");\n \t\t\ttry {\n \t\t\t\tpw.writeIndex(os);\n \t\t\t} finally {\ndiff --git a/org.spearce.jgit/src/org/spearce/jgit/transport/WalkRemoteObjectDatabase.java b/org.spearce.jgit/src/org/spearce/jgit/transport/WalkRemoteObjectDatabase.java\nindex 915faac..c5a5199 100644\n--- a/org.spearce.jgit/src/org/spearce/jgit/transport/WalkRemoteObjectDatabase.java\n+++ b/org.spearce.jgit/src/org/spearce/jgit/transport/WalkRemoteObjectDatabase.java\n@@ -52,6 +52,7 @@ import java.util.Map;\n import org.spearce.jgit.errors.TransportException;\n import org.spearce.jgit.lib.Constants;\n import org.spearce.jgit.lib.ObjectId;\n+import org.spearce.jgit.lib.ProgressMonitor;\n import org.spearce.jgit.lib.Ref;\n import org.spearce.jgit.util.NB;\n \n@@ -213,11 +214,17 @@ abstract class WalkRemoteObjectDatabase {\n \t *         complete the write request. The stream is not buffered and each\n \t *         write may cause a network request/response so callers should\n \t *         buffer to smooth out small writes.\n+\t * @param monitor\n+\t *            (optional) progress monitor to post write completion to during\n+\t *            the stream's close method.\n+\t * @param monitorTask\n+\t *            (optional) task name to display during the close method.\n \t * @throws IOException\n \t *             writing is not supported, or attempting to write the file\n \t *             failed, possibly due to permissions or remote disk full, etc.\n \t */\n-\tOutputStream writeFile(final String path) throws IOException {\n+\tOutputStream writeFile(final String path, final ProgressMonitor monitor,\n+\t\t\tfinal String monitorTask) throws IOException {\n \t\tthrow new IOException(\"Writing of '\" + path + \"' not supported.\");\n \t}\n \n@@ -247,7 +254,7 @@ abstract class WalkRemoteObjectDatabase {\n \t *             failed, possibly due to permissions or remote disk full, etc.\n \t */\n \tvoid writeFile(final String path, final byte[] data) throws IOException {\n-\t\tfinal OutputStream os = writeFile(path);\n+\t\tfinal OutputStream os = writeFile(path, null, null);\n \t\ttry {\n \t\t\tos.write(data);\n \t\t} finally {\n-- \n1.5.6.2.393.g45096\n"},{"id":"82783","messageId":"1215670403-19191-3-git-send-email-spearce@spearce.org","threadId":"14389","inReplyTo":"1215670403-19191-2-git-send-email-spearce@spearce.org","subject":"[JGIT PATCH 2/5] Don't display passwords on the console in fetch/push output","fromName":"Shawn O. Pearce","fromEmail":"spearce@spearce.org","sentAt":"2008-07-10T06:13:20Z","receivedAt":"2008-07-10T06:13:20Z","isPatch":true,"sender":{"key":"spearce@spearce.org","avatar":"https://avatars.githubusercontent.com/u/34844?v=4"},"body":"When we show the URI we just fetched or pushed against there may\nbe a user password embedded in that URI, as saved in the user's\n.git/config file.  We shouldn't display that in public to prying\neyes so nulling it out will give us a copy of the URI without that\nfield in it.\n\nSigned-off-by: Shawn O. Pearce <spearce@spearce.org>\n---\n .../src/org/spearce/jgit/pgm/Fetch.java            |    2 +-\n .../src/org/spearce/jgit/pgm/Push.java             |    2 +-\n 2 files changed, 2 insertions(+), 2 deletions(-)\n\ndiff --git a/org.spearce.jgit/src/org/spearce/jgit/pgm/Fetch.java b/org.spearce.jgit/src/org/spearce/jgit/pgm/Fetch.java\nindex c9c997e..36a0592 100644\n--- a/org.spearce.jgit/src/org/spearce/jgit/pgm/Fetch.java\n+++ b/org.spearce.jgit/src/org/spearce/jgit/pgm/Fetch.java\n@@ -71,7 +71,7 @@ class Fetch extends TextBuiltin {\n \t\t\treturn;\n \n \t\tout.print(\"From \");\n-\t\tout.print(tn.getURI());\n+\t\tout.print(tn.getURI().setPass(null));\n \t\tout.println();\n \t\tfor (final TrackingRefUpdate u : r.getTrackingRefUpdates()) {\n \t\t\tfinal char type = shortTypeOf(u.getResult());\ndiff --git a/org.spearce.jgit/src/org/spearce/jgit/pgm/Push.java b/org.spearce.jgit/src/org/spearce/jgit/pgm/Push.java\nindex cbdf465..8411a11 100644\n--- a/org.spearce.jgit/src/org/spearce/jgit/pgm/Push.java\n+++ b/org.spearce.jgit/src/org/spearce/jgit/pgm/Push.java\n@@ -149,7 +149,7 @@ class Push extends TextBuiltin {\n \t\t\tfinal RemoteRefUpdate rru) {\n \t\tif (first) {\n \t\t\tfirst = false;\n-\t\t\tout.format(\"To %s\\n\", transport.getURI());\n+\t\t\tout.format(\"To %s\\n\", transport.getURI().setPass(null));\n \t\t}\n \n \t\tfinal String remoteName = rru.getRemoteName();\n-- \n1.5.6.2.393.g45096\n"},{"id":"82780","messageId":"1215670403-19191-4-git-send-email-spearce@spearce.org","threadId":"14389","inReplyTo":"1215670403-19191-3-git-send-email-spearce@spearce.org","subject":"[JGIT PATCH 3/5] Reuse the same SSH connection when automatically fetching tags","fromName":"Shawn O. Pearce","fromEmail":"spearce@spearce.org","sentAt":"2008-07-10T06:13:21Z","receivedAt":"2008-07-10T06:13:21Z","isPatch":true,"sender":{"key":"spearce@spearce.org","avatar":"https://avatars.githubusercontent.com/u/34844?v=4"},"body":"If we have to open a second connection to git-upload-pack in order\nto automatically follow and fetch annotated tags we may be able to\nreuse the same JSch Session object and simply run the second stream\nover the already established, encrypted TCP stream.\n\nReusing the Session avoids the overheads associated with performing\npublic/private key authentication and reduces the overall latency\nof the fetch process.\n\nTo make reuse work we cache the Session at the Transport instance\nlevel and ask Transport API users to close the Transport once they\nare completely done talking to this remote repository.  While the\nTransport instance is unclosed however multiple FetchConnection\nand/or PushConnections can be established, with each subsequent\nconnection being cheaper to setup.\n\nIn the future we may also support reusing the same Session onto\ndifferent Transport instances, permitting access to multiple\nrepositories hosted on the same remote SSH server.\n\nSigned-off-by: Shawn O. Pearce <spearce@spearce.org>\n---\n .../org/spearce/egit/core/op/CloneOperation.java   |   10 +++-\n .../egit/ui/internal/clone/SourceBranchPage.java   |    1 +\n .../spearce/jgit/transport/PushProcessTest.java    |    5 ++\n .../org/spearce/jgit/transport/TransportTest.java  |   10 +++\n .../src/org/spearce/jgit/pgm/Fetch.java            |   17 ++++--\n .../src/org/spearce/jgit/pgm/LsRemote.java         |    1 +\n .../src/org/spearce/jgit/transport/Transport.java  |   10 +++\n .../spearce/jgit/transport/TransportAmazonS3.java  |    5 ++\n .../spearce/jgit/transport/TransportBundle.java    |    5 ++\n .../spearce/jgit/transport/TransportGitAnon.java   |    5 ++\n .../spearce/jgit/transport/TransportGitSsh.java    |   60 +++++++++-----------\n .../org/spearce/jgit/transport/TransportHttp.java  |    5 ++\n .../org/spearce/jgit/transport/TransportLocal.java |    5 ++\n .../org/spearce/jgit/transport/TransportSftp.java  |   52 +++++++++---------\n 14 files changed, 124 insertions(+), 67 deletions(-)\n\ndiff --git a/org.spearce.egit.core/src/org/spearce/egit/core/op/CloneOperation.java b/org.spearce.egit.core/src/org/spearce/egit/core/op/CloneOperation.java\nindex 7600e3b..656f3cb 100644\n--- a/org.spearce.egit.core/src/org/spearce/egit/core/op/CloneOperation.java\n+++ b/org.spearce.egit.core/src/org/spearce/egit/core/op/CloneOperation.java\n@@ -85,8 +85,14 @@ public class CloneOperation implements IRunnableWithProgress {\n \n \tprivate void doFetch(final IProgressMonitor monitor)\n \t\t\tthrows NotSupportedException, TransportException {\n-\t\tfetchResult = Transport.open(local, remote).fetch(\n-\t\t\t\tnew EclipseGitProgressTransformer(monitor), null);\n+\t\tfinal Transport tn = Transport.open(local, remote);\n+\t\ttry {\n+\t\t\tfinal EclipseGitProgressTransformer pm;\n+\t\t\tpm = new EclipseGitProgressTransformer(monitor);\n+\t\t\tfetchResult = tn.fetch(pm, null);\n+\t\t} finally {\n+\t\t\ttn.close();\n+\t\t}\n \t}\n \n \tprivate void doCheckout(final IProgressMonitor monitor) throws IOException {\ndiff --git a/org.spearce.egit.ui/src/org/spearce/egit/ui/internal/clone/SourceBranchPage.java b/org.spearce.egit.ui/src/org/spearce/egit/ui/internal/clone/SourceBranchPage.java\nindex b704aaa..b0aba1e 100644\n--- a/org.spearce.egit.ui/src/org/spearce/egit/ui/internal/clone/SourceBranchPage.java\n+++ b/org.spearce.egit.ui/src/org/spearce/egit/ui/internal/clone/SourceBranchPage.java\n@@ -241,6 +241,7 @@ class SourceBranchPage extends WizardPage {\n \t\t\t\t\t\t\tadv = fn.getRefs();\n \t\t\t\t\t\t} finally {\n \t\t\t\t\t\t\tfn.close();\n+\t\t\t\t\t\t\ttn.close();\n \t\t\t\t\t\t}\n \n \t\t\t\t\t\tfinal Ref idHEAD = fn.getRef(Constants.HEAD);\ndiff --git a/org.spearce.jgit.test/tst/org/spearce/jgit/transport/PushProcessTest.java b/org.spearce.jgit.test/tst/org/spearce/jgit/transport/PushProcessTest.java\nindex fae1cbb..357e6b7 100644\n--- a/org.spearce.jgit.test/tst/org/spearce/jgit/transport/PushProcessTest.java\n+++ b/org.spearce.jgit.test/tst/org/spearce/jgit/transport/PushProcessTest.java\n@@ -379,6 +379,11 @@ public class PushProcessTest extends RepositoryTestCase {\n \t\t\t\tTransportException {\n \t\t\treturn new MockPushConnection();\n \t\t}\n+\n+\t\t@Override\n+\t\tpublic void close() {\n+\t\t\t// nothing here\n+\t\t}\n \t}\n \n \tprivate class MockPushConnection extends BaseConnection implements\ndiff --git a/org.spearce.jgit.test/tst/org/spearce/jgit/transport/TransportTest.java b/org.spearce.jgit.test/tst/org/spearce/jgit/transport/TransportTest.java\nindex 10f6651..dc1cb21 100644\n--- a/org.spearce.jgit.test/tst/org/spearce/jgit/transport/TransportTest.java\n+++ b/org.spearce.jgit.test/tst/org/spearce/jgit/transport/TransportTest.java\n@@ -56,6 +56,16 @@ public class TransportTest extends RepositoryTestCase {\n \t\tfinal RepositoryConfig config = db.getConfig();\n \t\tremoteConfig = new RemoteConfig(config, \"test\");\n \t\tremoteConfig.addURI(new URIish(\"http://everyones.loves.git/u/2\"));\n+\t\ttransport = null;\n+\t}\n+\n+\t@Override\n+\tprotected void tearDown() throws Exception {\n+\t\tif (transport != null) {\n+\t\t\ttransport.close();\n+\t\t\ttransport = null;\n+\t\t}\n+\t\tsuper.tearDown();\n \t}\n \n \t/**\ndiff --git a/org.spearce.jgit/src/org/spearce/jgit/pgm/Fetch.java b/org.spearce.jgit/src/org/spearce/jgit/pgm/Fetch.java\nindex 36a0592..c361c26 100644\n--- a/org.spearce.jgit/src/org/spearce/jgit/pgm/Fetch.java\n+++ b/org.spearce.jgit/src/org/spearce/jgit/pgm/Fetch.java\n@@ -63,12 +63,17 @@ class Fetch extends TextBuiltin {\n \t\t\targs = new String[] { \"origin\" };\n \n \t\tfinal Transport tn = Transport.open(db, args[argi++]);\n-\t\tfinal List<RefSpec> toget = new ArrayList<RefSpec>();\n-\t\tfor (; argi < args.length; argi++)\n-\t\t\ttoget.add(new RefSpec(args[argi]));\n-\t\tfinal FetchResult r = tn.fetch(new TextProgressMonitor(), toget);\n-\t\tif (r.getTrackingRefUpdates().isEmpty())\n-\t\t\treturn;\n+\t\tfinal FetchResult r;\n+\t\ttry {\n+\t\t\tfinal List<RefSpec> toget = new ArrayList<RefSpec>();\n+\t\t\tfor (; argi < args.length; argi++)\n+\t\t\t\ttoget.add(new RefSpec(args[argi]));\n+\t\t\tr = tn.fetch(new TextProgressMonitor(), toget);\n+\t\t\tif (r.getTrackingRefUpdates().isEmpty())\n+\t\t\t\treturn;\n+\t\t} finally {\n+\t\t\ttn.close();\n+\t\t}\n \n \t\tout.print(\"From \");\n \t\tout.print(tn.getURI().setPass(null));\ndiff --git a/org.spearce.jgit/src/org/spearce/jgit/pgm/LsRemote.java b/org.spearce.jgit/src/org/spearce/jgit/pgm/LsRemote.java\nindex dbdfeb3..21e02ec 100644\n--- a/org.spearce.jgit/src/org/spearce/jgit/pgm/LsRemote.java\n+++ b/org.spearce.jgit/src/org/spearce/jgit/pgm/LsRemote.java\n@@ -70,6 +70,7 @@ class LsRemote extends TextBuiltin {\n \t\t\t}\n \t\t} finally {\n \t\t\tc.close();\n+\t\t\ttn.close();\n \t\t}\n \t}\n \ndiff --git a/org.spearce.jgit/src/org/spearce/jgit/transport/Transport.java b/org.spearce.jgit/src/org/spearce/jgit/transport/Transport.java\nindex b962162..5bec4d2 100644\n--- a/org.spearce.jgit/src/org/spearce/jgit/transport/Transport.java\n+++ b/org.spearce.jgit/src/org/spearce/jgit/transport/Transport.java\n@@ -527,6 +527,16 @@ public abstract class Transport {\n \tpublic abstract PushConnection openPush() throws NotSupportedException,\n \t\t\tTransportException;\n \n+\t/**\n+\t * Close any resources used by this transport.\n+\t * <p>\n+\t * If the remote repository is contacted by a network socket this method\n+\t * must close that network socket, disconnecting the two peers. If the\n+\t * remote repository is actually local (same system) this method must close\n+\t * any open file handles used to read the \"remote\" repository.\n+\t */\n+\tpublic abstract void close();\n+\n \tprivate Collection<RefSpec> expandPushWildcardsFor(\n \t\t\tfinal Collection<RefSpec> specs) {\n \t\tfinal Map<String, Ref> localRefs = local.getAllRefs();\ndiff --git a/org.spearce.jgit/src/org/spearce/jgit/transport/TransportAmazonS3.java b/org.spearce.jgit/src/org/spearce/jgit/transport/TransportAmazonS3.java\nindex cd62c5b..9aa2567 100644\n--- a/org.spearce.jgit/src/org/spearce/jgit/transport/TransportAmazonS3.java\n+++ b/org.spearce.jgit/src/org/spearce/jgit/transport/TransportAmazonS3.java\n@@ -159,6 +159,11 @@ class TransportAmazonS3 extends WalkTransport {\n \t\treturn r;\n \t}\n \n+\t@Override\n+\tpublic void close() {\n+\t\t// No explicit connections are maintained.\n+\t}\n+\n \tclass DatabaseS3 extends WalkRemoteObjectDatabase {\n \t\tprivate final String bucketName;\n \ndiff --git a/org.spearce.jgit/src/org/spearce/jgit/transport/TransportBundle.java b/org.spearce.jgit/src/org/spearce/jgit/transport/TransportBundle.java\nindex 6169179..24d49eb 100644\n--- a/org.spearce.jgit/src/org/spearce/jgit/transport/TransportBundle.java\n+++ b/org.spearce.jgit/src/org/spearce/jgit/transport/TransportBundle.java\n@@ -101,6 +101,11 @@ class TransportBundle extends PackTransport {\n \t\t\t\t\"Push is not supported for bundle transport\");\n \t}\n \n+\t@Override\n+\tpublic void close() {\n+\t\t// Resources must be established per-connection.\n+\t}\n+\n \tclass BundleFetchConnection extends BaseFetchConnection {\n \t\tFileInputStream in;\n \ndiff --git a/org.spearce.jgit/src/org/spearce/jgit/transport/TransportGitAnon.java b/org.spearce.jgit/src/org/spearce/jgit/transport/TransportGitAnon.java\nindex 8a78099..a80c335 100644\n--- a/org.spearce.jgit/src/org/spearce/jgit/transport/TransportGitAnon.java\n+++ b/org.spearce.jgit/src/org/spearce/jgit/transport/TransportGitAnon.java\n@@ -77,6 +77,11 @@ class TransportGitAnon extends PackTransport {\n \t\treturn new TcpPushConnection();\n \t}\n \n+\t@Override\n+\tpublic void close() {\n+\t\t// Resources must be established per-connection.\n+\t}\n+\n \tSocket openConnection() throws TransportException {\n \t\tfinal int port = uri.getPort() > 0 ? uri.getPort() : GIT_PORT;\n \t\ttry {\ndiff --git a/org.spearce.jgit/src/org/spearce/jgit/transport/TransportGitSsh.java b/org.spearce.jgit/src/org/spearce/jgit/transport/TransportGitSsh.java\nindex 1bbdf04..b169f4c 100644\n--- a/org.spearce.jgit/src/org/spearce/jgit/transport/TransportGitSsh.java\n+++ b/org.spearce.jgit/src/org/spearce/jgit/transport/TransportGitSsh.java\n@@ -78,7 +78,10 @@ class TransportGitSsh extends PackTransport {\n \t\treturn false;\n \t}\n \n-\tfinal SshSessionFactory sch;\n+\tprivate final SshSessionFactory sch;\n+\n+\tprivate Session sock;\n+\n \tOutputStream errStream;\n \n \tTransportGitSsh(final Repository local, final URIish uri) {\n@@ -96,6 +99,17 @@ class TransportGitSsh extends PackTransport {\n \t\treturn new SshPushConnection();\n \t}\n \n+\t@Override\n+\tpublic void close() {\n+\t\tif (sock != null) {\n+\t\t\ttry {\n+\t\t\t\tsch.releaseSession(sock);\n+\t\t\t} finally {\n+\t\t\t\tsock = null;\n+\t\t\t}\n+\t\t}\n+\t}\n+\n \tprivate static void sqMinimal(final StringBuilder cmd, final String val) {\n \t\tif (val.matches(\"^[a-zA-Z0-9._/-]*$\")) {\n \t\t\t// If the string matches only generally safe characters\n@@ -152,17 +166,18 @@ class TransportGitSsh extends PackTransport {\n \t\tcmd.append('\\'');\n \t}\n \n-\tSession openSession() throws TransportException {\n+\tprivate void initSession() throws TransportException {\n+\t\tif (sock != null)\n+\t\t\treturn;\n+\n \t\tfinal String user = uri.getUser();\n \t\tfinal String pass = uri.getPass();\n \t\tfinal String host = uri.getHost();\n \t\tfinal int port = uri.getPort();\n \t\ttry {\n-\t\t\tfinal Session session;\n-\t\t\tsession = sch.getSession(user, pass, host, port);\n-\t\t\tif (!session.isConnected())\n-\t\t\t\tsession.connect();\n-\t\t\treturn session;\n+\t\t\tsock = sch.getSession(user, pass, host, port);\n+\t\t\tif (!sock.isConnected())\n+\t\t\t\tsock.connect();\n \t\t} catch (JSchException je) {\n \t\t\tfinal Throwable c = je.getCause();\n \t\t\tif (c instanceof UnknownHostException)\n@@ -173,8 +188,9 @@ class TransportGitSsh extends PackTransport {\n \t\t}\n \t}\n \n-\tChannelExec exec(final Session sock, final String exe)\n-\t\t\tthrows TransportException {\n+\tChannelExec exec(final String exe) throws TransportException {\n+\t\tinitSession();\n+\n \t\ttry {\n \t\t\tfinal ChannelExec channel = (ChannelExec) sock.openChannel(\"exec\");\n \t\t\tString path = uri.getPath();\n@@ -202,15 +218,12 @@ class TransportGitSsh extends PackTransport {\n \t}\n \n \tclass SshFetchConnection extends BasePackFetchConnection {\n-\t\tprivate Session session;\n-\n \t\tprivate ChannelExec channel;\n \n \t\tSshFetchConnection() throws TransportException {\n \t\t\tsuper(TransportGitSsh.this);\n \t\t\ttry {\n-\t\t\t\tsession = openSession();\n-\t\t\t\tchannel = exec(session, getOptionUploadPack());\n+\t\t\t\tchannel = exec(getOptionUploadPack());\n \n \t\t\t\tif (channel.isConnected())\n \t\t\t\t\tinit(channel.getInputStream(), channel.getOutputStream());\n@@ -240,27 +253,16 @@ class TransportGitSsh extends PackTransport {\n \t\t\t\t\tchannel = null;\n \t\t\t\t}\n \t\t\t}\n-\n-\t\t\tif (session != null) {\n-\t\t\t\ttry {\n-\t\t\t\t\tsch.releaseSession(session);\n-\t\t\t\t} finally {\n-\t\t\t\t\tsession = null;\n-\t\t\t\t}\n-\t\t\t}\n \t\t}\n \t}\n \n \tclass SshPushConnection extends BasePackPushConnection {\n-\t\tprivate Session session;\n-\n \t\tprivate ChannelExec channel;\n \n \t\tSshPushConnection() throws TransportException {\n \t\t\tsuper(TransportGitSsh.this);\n \t\t\ttry {\n-\t\t\t\tsession = openSession();\n-\t\t\t\tchannel = exec(session, getOptionReceivePack());\n+\t\t\t\tchannel = exec(getOptionReceivePack());\n \t\t\t\tinit(channel.getInputStream(), channel.getOutputStream());\n \t\t\t} catch (TransportException err) {\n \t\t\t\tclose();\n@@ -285,14 +287,6 @@ class TransportGitSsh extends PackTransport {\n \t\t\t\t\tchannel = null;\n \t\t\t\t}\n \t\t\t}\n-\n-\t\t\tif (session != null) {\n-\t\t\t\ttry {\n-\t\t\t\t\tsch.releaseSession(session);\n-\t\t\t\t} finally {\n-\t\t\t\t\tsession = null;\n-\t\t\t\t}\n-\t\t\t}\n \t\t}\n \t}\n }\ndiff --git a/org.spearce.jgit/src/org/spearce/jgit/transport/TransportHttp.java b/org.spearce.jgit/src/org/spearce/jgit/transport/TransportHttp.java\nindex 9351a12..1357e58 100644\n--- a/org.spearce.jgit/src/org/spearce/jgit/transport/TransportHttp.java\n+++ b/org.spearce.jgit/src/org/spearce/jgit/transport/TransportHttp.java\n@@ -106,6 +106,11 @@ class TransportHttp extends WalkTransport {\n \t\treturn r;\n \t}\n \n+\t@Override\n+\tpublic void close() {\n+\t\t// No explicit connections are maintained.\n+\t}\n+\n \tclass HttpObjectDB extends WalkRemoteObjectDatabase {\n \t\tprivate final URL objectsUrl;\n \ndiff --git a/org.spearce.jgit/src/org/spearce/jgit/transport/TransportLocal.java b/org.spearce.jgit/src/org/spearce/jgit/transport/TransportLocal.java\nindex 155d59f..d74f1b3 100644\n--- a/org.spearce.jgit/src/org/spearce/jgit/transport/TransportLocal.java\n+++ b/org.spearce.jgit/src/org/spearce/jgit/transport/TransportLocal.java\n@@ -93,6 +93,11 @@ class TransportLocal extends PackTransport {\n \t\treturn new LocalPushConnection();\n \t}\n \n+\t@Override\n+\tpublic void close() {\n+\t\t// Resources must be established per-connection.\n+\t}\n+\n \tprotected Process startProcessWithErrStream(final String cmd)\n \t\t\tthrows TransportException {\n \t\ttry {\ndiff --git a/org.spearce.jgit/src/org/spearce/jgit/transport/TransportSftp.java b/org.spearce.jgit/src/org/spearce/jgit/transport/TransportSftp.java\nindex c2cbe6a..6a5df07 100644\n--- a/org.spearce.jgit/src/org/spearce/jgit/transport/TransportSftp.java\n+++ b/org.spearce.jgit/src/org/spearce/jgit/transport/TransportSftp.java\n@@ -91,7 +91,9 @@ class TransportSftp extends WalkTransport {\n \t\treturn uri.isRemote() && \"sftp\".equals(uri.getScheme());\n \t}\n \n-\tfinal SshSessionFactory sch;\n+\tprivate final SshSessionFactory sch;\n+\n+\tprivate Session sock;\n \n \tTransportSftp(final Repository local, final URIish uri) {\n \t\tsuper(local, uri);\n@@ -114,17 +116,29 @@ class TransportSftp extends WalkTransport {\n \t\treturn r;\n \t}\n \n-\tSession openSession() throws TransportException {\n+\t@Override\n+\tpublic void close() {\n+\t\tif (sock != null) {\n+\t\t\ttry {\n+\t\t\t\tsch.releaseSession(sock);\n+\t\t\t} finally {\n+\t\t\t\tsock = null;\n+\t\t\t}\n+\t\t}\n+\t}\n+\n+\tprivate void initSession() throws TransportException {\n+\t\tif (sock != null)\n+\t\t\treturn;\n+\n \t\tfinal String user = uri.getUser();\n \t\tfinal String pass = uri.getPass();\n \t\tfinal String host = uri.getHost();\n \t\tfinal int port = uri.getPort();\n \t\ttry {\n-\t\t\tfinal Session session;\n-\t\t\tsession = sch.getSession(user, pass, host, port);\n-\t\t\tif (!session.isConnected())\n-\t\t\t\tsession.connect();\n-\t\t\treturn session;\n+\t\t\tsock = sch.getSession(user, pass, host, port);\n+\t\t\tif (!sock.isConnected())\n+\t\t\t\tsock.connect();\n \t\t} catch (JSchException je) {\n \t\t\tfinal Throwable c = je.getCause();\n \t\t\tif (c instanceof UnknownHostException)\n@@ -135,7 +149,9 @@ class TransportSftp extends WalkTransport {\n \t\t}\n \t}\n \n-\tChannelSftp open(final Session sock) throws TransportException {\n+\tChannelSftp newSftp() throws TransportException {\n+\t\tinitSession();\n+\n \t\ttry {\n \t\t\tfinal Channel channel = sock.openChannel(\"sftp\");\n \t\t\tchannel.connect();\n@@ -148,10 +164,6 @@ class TransportSftp extends WalkTransport {\n \tclass SftpObjectDB extends WalkRemoteObjectDatabase {\n \t\tprivate final String objectsPath;\n \n-\t\tprivate final boolean sessionOwner;\n-\n-\t\tprivate Session session;\n-\n \t\tprivate ChannelSftp ftp;\n \n \t\tSftpObjectDB(String path) throws TransportException {\n@@ -160,9 +172,7 @@ class TransportSftp extends WalkTransport {\n \t\t\tif (path.startsWith(\"~/\"))\n \t\t\t\tpath = path.substring(2);\n \t\t\ttry {\n-\t\t\t\tsession = openSession();\n-\t\t\t\tsessionOwner = true;\n-\t\t\t\tftp = TransportSftp.this.open(session);\n+\t\t\t\tftp = newSftp();\n \t\t\t\tftp.cd(path);\n \t\t\t\tftp.cd(\"objects\");\n \t\t\t\tobjectsPath = ftp.pwd();\n@@ -177,10 +187,8 @@ class TransportSftp extends WalkTransport {\n \n \t\tSftpObjectDB(final SftpObjectDB parent, final String p)\n \t\t\t\tthrows TransportException {\n-\t\t\tsessionOwner = false;\n-\t\t\tsession = parent.session;\n \t\t\ttry {\n-\t\t\t\tftp = TransportSftp.this.open(session);\n+\t\t\t\tftp = newSftp();\n \t\t\t\tftp.cd(parent.objectsPath);\n \t\t\t\tftp.cd(p);\n \t\t\t\tobjectsPath = ftp.pwd();\n@@ -452,14 +460,6 @@ class TransportSftp extends WalkTransport {\n \t\t\t\t\tftp = null;\n \t\t\t\t}\n \t\t\t}\n-\n-\t\t\tif (sessionOwner && session != null) {\n-\t\t\t\ttry {\n-\t\t\t\t\tsch.releaseSession(session);\n-\t\t\t\t} finally {\n-\t\t\t\t\tsession = null;\n-\t\t\t\t}\n-\t\t\t}\n \t\t}\n \t}\n }\n-- \n1.5.6.2.393.g45096\n"},{"id":"82784","messageId":"1215670403-19191-5-git-send-email-spearce@spearce.org","threadId":"14389","inReplyTo":"1215670403-19191-4-git-send-email-spearce@spearce.org","subject":"[JGIT PATCH 4/5] Report remote SSH execution errors during push via TransportException","fromName":"Shawn O. Pearce","fromEmail":"spearce@spearce.org","sentAt":"2008-07-10T06:13:22Z","receivedAt":"2008-07-10T06:13:22Z","isPatch":true,"sender":{"key":"spearce@spearce.org","avatar":"https://avatars.githubusercontent.com/u/34844?v=4"},"body":"If the remote side failed to execute git-receive-pack we may\nhave the reason why on the stderr stream of the channel, as\nthe remote shell may have failed execution.\n\nSigned-off-by: Shawn O. Pearce <spearce@spearce.org>\n---\n .../spearce/jgit/transport/TransportGitSsh.java    |    9 +++++++--\n 1 files changed, 7 insertions(+), 2 deletions(-)\n\ndiff --git a/org.spearce.jgit/src/org/spearce/jgit/transport/TransportGitSsh.java b/org.spearce.jgit/src/org/spearce/jgit/transport/TransportGitSsh.java\nindex b169f4c..9a6c719 100644\n--- a/org.spearce.jgit/src/org/spearce/jgit/transport/TransportGitSsh.java\n+++ b/org.spearce.jgit/src/org/spearce/jgit/transport/TransportGitSsh.java\n@@ -228,7 +228,7 @@ class TransportGitSsh extends PackTransport {\n \t\t\t\tif (channel.isConnected())\n \t\t\t\t\tinit(channel.getInputStream(), channel.getOutputStream());\n \t\t\t\telse\n-\t\t\t\t\tthrow new TransportException(errStream.toString());\n+\t\t\t\t\tthrow new TransportException(uri, errStream.toString());\n \n \t\t\t} catch (TransportException err) {\n \t\t\t\tclose();\n@@ -263,7 +263,12 @@ class TransportGitSsh extends PackTransport {\n \t\t\tsuper(TransportGitSsh.this);\n \t\t\ttry {\n \t\t\t\tchannel = exec(getOptionReceivePack());\n-\t\t\t\tinit(channel.getInputStream(), channel.getOutputStream());\n+\n+\t\t\t\tif (channel.isConnected())\n+\t\t\t\t\tinit(channel.getInputStream(), channel.getOutputStream());\n+\t\t\t\telse\n+\t\t\t\t\tthrow new TransportException(uri, errStream.toString());\n+\n \t\t\t} catch (TransportException err) {\n \t\t\t\tclose();\n \t\t\t\tthrow err;\n-- \n1.5.6.2.393.g45096\n"},{"id":"82781","messageId":"1215670403-19191-6-git-send-email-spearce@spearce.org","threadId":"14389","inReplyTo":"1215670403-19191-5-git-send-email-spearce@spearce.org","subject":"[JGIT PATCH 5/5] Explicitly capture the stderr from a failed SSH fetch or push","fromName":"Shawn O. Pearce","fromEmail":"spearce@spearce.org","sentAt":"2008-07-10T06:13:23Z","receivedAt":"2008-07-10T06:13:23Z","isPatch":true,"sender":{"key":"spearce@spearce.org","avatar":"https://avatars.githubusercontent.com/u/34844?v=4"},"body":"If the remote command name is not found on the remote system we are\nlikely to get a shell error sent to the channel's stderr stream,\nand yet the channel is connected.  So we don't see the problem\nuntil we try to read the advertised refs, which is somewhat late.\n\nIf we get EOF before the first advertised ref it is a very good\nindication that the remote side did not start the process we wanted\nit to.  Tossing a specialized exception allows the SSH transport\nto offer up the contents of the stderr channel (if any) as possible\nindication of why the repository does not exist.\n\nSigned-off-by: Shawn O. Pearce <spearce@spearce.org>\n---\n .../spearce/egit/ui/EclipseSshSessionFactory.java  |    5 +-\n .../jgit/errors/NoRemoteRepositoryException.java   |   59 ++++++++++++++++++++\n .../spearce/jgit/transport/BasePackConnection.java |    3 +-\n .../jgit/transport/DefaultSshSessionFactory.java   |   30 ++++++++++-\n .../spearce/jgit/transport/TransportGitSsh.java    |   34 +++++++++++-\n 5 files changed, 126 insertions(+), 5 deletions(-)\n create mode 100644 org.spearce.jgit/src/org/spearce/jgit/errors/NoRemoteRepositoryException.java\n\ndiff --git a/org.spearce.egit.ui/src/org/spearce/egit/ui/EclipseSshSessionFactory.java b/org.spearce.egit.ui/src/org/spearce/egit/ui/EclipseSshSessionFactory.java\nindex 144d47d..8f80373 100644\n--- a/org.spearce.egit.ui/src/org/spearce/egit/ui/EclipseSshSessionFactory.java\n+++ b/org.spearce.egit.ui/src/org/spearce/egit/ui/EclipseSshSessionFactory.java\n@@ -55,7 +55,10 @@ class EclipseSshSessionFactory extends SshSessionFactory {\n \t\t\tStringBuilder sb = new StringBuilder();\n \n \t\t\tpublic String toString() {\n-\t\t\t\treturn all.toString();\n+\t\t\t\tString r = all.toString();\n+\t\t\t\twhile (r.endsWith(\"\\n\"))\n+\t\t\t\t\tr = r.substring(0, r.length() - 1);\n+\t\t\t\treturn r;\n \t\t\t}\n \n \t\t\t@Override\ndiff --git a/org.spearce.jgit/src/org/spearce/jgit/errors/NoRemoteRepositoryException.java b/org.spearce.jgit/src/org/spearce/jgit/errors/NoRemoteRepositoryException.java\nnew file mode 100644\nindex 0000000..604ec4d\n--- /dev/null\n+++ b/org.spearce.jgit/src/org/spearce/jgit/errors/NoRemoteRepositoryException.java\n@@ -0,0 +1,59 @@\n+/*\n+ * Copyright (C) 2008, Shawn O. Pearce <spearce@spearce.org>\n+ *\n+ * All rights reserved.\n+ *\n+ * Redistribution and use in source and binary forms, with or\n+ * without modification, are permitted provided that the following\n+ * conditions are met:\n+ *\n+ * - Redistributions of source code must retain the above copyright\n+ *   notice, this list of conditions and the following disclaimer.\n+ *\n+ * - Redistributions in binary form must reproduce the above\n+ *   copyright notice, this list of conditions and the following\n+ *   disclaimer in the documentation and/or other materials provided\n+ *   with the distribution.\n+ *\n+ * - Neither the name of the Git Development Community nor the\n+ *   names of its contributors may be used to endorse or promote\n+ *   products derived from this software without specific prior\n+ *   written permission.\n+ *\n+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND\n+ * CONTRIBUTORS \"AS IS\" AND ANY EXPRESS OR IMPLIED WARRANTIES,\n+ * INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES\n+ * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE\n+ * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR\n+ * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,\n+ * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT\n+ * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;\n+ * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER\n+ * CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT,\n+ * STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)\n+ * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF\n+ * ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.\n+ */\n+\n+package org.spearce.jgit.errors;\n+\n+import org.spearce.jgit.transport.URIish;\n+\n+/**\n+ * Indicates a remote repository does not exist.\n+ */\n+public class NoRemoteRepositoryException extends TransportException {\n+\tprivate static final long serialVersionUID = 1L;\n+\n+\t/**\n+\t * Constructs an exception indicating a repository does not exist.\n+\t *\n+\t * @param uri\n+\t *            URI used for transport\n+\t * @param s\n+\t *            message\n+\t */\n+\tpublic NoRemoteRepositoryException(final URIish uri, final String s) {\n+\t\tsuper(uri, s);\n+\t}\n+}\ndiff --git a/org.spearce.jgit/src/org/spearce/jgit/transport/BasePackConnection.java b/org.spearce.jgit/src/org/spearce/jgit/transport/BasePackConnection.java\nindex 7dc4620..52f3f48 100644\n--- a/org.spearce.jgit/src/org/spearce/jgit/transport/BasePackConnection.java\n+++ b/org.spearce.jgit/src/org/spearce/jgit/transport/BasePackConnection.java\n@@ -49,6 +49,7 @@ import java.util.HashSet;\n import java.util.LinkedHashMap;\n import java.util.Set;\n \n+import org.spearce.jgit.errors.NoRemoteRepositoryException;\n import org.spearce.jgit.errors.PackProtocolException;\n import org.spearce.jgit.errors.TransportException;\n import org.spearce.jgit.lib.ObjectId;\n@@ -129,7 +130,7 @@ abstract class BasePackConnection extends BaseConnection {\n \t\t\t\tline = pckIn.readString();\n \t\t\t} catch (EOFException eof) {\n \t\t\t\tif (avail.isEmpty())\n-\t\t\t\t\tthrow new TransportException(uri, \"not found.\");\n+\t\t\t\t\tthrow new NoRemoteRepositoryException(uri, \"not found.\");\n \t\t\t\tthrow eof;\n \t\t\t}\n \ndiff --git a/org.spearce.jgit/src/org/spearce/jgit/transport/DefaultSshSessionFactory.java b/org.spearce.jgit/src/org/spearce/jgit/transport/DefaultSshSessionFactory.java\nindex 5924a04..b4578d4 100644\n--- a/org.spearce.jgit/src/org/spearce/jgit/transport/DefaultSshSessionFactory.java\n+++ b/org.spearce.jgit/src/org/spearce/jgit/transport/DefaultSshSessionFactory.java\n@@ -253,6 +253,34 @@ class DefaultSshSessionFactory extends SshSessionFactory {\n \n \t@Override\n \tpublic OutputStream getErrorStream() {\n-\t\treturn System.err;\n+\t\treturn new OutputStream() {\n+\t\t\tprivate StringBuilder all = new StringBuilder();\n+\n+\t\t\tprivate StringBuilder sb = new StringBuilder();\n+\n+\t\t\tpublic String toString() {\n+\t\t\t\tString r = all.toString();\n+\t\t\t\twhile (r.endsWith(\"\\n\"))\n+\t\t\t\t\tr = r.substring(0, r.length() - 1);\n+\t\t\t\treturn r;\n+\t\t\t}\n+\n+\t\t\t@Override\n+\t\t\tpublic void write(final int b) throws IOException {\n+\t\t\t\tif (b == '\\r') {\n+\t\t\t\t\tSystem.err.print('\\r');\n+\t\t\t\t\treturn;\n+\t\t\t\t}\n+\n+\t\t\t\tsb.append((char) b);\n+\n+\t\t\t\tif (b == '\\n') {\n+\t\t\t\t\tfinal String line = sb.toString();\n+\t\t\t\t\tSystem.err.print(line);\n+\t\t\t\t\tall.append(line);\n+\t\t\t\t\tsb = new StringBuilder();\n+\t\t\t\t}\n+\t\t\t}\n+\t\t};\n \t}\n }\ndiff --git a/org.spearce.jgit/src/org/spearce/jgit/transport/TransportGitSsh.java b/org.spearce.jgit/src/org/spearce/jgit/transport/TransportGitSsh.java\nindex 9a6c719..3f2cd37 100644\n--- a/org.spearce.jgit/src/org/spearce/jgit/transport/TransportGitSsh.java\n+++ b/org.spearce.jgit/src/org/spearce/jgit/transport/TransportGitSsh.java\n@@ -44,6 +44,7 @@ import java.io.OutputStream;\n import java.net.ConnectException;\n import java.net.UnknownHostException;\n \n+import org.spearce.jgit.errors.NoRemoteRepositoryException;\n import org.spearce.jgit.errors.TransportException;\n import org.spearce.jgit.lib.Repository;\n \n@@ -217,6 +218,25 @@ class TransportGitSsh extends PackTransport {\n \t\t}\n \t}\n \n+\tNoRemoteRepositoryException cleanNotFound(NoRemoteRepositoryException nf) {\n+\t\tString why = errStream.toString();\n+\t\tif (why == null || why.length() == 0)\n+\t\t\treturn nf;\n+\n+\t\tString path = uri.getPath();\n+\t\tif (uri.getScheme() != null && uri.getPath().startsWith(\"/~\"))\n+\t\t\tpath = uri.getPath().substring(1);\n+\n+\t\tfinal StringBuilder pfx = new StringBuilder();\n+\t\tpfx.append(\"fatal: \");\n+\t\tsqAlways(pfx, path);\n+\t\tpfx.append(\": \");\n+\t\tif (why.startsWith(pfx.toString()))\n+\t\t\twhy = why.substring(pfx.length());\n+\n+\t\treturn new NoRemoteRepositoryException(uri, why);\n+\t}\n+\n \tclass SshFetchConnection extends BasePackFetchConnection {\n \t\tprivate ChannelExec channel;\n \n@@ -238,7 +258,12 @@ class TransportGitSsh extends PackTransport {\n \t\t\t\tthrow new TransportException(uri,\n \t\t\t\t\t\t\"remote hung up unexpectedly\", err);\n \t\t\t}\n-\t\t\treadAdvertisedRefs();\n+\n+\t\t\ttry {\n+\t\t\t\treadAdvertisedRefs();\n+\t\t\t} catch (NoRemoteRepositoryException notFound) {\n+\t\t\t\tthrow cleanNotFound(notFound);\n+\t\t\t}\n \t\t}\n \n \t\t@Override\n@@ -277,7 +302,12 @@ class TransportGitSsh extends PackTransport {\n \t\t\t\tthrow new TransportException(uri,\n \t\t\t\t\t\t\"remote hung up unexpectedly\", err);\n \t\t\t}\n-\t\t\treadAdvertisedRefs();\n+\n+\t\t\ttry {\n+\t\t\t\treadAdvertisedRefs();\n+\t\t\t} catch (NoRemoteRepositoryException notFound) {\n+\t\t\t\tthrow cleanNotFound(notFound);\n+\t\t\t}\n \t\t}\n \n \t\t@Override\n-- \n1.5.6.2.393.g45096\n"},{"id":"82835","messageId":"200807102056.43912.robin.rosenberg@dewire.com","threadId":"14389","inReplyTo":"1215670403-19191-3-git-send-email-spearce@spearce.org","subject":"Re: [JGIT PATCH 2/5] Don't display passwords on the console in fetch/push output","fromName":"Robin Rosenberg","fromEmail":"robin.rosenberg@dewire.com","sentAt":"2008-07-10T18:56:43Z","receivedAt":"2008-07-10T18:56:43Z","isPatch":true,"sender":{"key":"robin.rosenberg@dewire.com","avatar":"https://avatars.githubusercontent.com/u/46357?v=4"},"body":"torsdagen den 10 juli 2008 08.13.20 skrev Shawn O. Pearce:\n> When we show the URI we just fetched or pushed against there may\n> be a user password embedded in that URI, as saved in the user's\n> .git/config file.  We shouldn't display that in public to prying\n> eyes so nulling it out will give us a copy of the URI without that\n> field in it.\n> \n> Signed-off-by: Shawn O. Pearce <spearce@spearce.org>\n> ---\n>  .../src/org/spearce/jgit/pgm/Fetch.java            |    2 +-\n>  .../src/org/spearce/jgit/pgm/Push.java             |    2 +-\n>  2 files changed, 2 insertions(+), 2 deletions(-)\n> \n> diff --git a/org.spearce.jgit/src/org/spearce/jgit/pgm/Fetch.java b/org.spearce.jgit/src/org/spearce/jgit/pgm/Fetch.java\n> index c9c997e..36a0592 100644\n> --- a/org.spearce.jgit/src/org/spearce/jgit/pgm/Fetch.java\n> +++ b/org.spearce.jgit/src/org/spearce/jgit/pgm/Fetch.java\n> @@ -71,7 +71,7 @@ class Fetch extends TextBuiltin {\n>  \t\t\treturn;\n>  \n>  \t\tout.print(\"From \");\n> -\t\tout.print(tn.getURI());\n> +\t\tout.print(tn.getURI().setPass(null));\n\nWe did this a while ago. Sort of patching broken stuff instead of fixing what's broken, thus we should make URIIsh.toiString\nnot display the password.\n\n-- robin\n"},{"id":"82845","messageId":"200807102217.38459.robin.rosenberg.lists@dewire.com","threadId":"14389","inReplyTo":"200807102056.43912.robin.rosenberg@dewire.com","subject":"Re: [JGIT PATCH 2/5] Don't display passwords on the console in fetch/push output","fromName":"Robin Rosenberg","fromEmail":"robin.rosenberg.lists@dewire.com","sentAt":"2008-07-10T20:17:38Z","receivedAt":"2008-07-10T20:17:38Z","isPatch":true,"sender":{"key":"robin.rosenberg@dewire.com","avatar":"https://avatars.githubusercontent.com/u/46357?v=4"},"body":">From 99c09cf2321f36eb81043aed2fa6834811ee762b Mon Sep 17 00:00:00 2001\nFrom: Robin Rosenberg <robin.rosenberg@dewire.com>\nDate: Thu, 10 Jul 2008 22:16:19 +0200\nSubject: [PATCH] Avoid password leak from URIIsh\n\nThe toString() method is commonly used for dumping information. We\nnever ever want to use toString when the password is needed. By masking\nout the password we avoid unintentional password leaks.\n\nSigned-off-by: Robin Rosenberg <robin.rosenberg@dewire.com>\n---\n .../src/org/spearce/jgit/transport/URIish.java     |    2 +-\n 1 files changed, 1 insertions(+), 1 deletions(-)\n\ndiff --git a/org.spearce.jgit/src/org/spearce/jgit/transport/URIish.java b/org.spearce.jgit/src/org/spearce/jgit/transport/URIish.java\nindex e022e57..632c8ad 100644\n--- a/org.spearce.jgit/src/org/spearce/jgit/transport/URIish.java\n+++ b/org.spearce.jgit/src/org/spearce/jgit/transport/URIish.java\n@@ -318,7 +318,7 @@ public class URIish {\n \t\t\tr.append(getUser());\n \t\t\tif (getPass() != null) {\n \t\t\t\tr.append(':');\n-\t\t\t\tr.append(getPass());\n+\t\t\t\tr.append(\"PASSWORD\");\n \t\t\t}\n \t\t}\n \n-- \n1.5.6.2.220.g44701\n"},{"id":"82856","messageId":"alpine.DEB.1.00.0807110024480.3279@eeepc-johanness","threadId":"14389","inReplyTo":"200807102217.38459.robin.rosenberg.lists@dewire.com","subject":"Re: [JGIT PATCH 2/5] Don't display passwords on the console in fetch/push output","fromName":"Johannes Schindelin","fromEmail":"johannes.schindelin@gmx.de","sentAt":"2008-07-10T22:25:27Z","receivedAt":"2008-07-10T22:25:27Z","isPatch":true,"sender":{"key":"johannes.schindelin@gmx.de","avatar":"https://avatars.githubusercontent.com/u/127790?v=4"},"body":"Hi,\n\nOn Thu, 10 Jul 2008, Robin Rosenberg wrote:\n\n> >From 99c09cf2321f36eb81043aed2fa6834811ee762b Mon Sep 17 00:00:00 2001\n> From: Robin Rosenberg <robin.rosenberg@dewire.com>\n> Date: Thu, 10 Jul 2008 22:16:19 +0200\n> Subject: [PATCH] Avoid password leak from URIIsh\n\nWhat is this new fashion of sending them headers in the mail body?  Robin, \nI thought you knew better.\n\nCiao,\nDscho\n"},{"id":"82884","messageId":"200807110042.42752.robin.rosenberg.lists@dewire.com","threadId":"14389","inReplyTo":"alpine.DEB.1.00.0807110024480.3279@eeepc-johanness","subject":"Re: [JGIT PATCH 2/5] Don't display passwords on the console in fetch/push output","fromName":"Robin Rosenberg","fromEmail":"robin.rosenberg.lists@dewire.com","sentAt":"2008-07-10T22:42:42Z","receivedAt":"2008-07-10T22:42:42Z","isPatch":true,"sender":{"key":"robin.rosenberg@dewire.com","avatar":"https://avatars.githubusercontent.com/u/46357?v=4"},"body":"fredagen den 11 juli 2008 00.25.27 skrev Johannes Schindelin:\n> Hi,\n> \n> On Thu, 10 Jul 2008, Robin Rosenberg wrote:\n> \n> > >From 99c09cf2321f36eb81043aed2fa6834811ee762b Mon Sep 17 00:00:00 2001\n> > From: Robin Rosenberg <robin.rosenberg@dewire.com>\n> > Date: Thu, 10 Jul 2008 22:16:19 +0200\n> > Subject: [PATCH] Avoid password leak from URIIsh\n> \n> What is this new fashion of sending them headers in the mail body?  Robin, \n> I thought you knew better.\n\nLaziness, I suppose, and that's not a new fashion. I'll try to have better in the future.\n\n-- robin\n"},{"id":"82913","messageId":"20080711032004.GC32319@spearce.org","threadId":"14389","inReplyTo":"200807102217.38459.robin.rosenberg.lists@dewire.com","subject":"Re: [JGIT PATCH 2/5] Don't display passwords on the console in fetch/push output","fromName":"Shawn O. Pearce","fromEmail":"spearce@spearce.org","sentAt":"2008-07-11T03:20:04Z","receivedAt":"2008-07-11T03:20:04Z","isPatch":true,"sender":{"key":"spearce@spearce.org","avatar":"https://avatars.githubusercontent.com/u/34844?v=4"},"body":"Robin Rosenberg <robin.rosenberg.lists@dewire.com> wrote:\n> diff --git a/org.spearce.jgit/src/org/spearce/jgit/transport/URIish.java b/org.spearce.jgit/src/org/spearce/jgit/transport/URIish.java\n> index e022e57..632c8ad 100644\n> --- a/org.spearce.jgit/src/org/spearce/jgit/transport/URIish.java\n> +++ b/org.spearce.jgit/src/org/spearce/jgit/transport/URIish.java\n> @@ -318,7 +318,7 @@ public class URIish {\n>  \t\t\tr.append(getUser());\n>  \t\t\tif (getPass() != null) {\n>  \t\t\t\tr.append(':');\n> -\t\t\t\tr.append(getPass());\n> +\t\t\t\tr.append(\"PASSWORD\");\n>  \t\t\t}\n>  \t\t}\n>  \n\nI agree in theory, but the implementation isn't quite correct.\nHow about this instead?\n\n--8<--\nAvoid password leak from URIIsh\n\nThe toString() method is commonly used for dumping information. We\nnever ever want to use toString when the password is needed. By\nmasking out the password we avoid unintentional password leaks.\n\nSigned-off-by: Shawn O. Pearce <spearce@spearce.org>\n---\n .../tst/org/spearce/jgit/transport/URIishTest.java |    3 ++-\n .../org/spearce/jgit/transport/RemoteConfig.java   |    2 +-\n .../src/org/spearce/jgit/transport/URIish.java     |   15 ++++++++++++++-\n 3 files changed, 17 insertions(+), 3 deletions(-)\n\ndiff --git a/org.spearce.jgit.test/tst/org/spearce/jgit/transport/URIishTest.java b/org.spearce.jgit.test/tst/org/spearce/jgit/transport/URIishTest.java\nindex a460418..2e5e847 100644\n--- a/org.spearce.jgit.test/tst/org/spearce/jgit/transport/URIishTest.java\n+++ b/org.spearce.jgit.test/tst/org/spearce/jgit/transport/URIishTest.java\n@@ -214,7 +214,8 @@ public class URIishTest extends TestCase {\n \t\tassertEquals(\"user\", u.getUser());\n \t\tassertEquals(\"pass\", u.getPass());\n \t\tassertEquals(33, u.getPort());\n-\t\tassertEquals(str, u.toString());\n+\t\tassertEquals(str, u.toPrivateString());\n+\t\tassertEquals(u.setPass(null).toPrivateString(), u.toString());\n \t\tassertEquals(u, new URIish(str));\n \t}\n }\ndiff --git a/org.spearce.jgit/src/org/spearce/jgit/transport/RemoteConfig.java b/org.spearce.jgit/src/org/spearce/jgit/transport/RemoteConfig.java\nindex b0fd5b4..bb21511 100644\n--- a/org.spearce.jgit/src/org/spearce/jgit/transport/RemoteConfig.java\n+++ b/org.spearce.jgit/src/org/spearce/jgit/transport/RemoteConfig.java\n@@ -150,7 +150,7 @@ public class RemoteConfig {\n \n \t\tvlst.clear();\n \t\tfor (final URIish u : getURIs())\n-\t\t\tvlst.add(u.toString());\n+\t\t\tvlst.add(u.toPrivateString());\n \t\trc.setStringList(SECTION, getName(), KEY_URL, vlst);\n \n \t\tvlst.clear();\ndiff --git a/org.spearce.jgit/src/org/spearce/jgit/transport/URIish.java b/org.spearce.jgit/src/org/spearce/jgit/transport/URIish.java\nindex e022e57..54a0811 100644\n--- a/org.spearce.jgit/src/org/spearce/jgit/transport/URIish.java\n+++ b/org.spearce.jgit/src/org/spearce/jgit/transport/URIish.java\n@@ -307,7 +307,20 @@ public class URIish {\n \t\treturn a.equals(b);\n \t}\n \n+\t/**\n+\t * Obtain the string form of the URI, with the password included.\n+\t * \n+\t * @return the URI, including its password field, if any.\n+\t */\n+\tpublic String toPrivateString() {\n+\t\treturn format(true);\n+\t}\n+\n \tpublic String toString() {\n+\t\treturn format(false);\n+\t}\n+\n+\tprivate String format(final boolean includePassword) {\n \t\tfinal StringBuilder r = new StringBuilder();\n \t\tif (getScheme() != null) {\n \t\t\tr.append(getScheme());\n@@ -316,7 +329,7 @@ public class URIish {\n \n \t\tif (getUser() != null) {\n \t\t\tr.append(getUser());\n-\t\t\tif (getPass() != null) {\n+\t\t\tif (includePassword && getPass() != null) {\n \t\t\t\tr.append(':');\n \t\t\t\tr.append(getPass());\n \t\t\t}\n-- \n1.5.6.2.393.g45096\n\n-- \nShawn.\n"},{"id":"82935","messageId":"200807111030.31983.robin.rosenberg.lists@dewire.com","threadId":"14389","inReplyTo":"20080711032004.GC32319@spearce.org","subject":"Re: [JGIT PATCH 2/5] Don't display passwords on the console in fetch/push output","fromName":"Robin Rosenberg","fromEmail":"robin.rosenberg.lists@dewire.com","sentAt":"2008-07-11T08:30:31Z","receivedAt":"2008-07-11T08:30:31Z","isPatch":true,"sender":{"key":"robin.rosenberg@dewire.com","avatar":"https://avatars.githubusercontent.com/u/46357?v=4"},"body":"fredagen den 11 juli 2008 05.20.04 skrev Shawn O. Pearce:\n> +\tpublic String toPrivateString() {\n> +\t\treturn format(true);\n> +\t}\netc\n\nYes, that's much better. \n\n-- robin\n"}]}