{"thread":{"id":"16855","subject":"[JGIT PATCH 0/5] Add \"jgit upload-pack\" for fetch service","startedAt":"2008-12-23T22:56:42Z","lastAt":"2008-12-23T22:56:47Z","messageCount":6,"participants":["Shawn O. Pearce"],"isPatch":true,"patchVersion":1,"patchTotal":5},"messages":[{"id":"98638","messageId":"1230073007-17337-1-git-send-email-spearce@spearce.org","threadId":"16855","inReplyTo":null,"subject":"[JGIT PATCH 0/5] Add \"jgit upload-pack\" for fetch service","fromName":"Shawn O. Pearce","fromEmail":"spearce@spearce.org","sentAt":"2008-12-23T22:56:42Z","receivedAt":"2008-12-23T22:56:42Z","isPatch":true,"sender":{"key":"spearce@spearce.org","avatar":"https://avatars.githubusercontent.com/u/34844?v=4"},"body":"And uh, now we can act as a true git server, providing fetch and\nclone support over git:// with our own daemon, or through the use of\n\"git fetch --upload-pack='jgit upload-pack'\".\n\nRoughly tested by cloning the Linux kernel and WebKit (both about\n500 MB packed).  Kernel takes a while to enumerate the objects,\nbut eh, it works.  :-)\n\n\nShawn O. Pearce (5):\n  Sort Ref objects by OrigName and not Name\n  Permit subclass of ObjectId (e.g. RevObject) when calling PackWriter\n  Implement \"jgit upload-pack\" to support fetching from jgit\n  Fix BaseFetchPackConnection's output of selected capabilities\n  Switch local fetch connection to use our own UploadPack\n\n .../services/org.spearce.jgit.pgm.TextBuiltin      |    1 +\n .../src/org/spearce/jgit/pgm/UploadPack.java       |   67 +++\n .../src/org/spearce/jgit/lib/PackWriter.java       |   94 +++-\n .../src/org/spearce/jgit/lib/RefComparator.java    |    2 +-\n .../spearce/jgit/transport/BasePackConnection.java |    3 +-\n .../jgit/transport/BasePackFetchConnection.java    |    4 +\n .../jgit/transport/BasePackPushConnection.java     |    2 +-\n .../src/org/spearce/jgit/transport/Daemon.java     |   36 ++-\n .../org/spearce/jgit/transport/PacketLineOut.java  |   19 +-\n .../jgit/transport/SideBandInputStream.java        |    6 +-\n .../jgit/transport/SideBandOutputStream.java       |   93 ++++\n .../jgit/transport/SideBandProgressMonitor.java    |  150 ++++++\n .../org/spearce/jgit/transport/TransportLocal.java |   96 ++++-\n .../src/org/spearce/jgit/transport/UploadPack.java |  491 ++++++++++++++++++++\n 14 files changed, 1018 insertions(+), 46 deletions(-)\n create mode 100644 org.spearce.jgit.pgm/src/org/spearce/jgit/pgm/UploadPack.java\n create mode 100644 org.spearce.jgit/src/org/spearce/jgit/transport/SideBandOutputStream.java\n create mode 100644 org.spearce.jgit/src/org/spearce/jgit/transport/SideBandProgressMonitor.java\n create mode 100644 org.spearce.jgit/src/org/spearce/jgit/transport/UploadPack.java\n"},{"id":"98639","messageId":"1230073007-17337-2-git-send-email-spearce@spearce.org","threadId":"16855","inReplyTo":"1230073007-17337-1-git-send-email-spearce@spearce.org","subject":"[JGIT PATCH 1/5] Sort Ref objects by OrigName and not Name","fromName":"Shawn O. Pearce","fromEmail":"spearce@spearce.org","sentAt":"2008-12-23T22:56:43Z","receivedAt":"2008-12-23T22:56:43Z","isPatch":true,"sender":{"key":"spearce@spearce.org","avatar":"https://avatars.githubusercontent.com/u/34844?v=4"},"body":"This avoids sorting symrefs by their target; instead we sort the\nsymref by the symref's own name, thus placing \"HEAD\" before the\nstandard \"refs/heads/...\" namespace.\n\nSigned-off-by: Shawn O. Pearce <spearce@spearce.org>\n---\n .../src/org/spearce/jgit/lib/RefComparator.java    |    2 +-\n 1 files changed, 1 insertions(+), 1 deletions(-)\n\ndiff --git a/org.spearce.jgit/src/org/spearce/jgit/lib/RefComparator.java b/org.spearce.jgit/src/org/spearce/jgit/lib/RefComparator.java\nindex 95e3e0f..940a7ec 100644\n--- a/org.spearce.jgit/src/org/spearce/jgit/lib/RefComparator.java\n+++ b/org.spearce.jgit/src/org/spearce/jgit/lib/RefComparator.java\n@@ -54,7 +54,7 @@\n \tpublic static final RefComparator INSTANCE = new RefComparator();\n \n \tpublic int compare(final Ref o1, final Ref o2) {\n-\t\treturn o1.getName().compareTo(o2.getName());\n+\t\treturn o1.getOrigName().compareTo(o2.getOrigName());\n \t}\n \n \t/**\n-- \n1.6.1.rc4.301.g5497a\n"},{"id":"98641","messageId":"1230073007-17337-3-git-send-email-spearce@spearce.org","threadId":"16855","inReplyTo":"1230073007-17337-2-git-send-email-spearce@spearce.org","subject":"[JGIT PATCH 2/5] Permit subclass of ObjectId (e.g. RevObject) when calling PackWriter","fromName":"Shawn O. Pearce","fromEmail":"spearce@spearce.org","sentAt":"2008-12-23T22:56:44Z","receivedAt":"2008-12-23T22:56:44Z","isPatch":true,"sender":{"key":"spearce@spearce.org","avatar":"https://avatars.githubusercontent.com/u/34844?v=4"},"body":"Signed-off-by: Shawn O. Pearce <spearce@spearce.org>\n---\n .../src/org/spearce/jgit/lib/PackWriter.java       |    9 +++++----\n 1 files changed, 5 insertions(+), 4 deletions(-)\n\ndiff --git a/org.spearce.jgit/src/org/spearce/jgit/lib/PackWriter.java b/org.spearce.jgit/src/org/spearce/jgit/lib/PackWriter.java\nindex 32bf738..32394f2 100644\n--- a/org.spearce.jgit/src/org/spearce/jgit/lib/PackWriter.java\n+++ b/org.spearce.jgit/src/org/spearce/jgit/lib/PackWriter.java\n@@ -436,8 +436,9 @@ public void preparePack(final Iterator<RevObject> objectsSource)\n \t * @throws IOException\n \t *             when some I/O problem occur during reading objects.\n \t */\n-\tpublic void preparePack(final Collection<ObjectId> interestingObjects,\n-\t\t\tfinal Collection<ObjectId> uninterestingObjects,\n+\tpublic void preparePack(\n+\t\t\tfinal Collection<? extends ObjectId> interestingObjects,\n+\t\t\tfinal Collection<? extends ObjectId> uninterestingObjects,\n \t\t\tfinal boolean thin, final boolean ignoreMissingUninteresting)\n \t\t\tthrows IOException {\n \t\tObjectWalk walker = setUpWalker(interestingObjects,\n@@ -727,8 +728,8 @@ private void writeChecksum() throws IOException {\n \t}\n \n \tprivate ObjectWalk setUpWalker(\n-\t\t\tfinal Collection<ObjectId> interestingObjects,\n-\t\t\tfinal Collection<ObjectId> uninterestingObjects,\n+\t\t\tfinal Collection<? extends ObjectId> interestingObjects,\n+\t\t\tfinal Collection<? extends ObjectId> uninterestingObjects,\n \t\t\tfinal boolean thin, final boolean ignoreMissingUninteresting)\n \t\t\tthrows MissingObjectException, IOException,\n \t\t\tIncorrectObjectTypeException {\n-- \n1.6.1.rc4.301.g5497a\n"},{"id":"98640","messageId":"1230073007-17337-4-git-send-email-spearce@spearce.org","threadId":"16855","inReplyTo":"1230073007-17337-3-git-send-email-spearce@spearce.org","subject":"[JGIT PATCH 3/5] Implement \"jgit upload-pack\" to support fetching from jgit","fromName":"Shawn O. Pearce","fromEmail":"spearce@spearce.org","sentAt":"2008-12-23T22:56:45Z","receivedAt":"2008-12-23T22:56:45Z","isPatch":true,"sender":{"key":"spearce@spearce.org","avatar":"https://avatars.githubusercontent.com/u/34844?v=4"},"body":"All current options, with the exception of shallow fetch, are\nsupported by this implementation.\n\n\"jgit upload-pack\" is included to support remote server side\nexecution.  \"jgit daemon\" has also had the upload-pack service\nadded to its service table, making fetch over git:// possible.\n\nSigned-off-by: Shawn O. Pearce <spearce@spearce.org>\n---\n .../services/org.spearce.jgit.pgm.TextBuiltin      |    1 +\n .../src/org/spearce/jgit/pgm/UploadPack.java       |   67 +++\n .../src/org/spearce/jgit/lib/PackWriter.java       |   85 +++-\n .../jgit/transport/BasePackFetchConnection.java    |    2 +\n .../src/org/spearce/jgit/transport/Daemon.java     |   36 ++-\n .../org/spearce/jgit/transport/PacketLineOut.java  |   19 +-\n .../jgit/transport/SideBandInputStream.java        |    6 +-\n .../jgit/transport/SideBandOutputStream.java       |   93 ++++\n .../jgit/transport/SideBandProgressMonitor.java    |  150 ++++++\n .../src/org/spearce/jgit/transport/UploadPack.java |  491 ++++++++++++++++++++\n 10 files changed, 915 insertions(+), 35 deletions(-)\n create mode 100644 org.spearce.jgit.pgm/src/org/spearce/jgit/pgm/UploadPack.java\n create mode 100644 org.spearce.jgit/src/org/spearce/jgit/transport/SideBandOutputStream.java\n create mode 100644 org.spearce.jgit/src/org/spearce/jgit/transport/SideBandProgressMonitor.java\n create mode 100644 org.spearce.jgit/src/org/spearce/jgit/transport/UploadPack.java\n\ndiff --git a/org.spearce.jgit.pgm/src/META-INF/services/org.spearce.jgit.pgm.TextBuiltin b/org.spearce.jgit.pgm/src/META-INF/services/org.spearce.jgit.pgm.TextBuiltin\nindex 40177f9..1ba29e6 100644\n--- a/org.spearce.jgit.pgm/src/META-INF/services/org.spearce.jgit.pgm.TextBuiltin\n+++ b/org.spearce.jgit.pgm/src/META-INF/services/org.spearce.jgit.pgm.TextBuiltin\n@@ -16,6 +16,7 @@ org.spearce.jgit.pgm.RevList\n org.spearce.jgit.pgm.Rm\n org.spearce.jgit.pgm.ShowRev\n org.spearce.jgit.pgm.Tag\n+org.spearce.jgit.pgm.UploadPack\n org.spearce.jgit.pgm.Version\n \n org.spearce.jgit.pgm.debug.MakeCacheTree\ndiff --git a/org.spearce.jgit.pgm/src/org/spearce/jgit/pgm/UploadPack.java b/org.spearce.jgit.pgm/src/org/spearce/jgit/pgm/UploadPack.java\nnew file mode 100644\nindex 0000000..d6f6d7c\n--- /dev/null\n+++ b/org.spearce.jgit.pgm/src/org/spearce/jgit/pgm/UploadPack.java\n@@ -0,0 +1,67 @@\n+/*\n+ * Copyright (C) 2008, Google Inc.\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.pgm;\n+\n+import java.io.File;\n+\n+import org.kohsuke.args4j.Argument;\n+import org.spearce.jgit.lib.Repository;\n+\n+@Command(common = false, usage = \"Server side backend for 'jgit fetch'\")\n+class UploadPack extends TextBuiltin {\n+\t@Argument(index = 0, required = true, metaVar = \"DIRECTORY\", usage = \"Repository to read from\")\n+\tFile gitdir;\n+\n+\t@Override\n+\tprotected final boolean requiresRepository() {\n+\t\treturn false;\n+\t}\n+\n+\t@Override\n+\tprotected void run() throws Exception {\n+\t\tfinal org.spearce.jgit.transport.UploadPack rp;\n+\n+\t\tif (new File(gitdir, \".git\").isDirectory())\n+\t\t\tgitdir = new File(gitdir, \".git\");\n+\t\tdb = new Repository(gitdir);\n+\t\tif (!db.getObjectsDirectory().isDirectory())\n+\t\t\tthrow die(\"'\" + gitdir.getPath() + \"' not a git repository\");\n+\t\trp = new org.spearce.jgit.transport.UploadPack(db);\n+\t\trp.upload(System.in, System.out, System.err);\n+\t}\n+}\ndiff --git a/org.spearce.jgit/src/org/spearce/jgit/lib/PackWriter.java b/org.spearce.jgit/src/org/spearce/jgit/lib/PackWriter.java\nindex 32394f2..89460f2 100644\n--- a/org.spearce.jgit/src/org/spearce/jgit/lib/PackWriter.java\n+++ b/org.spearce.jgit/src/org/spearce/jgit/lib/PackWriter.java\n@@ -173,7 +173,9 @@\n \n \tprivate final Deflater deflater;\n \n-\tprivate final ProgressMonitor monitor;\n+\tprivate ProgressMonitor initMonitor;\n+\n+\tprivate ProgressMonitor writeMonitor;\n \n \tprivate final byte[] buf = new byte[16384]; // 16 KB\n \n@@ -200,18 +202,41 @@\n \t * <p>\n \t * Objects for packing are specified in {@link #preparePack(Iterator)} or\n \t * {@link #preparePack(Collection, Collection, boolean, boolean)}.\n-\t *\n+\t * \n \t * @param repo\n \t *            repository where objects are stored.\n \t * @param monitor\n \t *            operations progress monitor, used within\n \t *            {@link #preparePack(Iterator)},\n-\t *            {@link #preparePack(Collection, Collection, boolean, boolean)},\n-\t *            or {@link #writePack(OutputStream)}.\n+\t *            {@link #preparePack(Collection, Collection, boolean, boolean)}\n+\t *            , or {@link #writePack(OutputStream)}.\n \t */\n \tpublic PackWriter(final Repository repo, final ProgressMonitor monitor) {\n+\t\tthis(repo, monitor, monitor);\n+\t}\n+\n+\t/**\n+\t * Create writer for specified repository.\n+\t * <p>\n+\t * Objects for packing are specified in {@link #preparePack(Iterator)} or\n+\t * {@link #preparePack(Collection, Collection, boolean, boolean)}.\n+\t * \n+\t * @param repo\n+\t *            repository where objects are stored.\n+\t * @param imonitor\n+\t *            operations progress monitor, used within\n+\t *            {@link #preparePack(Iterator)},\n+\t *            {@link #preparePack(Collection, Collection, boolean, boolean)}\n+\t *            ;\n+\t * @param wmonitor\n+\t *            operations progress monitor, used within\n+\t *            {@link #writePack(OutputStream)}.\n+\t */\n+\tpublic PackWriter(final Repository repo, final ProgressMonitor imonitor,\n+\t\t\tfinal ProgressMonitor wmonitor) {\n \t\tthis.db = repo;\n-\t\tthis.monitor = monitor;\n+\t\tinitMonitor = imonitor;\n+\t\twriteMonitor = wmonitor;\n \t\tthis.deflater = new Deflater(db.getConfig().getCore().getCompression());\n \t}\n \n@@ -447,6 +472,17 @@ public void preparePack(\n \t}\n \n \t/**\n+\t * Determine if the pack file will contain the requested object.\n+\t * \n+\t * @param id\n+\t *            the object to test the existence of.\n+\t * @return true if the object will appear in the output pack file.\n+\t */\n+\tpublic boolean willInclude(final AnyObjectId id) {\n+\t\treturn objectsMap.get(id) != null;\n+\t}\n+\n+\t/**\n \t * Computes SHA-1 of lexicographically sorted objects ids written in this\n \t * pack, as used to name a pack file in repository.\n \t *\n@@ -529,23 +565,23 @@ public void writePack(OutputStream packStream) throws IOException {\n \t\tcountingOut = new CountingOutputStream(packStream);\n \t\tout = new DigestOutputStream(countingOut, Constants.newMessageDigest());\n \n-\t\tmonitor.beginTask(WRITING_OBJECTS_PROGRESS, getObjectsNumber());\n+\t\twriteMonitor.beginTask(WRITING_OBJECTS_PROGRESS, getObjectsNumber());\n \t\twriteHeader();\n \t\twriteObjects();\n \t\twriteChecksum();\n \n \t\tout.flush();\n \t\twindowCursor.release();\n-\t\tmonitor.endTask();\n+\t\twriteMonitor.endTask();\n \t}\n \n \tprivate void searchForReuse() throws IOException {\n-\t\tmonitor.beginTask(SEARCHING_REUSE_PROGRESS, getObjectsNumber());\n+\t\tinitMonitor.beginTask(SEARCHING_REUSE_PROGRESS, getObjectsNumber());\n \t\tfinal Collection<PackedObjectLoader> reuseLoaders = new LinkedList<PackedObjectLoader>();\n \n \t\tfor (List<ObjectToPack> list : objectsLists) {\n \t\t\tfor (ObjectToPack otp : list) {\n-\t\t\t\tif (monitor.isCancelled())\n+\t\t\t\tif (initMonitor.isCancelled())\n \t\t\t\t\tthrow new IOException(\n \t\t\t\t\t\t\t\"Packing cancelled during objects writing\");\n \t\t\t\treuseLoaders.clear();\n@@ -557,11 +593,11 @@ private void searchForReuse() throws IOException {\n \t\t\t\tif (reuseObjects && !otp.hasReuseLoader()) {\n \t\t\t\t\tselectObjectReuseForObject(otp, reuseLoaders);\n \t\t\t\t}\n-\t\t\t\tmonitor.update(1);\n+\t\t\t\tinitMonitor.update(1);\n \t\t\t}\n \t\t}\n \n-\t\tmonitor.endTask();\n+\t\tinitMonitor.endTask();\n \t}\n \n \tprivate void selectDeltaReuseForObject(final ObjectToPack otp,\n@@ -625,7 +661,7 @@ private void writeHeader() throws IOException {\n \tprivate void writeObjects() throws IOException {\n \t\tfor (List<ObjectToPack> list : objectsLists) {\n \t\t\tfor (ObjectToPack otp : list) {\n-\t\t\t\tif (monitor.isCancelled())\n+\t\t\t\tif (writeMonitor.isCancelled())\n \t\t\t\t\tthrow new IOException(\n \t\t\t\t\t\t\t\"Packing cancelled during objects writing\");\n \t\t\t\tif (!otp.isWritten())\n@@ -663,7 +699,7 @@ private void writeObject(final ObjectToPack otp) throws IOException {\n \t\telse\n \t\t\twriteWholeObject(otp);\n \n-\t\tmonitor.update(1);\n+\t\twriteMonitor.update(1);\n \t}\n \n \tprivate void writeWholeObject(final ObjectToPack otp) throws IOException {\n@@ -760,21 +796,34 @@ private ObjectWalk setUpWalker(\n \tprivate void findObjectsToPack(final ObjectWalk walker)\n \t\t\tthrows MissingObjectException, IncorrectObjectTypeException,\n \t\t\tIOException {\n-\t\tmonitor.beginTask(COUNTING_OBJECTS_PROGRESS, ProgressMonitor.UNKNOWN);\n+\t\tinitMonitor.beginTask(COUNTING_OBJECTS_PROGRESS,\n+\t\t\t\tProgressMonitor.UNKNOWN);\n \t\tRevObject o;\n \n \t\twhile ((o = walker.next()) != null) {\n \t\t\taddObject(o);\n-\t\t\tmonitor.update(1);\n+\t\t\tinitMonitor.update(1);\n \t\t}\n \t\twhile ((o = walker.nextObject()) != null) {\n \t\t\taddObject(o);\n-\t\t\tmonitor.update(1);\n+\t\t\tinitMonitor.update(1);\n \t\t}\n-\t\tmonitor.endTask();\n+\t\tinitMonitor.endTask();\n \t}\n \n-\tprivate void addObject(RevObject object)\n+\t/**\n+\t * Include one object to the output file.\n+\t * <p>\n+\t * Objects are written in the order they are added. If the same object is\n+\t * added twice, it may be written twice, creating a larger than necessary\n+\t * file.\n+\t * \n+\t * @param object\n+\t *            the object to add.\n+\t * @throws IncorrectObjectTypeException\n+\t *             the object is an unsupported type.\n+\t */\n+\tpublic void addObject(final RevObject object)\n \t\t\tthrows IncorrectObjectTypeException {\n \t\tif (object.has(RevFlag.UNINTERESTING)) {\n \t\t\tedgeObjects.add(object);\ndiff --git a/org.spearce.jgit/src/org/spearce/jgit/transport/BasePackFetchConnection.java b/org.spearce.jgit/src/org/spearce/jgit/transport/BasePackFetchConnection.java\nindex 2cb9b64..1fe504b 100644\n--- a/org.spearce.jgit/src/org/spearce/jgit/transport/BasePackFetchConnection.java\n+++ b/org.spearce.jgit/src/org/spearce/jgit/transport/BasePackFetchConnection.java\n@@ -100,6 +100,8 @@\n \n \tstatic final String OPTION_SHALLOW = \"shallow\";\n \n+\tstatic final String OPTION_NO_PROGRESS = \"no-progress\";\n+\n \tprivate final RevWalk walk;\n \n \t/** All commits that are immediately reachable by a local ref. */\ndiff --git a/org.spearce.jgit/src/org/spearce/jgit/transport/Daemon.java b/org.spearce.jgit/src/org/spearce/jgit/transport/Daemon.java\nindex c225740..b5097ef 100644\n--- a/org.spearce.jgit/src/org/spearce/jgit/transport/Daemon.java\n+++ b/org.spearce.jgit/src/org/spearce/jgit/transport/Daemon.java\n@@ -41,6 +41,7 @@\n import java.io.BufferedOutputStream;\n import java.io.File;\n import java.io.IOException;\n+import java.io.InputStream;\n import java.io.InterruptedIOException;\n import java.net.InetSocketAddress;\n import java.net.ServerSocket;\n@@ -96,15 +97,32 @@ public Daemon(final InetSocketAddress addr) {\n \t\texportBase = new ArrayList<File>();\n \t\tprocessors = new ThreadGroup(\"Git-Daemon\");\n \n-\t\tservices = new DaemonService[] { new DaemonService(\"receive-pack\",\n-\t\t\t\t\"receivepack\") {\n-\t\t\t@Override\n-\t\t\tprotected void execute(final DaemonClient dc, final Repository db)\n-\t\t\t\t\tthrows IOException {\n-\t\t\t\tfinal ReceivePack rp = new ReceivePack(db);\n-\t\t\t\trp.receive(dc.getInputStream(), dc.getOutputStream(), null);\n-\t\t\t}\n-\t\t} };\n+\t\tservices = new DaemonService[] {\n+\t\t\t\tnew DaemonService(\"upload-pack\", \"uploadpack\") {\n+\t\t\t\t\t{\n+\t\t\t\t\t\tsetEnabled(true);\n+\t\t\t\t\t}\n+\n+\t\t\t\t\t@Override\n+\t\t\t\t\tprotected void execute(final DaemonClient dc,\n+\t\t\t\t\t\t\tfinal Repository db) throws IOException {\n+\t\t\t\t\t\tfinal UploadPack rp = new UploadPack(db);\n+\t\t\t\t\t\tfinal InputStream in = dc.getInputStream();\n+\t\t\t\t\t\trp.upload(in, dc.getOutputStream(), null);\n+\t\t\t\t\t}\n+\t\t\t\t}, new DaemonService(\"receive-pack\", \"receivepack\") {\n+\t\t\t\t\t{\n+\t\t\t\t\t\tsetEnabled(false);\n+\t\t\t\t\t}\n+\n+\t\t\t\t\t@Override\n+\t\t\t\t\tprotected void execute(final DaemonClient dc,\n+\t\t\t\t\t\t\tfinal Repository db) throws IOException {\n+\t\t\t\t\t\tfinal ReceivePack rp = new ReceivePack(db);\n+\t\t\t\t\t\tfinal InputStream in = dc.getInputStream();\n+\t\t\t\t\t\trp.receive(in, dc.getOutputStream(), null);\n+\t\t\t\t\t}\n+\t\t\t\t} };\n \t}\n \n \t/** @return the address connections are received on. */\ndiff --git a/org.spearce.jgit/src/org/spearce/jgit/transport/PacketLineOut.java b/org.spearce.jgit/src/org/spearce/jgit/transport/PacketLineOut.java\nindex d37b217..aae4be5 100644\n--- a/org.spearce.jgit/src/org/spearce/jgit/transport/PacketLineOut.java\n+++ b/org.spearce.jgit/src/org/spearce/jgit/transport/PacketLineOut.java\n@@ -50,7 +50,7 @@\n \n \tPacketLineOut(final OutputStream i) {\n \t\tout = i;\n-\t\tlenbuffer = new byte[4];\n+\t\tlenbuffer = new byte[5];\n \t}\n \n \tvoid writeString(final String s) throws IOException {\n@@ -58,12 +58,22 @@ void writeString(final String s) throws IOException {\n \t}\n \n \tvoid writePacket(final byte[] packet) throws IOException {\n-\t\twriteLength(packet.length + 4);\n+\t\tformatLength(packet.length + 4);\n+\t\tout.write(lenbuffer, 0, 4);\n \t\tout.write(packet);\n \t}\n \n+\tvoid writeChannelPacket(final int channel, final byte[] buf, int off,\n+\t\t\tint len) throws IOException {\n+\t\tformatLength(len + 5);\n+\t\tlenbuffer[4] = (byte) channel;\n+\t\tout.write(lenbuffer, 0, 5);\n+\t\tout.write(buf, off, len);\n+\t}\n+\n \tvoid end() throws IOException {\n-\t\twriteLength(0);\n+\t\tformatLength(0);\n+\t\tout.write(lenbuffer, 0, 4);\n \t\tflush();\n \t}\n \n@@ -74,7 +84,7 @@ void flush() throws IOException {\n \tprivate static final byte[] hexchar = { '0', '1', '2', '3', '4', '5', '6',\n \t\t\t'7', '8', '9', 'a', 'b', 'c', 'd', 'e', 'f' };\n \n-\tprivate void writeLength(int w) throws IOException {\n+\tprivate void formatLength(int w) {\n \t\tint o = 3;\n \t\twhile (o >= 0 && w != 0) {\n \t\t\tlenbuffer[o--] = hexchar[w & 0xf];\n@@ -82,6 +92,5 @@ private void writeLength(int w) throws IOException {\n \t\t}\n \t\twhile (o >= 0)\n \t\t\tlenbuffer[o--] = '0';\n-\t\tout.write(lenbuffer, 0, 4);\n \t}\n }\ndiff --git a/org.spearce.jgit/src/org/spearce/jgit/transport/SideBandInputStream.java b/org.spearce.jgit/src/org/spearce/jgit/transport/SideBandInputStream.java\nindex 3ec9bff..b08cf4d 100644\n--- a/org.spearce.jgit/src/org/spearce/jgit/transport/SideBandInputStream.java\n+++ b/org.spearce.jgit/src/org/spearce/jgit/transport/SideBandInputStream.java\n@@ -66,11 +66,11 @@\n  * @see PacketLineIn#sideband(ProgressMonitor)\n  */\n class SideBandInputStream extends InputStream {\n-\tprivate static final int CH_DATA = 1;\n+\tstatic final int CH_DATA = 1;\n \n-\tprivate static final int CH_PROGRESS = 2;\n+\tstatic final int CH_PROGRESS = 2;\n \n-\tprivate static final int CH_ERROR = 3;\n+\tstatic final int CH_ERROR = 3;\n \n \tprivate static Pattern P_UNBOUNDED = Pattern.compile(\n \t\t\t\".*?([\\\\w ]+): (\\\\d+)(, done)?.*\", Pattern.DOTALL);\ndiff --git a/org.spearce.jgit/src/org/spearce/jgit/transport/SideBandOutputStream.java b/org.spearce.jgit/src/org/spearce/jgit/transport/SideBandOutputStream.java\nnew file mode 100644\nindex 0000000..31c9f5d\n--- /dev/null\n+++ b/org.spearce.jgit/src/org/spearce/jgit/transport/SideBandOutputStream.java\n@@ -0,0 +1,93 @@\n+/*\n+ * Copyright (C) 2008, Google Inc.\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.transport;\n+\n+import java.io.IOException;\n+import java.io.OutputStream;\n+\n+/**\n+ * Multiplexes data and progress messages\n+ * <p>\n+ * To correctly use this class you must wrap it in a BufferedOutputStream with a\n+ * buffer size no larger than either {@link #SMALL_BUF} or {@link #MAX_BUF},\n+ * minus {@link #HDR_SIZE}.\n+ */\n+class SideBandOutputStream extends OutputStream {\n+\tstatic final int CH_DATA = SideBandInputStream.CH_DATA;\n+\n+\tstatic final int CH_PROGRESS = SideBandInputStream.CH_PROGRESS;\n+\n+\tstatic final int CH_ERROR = SideBandInputStream.CH_ERROR;\n+\n+\tstatic final int SMALL_BUF = 1000;\n+\n+\tstatic final int MAX_BUF = 65520;\n+\n+\tstatic final int HDR_SIZE = 5;\n+\n+\tprivate final int channel;\n+\n+\tprivate final PacketLineOut pckOut;\n+\n+\tprivate byte[] singleByteBuffer;\n+\n+\tSideBandOutputStream(final int chan, final PacketLineOut out) {\n+\t\tchannel = chan;\n+\t\tpckOut = out;\n+\t}\n+\n+\t@Override\n+\tpublic void flush() throws IOException {\n+\t\tif (channel != CH_DATA)\n+\t\t\tpckOut.flush();\n+\t}\n+\n+\t@Override\n+\tpublic void write(final byte[] b, final int off, final int len)\n+\t\t\tthrows IOException {\n+\t\tpckOut.writeChannelPacket(channel, b, off, len);\n+\t}\n+\n+\t@Override\n+\tpublic void write(final int b) throws IOException {\n+\t\tif (singleByteBuffer == null)\n+\t\t\tsingleByteBuffer = new byte[1];\n+\t\tsingleByteBuffer[0] = (byte) b;\n+\t\twrite(singleByteBuffer);\n+\t}\n+}\ndiff --git a/org.spearce.jgit/src/org/spearce/jgit/transport/SideBandProgressMonitor.java b/org.spearce.jgit/src/org/spearce/jgit/transport/SideBandProgressMonitor.java\nnew file mode 100644\nindex 0000000..5cda7c5\n--- /dev/null\n+++ b/org.spearce.jgit/src/org/spearce/jgit/transport/SideBandProgressMonitor.java\n@@ -0,0 +1,150 @@\n+/*\n+ * Copyright (C) 2008, Google Inc.\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.transport;\n+\n+import java.io.BufferedOutputStream;\n+import java.io.OutputStreamWriter;\n+import java.io.PrintWriter;\n+\n+import org.spearce.jgit.lib.Constants;\n+import org.spearce.jgit.lib.ProgressMonitor;\n+\n+/** Write progress messages out to the sideband channel. */\n+class SideBandProgressMonitor implements ProgressMonitor {\n+\tprivate PrintWriter out;\n+\n+\tprivate boolean output;\n+\n+\tprivate long taskBeganAt;\n+\n+\tprivate long lastOutput;\n+\n+\tprivate String msg;\n+\n+\tprivate int lastWorked;\n+\n+\tprivate int totalWork;\n+\n+\tSideBandProgressMonitor(final PacketLineOut pckOut) {\n+\t\tfinal int bufsz = SideBandOutputStream.SMALL_BUF\n+\t\t\t\t- SideBandOutputStream.HDR_SIZE;\n+\t\tout = new PrintWriter(new OutputStreamWriter(new BufferedOutputStream(\n+\t\t\t\tnew SideBandOutputStream(SideBandOutputStream.CH_PROGRESS,\n+\t\t\t\t\t\tpckOut), bufsz), Constants.CHARSET));\n+\t}\n+\n+\tpublic void start(final int totalTasks) {\n+\t\t// Ignore the number of tasks.\n+\t\ttaskBeganAt = System.currentTimeMillis();\n+\t\tlastOutput = taskBeganAt;\n+\t}\n+\n+\tpublic void beginTask(final String title, final int total) {\n+\t\tendTask();\n+\t\tmsg = title;\n+\t\tlastWorked = 0;\n+\t\ttotalWork = total;\n+\t}\n+\n+\tpublic void update(final int completed) {\n+\t\tif (msg == null)\n+\t\t\treturn;\n+\n+\t\tfinal int cmp = lastWorked + completed;\n+\t\tfinal long now = System.currentTimeMillis();\n+\t\tif (!output && now - taskBeganAt < 500)\n+\t\t\treturn;\n+\t\tif (totalWork == UNKNOWN) {\n+\t\t\tif (now - lastOutput >= 500) {\n+\t\t\t\tdisplay(cmp, null);\n+\t\t\t\tlastOutput = now;\n+\t\t\t}\n+\t\t} else {\n+\t\t\tif ((cmp * 100 / totalWork) != (lastWorked * 100) / totalWork\n+\t\t\t\t\t|| now - lastOutput >= 500) {\n+\t\t\t\tdisplay(cmp, null);\n+\t\t\t\tlastOutput = now;\n+\t\t\t}\n+\t\t}\n+\t\tlastWorked = cmp;\n+\t\toutput = true;\n+\t}\n+\n+\tprivate void display(final int cmp, final String eol) {\n+\t\tfinal StringBuilder m = new StringBuilder();\n+\t\tm.append(msg);\n+\t\tm.append(\": \");\n+\n+\t\tif (totalWork == UNKNOWN) {\n+\t\t\tm.append(cmp);\n+\t\t} else {\n+\t\t\tfinal int pcnt = (cmp * 100 / totalWork);\n+\t\t\tif (pcnt < 100)\n+\t\t\t\tm.append(' ');\n+\t\t\tif (pcnt < 10)\n+\t\t\t\tm.append(' ');\n+\t\t\tm.append(pcnt);\n+\t\t\tm.append(\"% (\");\n+\t\t\tm.append(cmp);\n+\t\t\tm.append(\"/\");\n+\t\t\tm.append(totalWork);\n+\t\t\tm.append(\")\");\n+\t\t}\n+\t\tif (eol != null)\n+\t\t\tm.append(eol);\n+\t\telse\n+\t\t\tm.append(\"   \\r\");\n+\t\tout.print(m);\n+\t\tout.flush();\n+\t}\n+\n+\tpublic boolean isCancelled() {\n+\t\treturn false;\n+\t}\n+\n+\tpublic void endTask() {\n+\t\tif (output) {\n+\t\t\tif (totalWork == UNKNOWN)\n+\t\t\t\tdisplay(lastWorked, \", done\\n\");\n+\t\t\telse\n+\t\t\t\tdisplay(totalWork, \"\\n\");\n+\t\t}\n+\t\toutput = false;\n+\t\tmsg = null;\n+\t}\n+}\ndiff --git a/org.spearce.jgit/src/org/spearce/jgit/transport/UploadPack.java b/org.spearce.jgit/src/org/spearce/jgit/transport/UploadPack.java\nnew file mode 100644\nindex 0000000..4401951\n--- /dev/null\n+++ b/org.spearce.jgit/src/org/spearce/jgit/transport/UploadPack.java\n@@ -0,0 +1,491 @@\n+/*\n+ * Copyright (C) 2008, Google Inc.\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.transport;\n+\n+import java.io.BufferedOutputStream;\n+import java.io.EOFException;\n+import java.io.IOException;\n+import java.io.InputStream;\n+import java.io.OutputStream;\n+import java.util.ArrayList;\n+import java.util.HashSet;\n+import java.util.Iterator;\n+import java.util.List;\n+import java.util.Map;\n+import java.util.Set;\n+\n+import org.spearce.jgit.errors.PackProtocolException;\n+import org.spearce.jgit.lib.Constants;\n+import org.spearce.jgit.lib.NullProgressMonitor;\n+import org.spearce.jgit.lib.ObjectId;\n+import org.spearce.jgit.lib.PackWriter;\n+import org.spearce.jgit.lib.ProgressMonitor;\n+import org.spearce.jgit.lib.Ref;\n+import org.spearce.jgit.lib.RefComparator;\n+import org.spearce.jgit.lib.Repository;\n+import org.spearce.jgit.revwalk.RevCommit;\n+import org.spearce.jgit.revwalk.RevFlag;\n+import org.spearce.jgit.revwalk.RevFlagSet;\n+import org.spearce.jgit.revwalk.RevObject;\n+import org.spearce.jgit.revwalk.RevTag;\n+import org.spearce.jgit.revwalk.RevWalk;\n+\n+/**\n+ * Implements the server side of a fetch connection, transmitting objects.\n+ */\n+public class UploadPack {\n+\tstatic final String OPTION_INCLUDE_TAG = BasePackFetchConnection.OPTION_INCLUDE_TAG;\n+\n+\tstatic final String OPTION_MULTI_ACK = BasePackFetchConnection.OPTION_MULTI_ACK;\n+\n+\tstatic final String OPTION_THIN_PACK = BasePackFetchConnection.OPTION_THIN_PACK;\n+\n+\tstatic final String OPTION_SIDE_BAND = BasePackFetchConnection.OPTION_SIDE_BAND;\n+\n+\tstatic final String OPTION_SIDE_BAND_64K = BasePackFetchConnection.OPTION_SIDE_BAND_64K;\n+\n+\tstatic final String OPTION_OFS_DELTA = BasePackFetchConnection.OPTION_OFS_DELTA;\n+\n+\tstatic final String OPTION_NO_PROGRESS = BasePackFetchConnection.OPTION_NO_PROGRESS;\n+\n+\t/** Database we read the objects from. */\n+\tprivate final Repository db;\n+\n+\t/** Revision traversal support over {@link #db}. */\n+\tprivate final RevWalk walk;\n+\n+\tprivate InputStream rawIn;\n+\n+\tprivate OutputStream rawOut;\n+\n+\tprivate PacketLineIn pckIn;\n+\n+\tprivate PacketLineOut pckOut;\n+\n+\t/** The refs we advertised as existing at the start of the connection. */\n+\tprivate Map<String, Ref> refs;\n+\n+\t/** Capabilities requested by the client. */\n+\tprivate final Set<String> options = new HashSet<String>();\n+\n+\t/** Objects the client wants to obtain. */\n+\tprivate final List<RevObject> wantAll = new ArrayList<RevObject>();\n+\n+\t/** Objects the client wants to obtain. */\n+\tprivate final List<RevCommit> wantCommits = new ArrayList<RevCommit>();\n+\n+\t/** Objects on both sides, these don't have to be sent. */\n+\tprivate final List<RevObject> commonBase = new ArrayList<RevObject>();\n+\n+\t/** Marked on objects we sent in our advertisement list. */\n+\tprivate final RevFlag ADVERTISED;\n+\n+\t/** Marked on objects the client has asked us to give them. */\n+\tprivate final RevFlag WANT;\n+\n+\t/** Marked on objects both we and the client have. */\n+\tprivate final RevFlag PEER_HAS;\n+\n+\t/** Marked on objects in {@link #commonBase}. */\n+\tprivate final RevFlag COMMON;\n+\n+\tprivate final RevFlagSet SAVE;\n+\n+\tprivate boolean multiAck;\n+\n+\t/**\n+\t * Create a new pack upload for an open repository.\n+\t * \n+\t * @param copyFrom\n+\t *            the source repository.\n+\t */\n+\tpublic UploadPack(final Repository copyFrom) {\n+\t\tdb = copyFrom;\n+\t\twalk = new RevWalk(db);\n+\n+\t\tADVERTISED = walk.newFlag(\"ADVERTISED\");\n+\t\tWANT = walk.newFlag(\"WANT\");\n+\t\tPEER_HAS = walk.newFlag(\"PEER_HAS\");\n+\t\tCOMMON = walk.newFlag(\"COMMON\");\n+\t\twalk.carry(PEER_HAS);\n+\n+\t\tSAVE = new RevFlagSet();\n+\t\tSAVE.add(ADVERTISED);\n+\t\tSAVE.add(WANT);\n+\t\tSAVE.add(PEER_HAS);\n+\t}\n+\n+\t/** @return the repository this receive completes into. */\n+\tpublic final Repository getRepository() {\n+\t\treturn db;\n+\t}\n+\n+\t/** @return the RevWalk instance used by this connection. */\n+\tpublic final RevWalk getRevWalk() {\n+\t\treturn walk;\n+\t}\n+\n+\t/**\n+\t * Execute the upload task on the socket.\n+\t * \n+\t * @param input\n+\t *            raw input to read client commands from. Caller must ensure the\n+\t *            input is buffered, otherwise read performance may suffer.\n+\t * @param output\n+\t *            response back to the Git network client, to write the pack\n+\t *            data onto. Caller must ensure the output is buffered,\n+\t *            otherwise write performance may suffer.\n+\t * @param messages\n+\t *            secondary \"notice\" channel to send additional messages out\n+\t *            through. When run over SSH this should be tied back to the\n+\t *            standard error channel of the command execution. For most\n+\t *            other network connections this should be null.\n+\t * @throws IOException\n+\t */\n+\tpublic void upload(final InputStream input, final OutputStream output,\n+\t\t\tfinal OutputStream messages) throws IOException {\n+\t\trawIn = input;\n+\t\trawOut = output;\n+\n+\t\tpckIn = new PacketLineIn(rawIn);\n+\t\tpckOut = new PacketLineOut(rawOut);\n+\t\tservice();\n+\t}\n+\n+\tprivate void service() throws IOException {\n+\t\tsendAdvertisedRefs();\n+\t\trecvWants();\n+\t\tif (wantAll.isEmpty())\n+\t\t\treturn;\n+\t\tmultiAck = options.contains(OPTION_MULTI_ACK);\n+\t\tnegotiate();\n+\t\tsendPack();\n+\t}\n+\n+\tprivate void sendAdvertisedRefs() throws IOException {\n+\t\trefs = db.getAllRefs();\n+\n+\t\tfinal StringBuilder m = new StringBuilder(100);\n+\t\tfinal char[] idtmp = new char[2 * Constants.OBJECT_ID_LENGTH];\n+\t\tfinal Iterator<Ref> i = RefComparator.sort(refs.values()).iterator();\n+\t\tif (i.hasNext()) {\n+\t\t\tfinal Ref r = i.next();\n+\t\t\tfinal RevObject o = safeParseAny(r.getObjectId());\n+\t\t\tif (o != null) {\n+\t\t\t\tadvertise(m, idtmp, o, r.getOrigName());\n+\t\t\t\tm.append('\\0');\n+\t\t\t\tm.append(' ');\n+\t\t\t\tm.append(OPTION_INCLUDE_TAG);\n+\t\t\t\tm.append(' ');\n+\t\t\t\tm.append(OPTION_MULTI_ACK);\n+\t\t\t\tm.append(' ');\n+\t\t\t\tm.append(OPTION_OFS_DELTA);\n+\t\t\t\tm.append(' ');\n+\t\t\t\tm.append(OPTION_SIDE_BAND);\n+\t\t\t\tm.append(' ');\n+\t\t\t\tm.append(OPTION_SIDE_BAND_64K);\n+\t\t\t\tm.append(' ');\n+\t\t\t\tm.append(OPTION_THIN_PACK);\n+\t\t\t\tm.append(' ');\n+\t\t\t\tm.append(OPTION_NO_PROGRESS);\n+\t\t\t\tm.append(' ');\n+\t\t\t\twriteAdvertisedRef(m);\n+\t\t\t\tif (o instanceof RevTag)\n+\t\t\t\t\twriteAdvertisedTag(m, idtmp, o, r.getName());\n+\t\t\t}\n+\t\t}\n+\t\twhile (i.hasNext()) {\n+\t\t\tfinal Ref r = i.next();\n+\t\t\tfinal RevObject o = safeParseAny(r.getObjectId());\n+\t\t\tif (o != null) {\n+\t\t\t\tadvertise(m, idtmp, o, r.getOrigName());\n+\t\t\t\twriteAdvertisedRef(m);\n+\t\t\t\tif (o instanceof RevTag)\n+\t\t\t\t\twriteAdvertisedTag(m, idtmp, o, r.getName());\n+\t\t\t}\n+\t\t}\n+\t\tpckOut.end();\n+\t}\n+\n+\tprivate RevObject safeParseAny(final ObjectId id) {\n+\t\ttry {\n+\t\t\treturn walk.parseAny(id);\n+\t\t} catch (IOException e) {\n+\t\t\treturn null;\n+\t\t}\n+\t}\n+\n+\tprivate void advertise(final StringBuilder m, final char[] idtmp,\n+\t\t\tfinal RevObject o, final String name) {\n+\t\to.add(ADVERTISED);\n+\t\tm.setLength(0);\n+\t\to.getId().copyTo(idtmp, m);\n+\t\tm.append(' ');\n+\t\tm.append(name);\n+\t}\n+\n+\tprivate void writeAdvertisedRef(final StringBuilder m) throws IOException {\n+\t\tm.append('\\n');\n+\t\tpckOut.writeString(m.toString());\n+\t}\n+\n+\tprivate void writeAdvertisedTag(final StringBuilder m, final char[] idtmp,\n+\t\t\tfinal RevObject tag, final String name) throws IOException {\n+\t\tRevObject o = tag;\n+\t\twhile (o instanceof RevTag) {\n+\t\t\t// Fully unwrap here so later on we have these already parsed.\n+\t\t\ttry {\n+\t\t\t\twalk.parse(((RevTag) o).getObject());\n+\t\t\t} catch (IOException err) {\n+\t\t\t\treturn;\n+\t\t\t}\n+\t\t\to = ((RevTag) o).getObject();\n+\t\t\to.add(ADVERTISED);\n+\t\t}\n+\t\tadvertise(m, idtmp, ((RevTag) tag).getObject(), name + \"^{}\");\n+\t\twriteAdvertisedRef(m);\n+\t}\n+\n+\tprivate void recvWants() throws IOException {\n+\t\tboolean isFirst = true;\n+\t\tfor (;; isFirst = false) {\n+\t\t\tString line;\n+\t\t\ttry {\n+\t\t\t\tline = pckIn.readString();\n+\t\t\t} catch (EOFException eof) {\n+\t\t\t\tif (isFirst)\n+\t\t\t\t\tbreak;\n+\t\t\t\tthrow eof;\n+\t\t\t}\n+\n+\t\t\tif (line.length() == 0)\n+\t\t\t\tbreak;\n+\t\t\tif (!line.startsWith(\"want \") || line.length() < 45)\n+\t\t\t\tthrow new PackProtocolException(\"expected want; got \" + line);\n+\n+\t\t\tif (isFirst) {\n+\t\t\t\tfinal int sp = line.indexOf(' ', 45);\n+\t\t\t\tif (sp >= 0) {\n+\t\t\t\t\tfor (String c : line.substring(sp + 1).split(\" \"))\n+\t\t\t\t\t\toptions.add(c);\n+\t\t\t\t\tline = line.substring(0, sp);\n+\t\t\t\t}\n+\t\t\t}\n+\n+\t\t\tfinal ObjectId id = ObjectId.fromString(line.substring(5));\n+\t\t\tfinal RevObject o;\n+\t\t\ttry {\n+\t\t\t\to = walk.parseAny(id);\n+\t\t\t} catch (IOException e) {\n+\t\t\t\tthrow new PackProtocolException(id.name() + \" not valid\", e);\n+\t\t\t}\n+\t\t\tif (!o.has(ADVERTISED))\n+\t\t\t\tthrow new PackProtocolException(id.name() + \" not valid\");\n+\t\t\twant(o);\n+\t\t}\n+\t}\n+\n+\tprivate void want(RevObject o) {\n+\t\tif (!o.has(WANT)) {\n+\t\t\to.add(WANT);\n+\t\t\twantAll.add(o);\n+\n+\t\t\tif (o instanceof RevCommit)\n+\t\t\t\twantCommits.add((RevCommit) o);\n+\n+\t\t\telse if (o instanceof RevTag) {\n+\t\t\t\tdo {\n+\t\t\t\t\to = ((RevTag) o).getObject();\n+\t\t\t\t} while (o instanceof RevTag);\n+\t\t\t\tif (o instanceof RevCommit)\n+\t\t\t\t\twant(o);\n+\t\t\t}\n+\t\t}\n+\t}\n+\n+\tprivate void negotiate() throws IOException {\n+\t\tObjectId last = ObjectId.zeroId();\n+\t\tfor (;;) {\n+\t\t\tString line;\n+\t\t\ttry {\n+\t\t\t\tline = pckIn.readString();\n+\t\t\t} catch (EOFException eof) {\n+\t\t\t\tthrow eof;\n+\t\t\t}\n+\n+\t\t\tif (line.length() == 0) {\n+\t\t\t\tif (commonBase.isEmpty() || multiAck)\n+\t\t\t\t\tpckOut.writeString(\"NAK\\n\");\n+\n+\t\t\t} else if (line.startsWith(\"have \") && line.length() == 45) {\n+\t\t\t\tfinal ObjectId id = ObjectId.fromString(line.substring(5));\n+\t\t\t\tif (matchHave(id)) {\n+\t\t\t\t\t// Both sides have the same object; let the client know.\n+\t\t\t\t\t//\n+\t\t\t\t\tif (multiAck) {\n+\t\t\t\t\t\tlast = id;\n+\t\t\t\t\t\tpckOut.writeString(\"ACK \" + id.name() + \" continue\\n\");\n+\t\t\t\t\t} else if (commonBase.size() == 1)\n+\t\t\t\t\t\tpckOut.writeString(\"ACK \" + id.name() + \"\\n\");\n+\t\t\t\t} else {\n+\t\t\t\t\t// They have this object; we don't.\n+\t\t\t\t\t//\n+\t\t\t\t\tif (multiAck && okToGiveUp())\n+\t\t\t\t\t\tpckOut.writeString(\"ACK \" + id.name() + \" continue\\n\");\n+\t\t\t\t}\n+\n+\t\t\t} else if (line.equals(\"done\")) {\n+\t\t\t\tif (commonBase.isEmpty())\n+\t\t\t\t\tpckOut.writeString(\"NAK\\n\");\n+\n+\t\t\t\telse if (multiAck)\n+\t\t\t\t\tpckOut.writeString(\"ACK \" + last.name() + \"\\n\");\n+\t\t\t\tbreak;\n+\n+\t\t\t} else {\n+\t\t\t\tthrow new PackProtocolException(\"expected have; got \" + line);\n+\t\t\t}\n+\t\t}\n+\t}\n+\n+\tprivate boolean matchHave(final ObjectId id) {\n+\t\tfinal RevObject o;\n+\t\ttry {\n+\t\t\to = walk.parseAny(id);\n+\t\t} catch (IOException err) {\n+\t\t\treturn false;\n+\t\t}\n+\n+\t\tif (!o.has(PEER_HAS)) {\n+\t\t\to.add(PEER_HAS);\n+\t\t\tif (o instanceof RevCommit)\n+\t\t\t\t((RevCommit) o).carry(PEER_HAS);\n+\t\t\tif (!o.has(COMMON)) {\n+\t\t\t\to.add(COMMON);\n+\t\t\t\tcommonBase.add(o);\n+\t\t\t}\n+\t\t}\n+\t\treturn true;\n+\t}\n+\n+\tprivate boolean okToGiveUp() throws PackProtocolException {\n+\t\tif (commonBase.isEmpty())\n+\t\t\treturn false;\n+\n+\t\ttry {\n+\t\t\tfor (final Iterator<RevCommit> i = wantCommits.iterator(); i\n+\t\t\t\t\t.hasNext();) {\n+\t\t\t\tfinal RevCommit want = i.next();\n+\t\t\t\tif (wantSatisfied(want))\n+\t\t\t\t\ti.remove();\n+\t\t\t}\n+\t\t} catch (IOException e) {\n+\t\t\tthrow new PackProtocolException(\"internal revision error\", e);\n+\t\t}\n+\t\treturn wantCommits.isEmpty();\n+\t}\n+\n+\tprivate boolean wantSatisfied(final RevCommit want) throws IOException {\n+\t\twalk.resetRetain(SAVE);\n+\t\twalk.markStart(want);\n+\t\tfor (;;) {\n+\t\t\tfinal RevCommit c = walk.next();\n+\t\t\tif (c == null)\n+\t\t\t\tbreak;\n+\t\t\tif (c.has(PEER_HAS)) {\n+\t\t\t\tif (!c.has(COMMON)) {\n+\t\t\t\t\tc.add(COMMON);\n+\t\t\t\t\tcommonBase.add(c);\n+\t\t\t\t}\n+\t\t\t\treturn true;\n+\t\t\t}\n+\t\t}\n+\t\treturn false;\n+\t}\n+\n+\tprivate void sendPack() throws IOException {\n+\t\tfinal boolean thin = options.contains(OPTION_THIN_PACK);\n+\t\tfinal boolean progress = !options.contains(OPTION_NO_PROGRESS);\n+\t\tfinal boolean sideband = options.contains(OPTION_SIDE_BAND)\n+\t\t\t\t|| options.contains(OPTION_SIDE_BAND_64K);\n+\n+\t\tProgressMonitor pm = NullProgressMonitor.INSTANCE;\n+\t\tOutputStream packOut = rawOut;\n+\n+\t\tif (sideband) {\n+\t\t\tint bufsz = SideBandOutputStream.SMALL_BUF;\n+\t\t\tif (options.contains(OPTION_SIDE_BAND_64K))\n+\t\t\t\tbufsz = SideBandOutputStream.MAX_BUF;\n+\t\t\tbufsz -= SideBandOutputStream.HDR_SIZE;\n+\n+\t\t\tpackOut = new BufferedOutputStream(new SideBandOutputStream(\n+\t\t\t\t\tSideBandOutputStream.CH_DATA, pckOut), bufsz);\n+\n+\t\t\tif (progress)\n+\t\t\t\tpm = new SideBandProgressMonitor(pckOut);\n+\t\t}\n+\n+\t\tfinal PackWriter pw;\n+\t\tpw = new PackWriter(db, pm, NullProgressMonitor.INSTANCE);\n+\t\tpw.setDeltaBaseAsOffset(options.contains(OPTION_OFS_DELTA));\n+\t\tpw.preparePack(wantAll, commonBase, thin, true);\n+\t\tif (options.contains(OPTION_INCLUDE_TAG)) {\n+\t\t\tfor (final Ref r : refs.values()) {\n+\t\t\t\tfinal RevObject o;\n+\t\t\t\ttry {\n+\t\t\t\t\to = walk.parseAny(r.getObjectId());\n+\t\t\t\t} catch (IOException e) {\n+\t\t\t\t\tcontinue;\n+\t\t\t\t}\n+\t\t\t\tif (o.has(WANT) || !(o instanceof RevTag))\n+\t\t\t\t\tcontinue;\n+\t\t\t\tfinal RevTag t = (RevTag) o;\n+\t\t\t\tif (!pw.willInclude(t) && pw.willInclude(t.getObject()))\n+\t\t\t\t\tpw.addObject(t);\n+\t\t\t}\n+\t\t}\n+\t\tpw.writePack(packOut);\n+\n+\t\tif (sideband) {\n+\t\t\tpackOut.flush();\n+\t\t\tpckOut.end();\n+\t\t} else {\n+\t\t\trawOut.flush();\n+\t\t}\n+\t}\n+}\n-- \n1.6.1.rc4.301.g5497a\n"},{"id":"98643","messageId":"1230073007-17337-5-git-send-email-spearce@spearce.org","threadId":"16855","inReplyTo":"1230073007-17337-4-git-send-email-spearce@spearce.org","subject":"[JGIT PATCH 4/5] Fix BaseFetchPackConnection's output of selected capabilities","fromName":"Shawn O. Pearce","fromEmail":"spearce@spearce.org","sentAt":"2008-12-23T22:56:46Z","receivedAt":"2008-12-23T22:56:46Z","isPatch":true,"sender":{"key":"spearce@spearce.org","avatar":"https://avatars.githubusercontent.com/u/34844?v=4"},"body":"When we output the first \"have ...\" line for an upload-pack server\nprocess we need to dump \"have $id $cap1 $cap2 ..\", where there is\na space between the SHA-1 $id and the first capability name $cap1.\nIf we don't dump that space we run into errors with our own version\nof upload-pack not being able to parse the SHA-1 out of the line,\nas the line was split incorrectly.\n\nSigned-off-by: Shawn O. Pearce <spearce@spearce.org>\n---\n .../spearce/jgit/transport/BasePackConnection.java |    3 +--\n .../jgit/transport/BasePackPushConnection.java     |    2 +-\n 2 files changed, 2 insertions(+), 3 deletions(-)\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 e9df30e..c9232ce 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@@ -207,8 +207,7 @@ protected boolean isCapableOf(final String option) {\n \tprotected boolean wantCapability(final StringBuilder b, final String option) {\n \t\tif (!isCapableOf(option))\n \t\t\treturn false;\n-\t\tif (b.length() > 0)\n-\t\t\tb.append(' ');\n+\t\tb.append(' ');\n \t\tb.append(option);\n \t\treturn true;\n \t}\ndiff --git a/org.spearce.jgit/src/org/spearce/jgit/transport/BasePackPushConnection.java b/org.spearce.jgit/src/org/spearce/jgit/transport/BasePackPushConnection.java\nindex 17f6915..a078d7e 100644\n--- a/org.spearce.jgit/src/org/spearce/jgit/transport/BasePackPushConnection.java\n+++ b/org.spearce.jgit/src/org/spearce/jgit/transport/BasePackPushConnection.java\n@@ -181,7 +181,7 @@ private String enableCapabilities() {\n \t\tcapableReport = wantCapability(line, CAPABILITY_REPORT_STATUS);\n \t\tcapableDeleteRefs = wantCapability(line, CAPABILITY_DELETE_REFS);\n \t\tif (line.length() > 0)\n-\t\t\tline.insert(0, '\\0');\n+\t\t\tline.setCharAt(0, '\\0');\n \t\treturn line.toString();\n \t}\n \n-- \n1.6.1.rc4.301.g5497a\n"},{"id":"98642","messageId":"1230073007-17337-6-git-send-email-spearce@spearce.org","threadId":"16855","inReplyTo":"1230073007-17337-5-git-send-email-spearce@spearce.org","subject":"[JGIT PATCH 5/5] Switch local fetch connection to use our own UploadPack","fromName":"Shawn O. Pearce","fromEmail":"spearce@spearce.org","sentAt":"2008-12-23T22:56:47Z","receivedAt":"2008-12-23T22:56:47Z","isPatch":true,"sender":{"key":"spearce@spearce.org","avatar":"https://avatars.githubusercontent.com/u/34844?v=4"},"body":"Just like with push, we now use our own version of upload pack to\nperform a fetch from a local repository.\n\nSigned-off-by: Shawn O. Pearce <spearce@spearce.org>\n---\n .../jgit/transport/BasePackFetchConnection.java    |    2 +\n .../org/spearce/jgit/transport/TransportLocal.java |   96 +++++++++++++++++++-\n 2 files changed, 95 insertions(+), 3 deletions(-)\n\ndiff --git a/org.spearce.jgit/src/org/spearce/jgit/transport/BasePackFetchConnection.java b/org.spearce.jgit/src/org/spearce/jgit/transport/BasePackFetchConnection.java\nindex 1fe504b..19ac161 100644\n--- a/org.spearce.jgit/src/org/spearce/jgit/transport/BasePackFetchConnection.java\n+++ b/org.spearce.jgit/src/org/spearce/jgit/transport/BasePackFetchConnection.java\n@@ -86,6 +86,8 @@\n \t */\n \tprivate static final int MAX_HAVES = 256;\n \n+\tprotected static final int MAX_CLIENT_BUFFER = MAX_HAVES * 46 + 1024;\n+\n \tstatic final String OPTION_INCLUDE_TAG = \"include-tag\";\n \n \tstatic final String OPTION_MULTI_ACK = \"multi_ack\";\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 b5dd5fc..17d95c2 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@@ -86,7 +86,10 @@ TransportLocal(final Repository local, final URIish uri) {\n \n \t@Override\n \tpublic FetchConnection openFetch() throws TransportException {\n-\t\treturn new LocalFetchConnection();\n+\t\tfinal String up = getOptionUploadPack();\n+\t\tif (\"git-upload-pack\".equals(up) || \"git upload-pack\".equals(up))\n+\t\t\treturn new InternalLocalFetchConnection();\n+\t\treturn new ForkLocalFetchConnection();\n \t}\n \n \t@Override\n@@ -130,10 +133,97 @@ protected Process startProcessWithErrStream(final String cmd)\n \t\t}\n \t}\n \n-\tclass LocalFetchConnection extends BasePackFetchConnection {\n+\tclass InternalLocalFetchConnection extends BasePackFetchConnection {\n+\t\tprivate Thread worker;\n+\n+\t\tInternalLocalFetchConnection() throws TransportException {\n+\t\t\tsuper(TransportLocal.this);\n+\n+\t\t\tfinal Repository dst;\n+\t\t\ttry {\n+\t\t\t\tdst = new Repository(remoteGitDir);\n+\t\t\t} catch (IOException err) {\n+\t\t\t\tthrow new TransportException(uri, \"not a git directory\");\n+\t\t\t}\n+\n+\t\t\tfinal PipedInputStream in_r;\n+\t\t\tfinal PipedOutputStream in_w;\n+\n+\t\t\tfinal PipedInputStream out_r;\n+\t\t\tfinal PipedOutputStream out_w;\n+\t\t\ttry {\n+\t\t\t\tin_r = new PipedInputStream();\n+\t\t\t\tin_w = new PipedOutputStream(in_r);\n+\n+\t\t\t\tout_r = new PipedInputStream() {\n+\t\t\t\t\t// The client (BasePackFetchConnection) can write\n+\t\t\t\t\t// a huge burst before it reads again. We need to\n+\t\t\t\t\t// force the buffer to be big enough, otherwise it\n+\t\t\t\t\t// will deadlock both threads.\n+\t\t\t\t\t{\n+\t\t\t\t\t\tbuffer = new byte[MAX_CLIENT_BUFFER];\n+\t\t\t\t\t}\n+\t\t\t\t};\n+\t\t\t\tout_w = new PipedOutputStream(out_r);\n+\t\t\t} catch (IOException err) {\n+\t\t\t\tdst.close();\n+\t\t\t\tthrow new TransportException(uri, \"cannot connect pipes\", err);\n+\t\t\t}\n+\n+\t\t\tworker = new Thread(\"JGit-Upload-Pack\") {\n+\t\t\t\tpublic void run() {\n+\t\t\t\t\ttry {\n+\t\t\t\t\t\tfinal UploadPack rp = new UploadPack(dst);\n+\t\t\t\t\t\trp.upload(out_r, in_w, null);\n+\t\t\t\t\t} catch (IOException err) {\n+\t\t\t\t\t\t// Client side of the pipes should report the problem.\n+\t\t\t\t\t\terr.printStackTrace();\n+\t\t\t\t\t} catch (RuntimeException err) {\n+\t\t\t\t\t\t// Clients side will notice we went away, and report.\n+\t\t\t\t\t\terr.printStackTrace();\n+\t\t\t\t\t} finally {\n+\t\t\t\t\t\ttry {\n+\t\t\t\t\t\t\tout_r.close();\n+\t\t\t\t\t\t} catch (IOException e2) {\n+\t\t\t\t\t\t\t// Ignore close failure, we probably crashed above.\n+\t\t\t\t\t\t}\n+\n+\t\t\t\t\t\ttry {\n+\t\t\t\t\t\t\tin_w.close();\n+\t\t\t\t\t\t} catch (IOException e2) {\n+\t\t\t\t\t\t\t// Ignore close failure, we probably crashed above.\n+\t\t\t\t\t\t}\n+\n+\t\t\t\t\t\tdst.close();\n+\t\t\t\t\t}\n+\t\t\t\t}\n+\t\t\t};\n+\t\t\tworker.start();\n+\n+\t\t\tinit(in_r, out_w);\n+\t\t\treadAdvertisedRefs();\n+\t\t}\n+\n+\t\t@Override\n+\t\tpublic void close() {\n+\t\t\tsuper.close();\n+\n+\t\t\tif (worker != null) {\n+\t\t\t\ttry {\n+\t\t\t\t\tworker.join();\n+\t\t\t\t} catch (InterruptedException ie) {\n+\t\t\t\t\t// Stop waiting and return anyway.\n+\t\t\t\t} finally {\n+\t\t\t\t\tworker = null;\n+\t\t\t\t}\n+\t\t\t}\n+\t\t}\n+\t}\n+\n+\tclass ForkLocalFetchConnection extends BasePackFetchConnection {\n \t\tprivate Process uploadPack;\n \n-\t\tLocalFetchConnection() throws TransportException {\n+\t\tForkLocalFetchConnection() throws TransportException {\n \t\t\tsuper(TransportLocal.this);\n \t\t\tuploadPack = startProcessWithErrStream(getOptionUploadPack());\n \t\t\tinit(uploadPack.getInputStream(), uploadPack.getOutputStream());\n-- \n1.6.1.rc4.301.g5497a\n"}]}