{"thread":{"id":"18224","subject":"[PoC PATCH JGIT 0/2] Proof of concept code for Git Freenet transport","startedAt":"2009-03-09T03:36:03Z","lastAt":"2009-03-09T14:59:55Z","messageCount":6,"participants":["Daniel Cheng (aka SDiZ)","Daniel Cheng","Johannes Schindelin","Shawn O. Pearce"],"isPatch":true,"patchVersion":1,"patchTotal":2},"messages":[{"id":"107434","messageId":"1236569765-8882-1-git-send-email-j16sdiz+freenet@gmail.com","threadId":"18224","inReplyTo":null,"subject":"[PoC PATCH JGIT 0/2] Proof of concept code for Git Freenet transport","fromName":"Daniel Cheng (aka SDiZ)","fromEmail":"j16sdiz+freenet-re5jqeeqqe8avxtiumwx3w@public.gmane.org","sentAt":"2009-03-09T03:36:03Z","receivedAt":"2009-03-09T03:36:03Z","isPatch":true,"sender":{"key":"j16sdiz+freenet-re5jqeeqqe8avxtiumwx3w@public.gmane.org","avatar":null},"body":"Hi JGit / Freenet community,\n\nHere is some proof-of-concept code for Git-over-Freenet.\nI am sending this to see the feedback from communities.\n\nThe code need some more cleanups, so it it is not ready for apply (yet).\n\nThis is a real-life example\n\nPush:\n   $ git remote add fcp fcp://SSK@[my public key]^[my private key]/test.git\n   $ ./jgit push fcp refs/remotes/origin/stable:refs/heads/master\n \n /ALTERNATIVLY/\n\n   Insert a bare repository under USK@<.....>/test.git/-1/\n\nPull:\n $ ./jgit clone fcp://SSK@[my public key]^[my private key]/test.git\n\n\nTo workaround the metadata update problem, this client translate the\npath seperator to \"-\", that means:\n\nOn push:\n   objects/aa/bbbbbbbb   --> USK@..../test.git-objects-aa-bbbbbbb/-1/\n   refs/heads/xxx        --> USK@..../test.git-objects-ref-heads-xxx/-1/\n\nOn pull:\n   To support uploading from jSite, \n    when we load the info/refs we first check USK@..../test.git-info-refs/-1/\n    if it is unavailiable, we would use USK@..../test.git/-1/info/refs\n\n   The \"traditional\" type (USK@..../test.git/-1/objects) repository is\n   always added as an alternative objects database. No other info/alternatives \n   are supported\n\nFIXME:\n - How to store the private key of repository?\n   Currently, we use URI of form fcp://SSK@<public key>^<private key>/some-id\n   This is quite ugly. Could we use a per remote Config ? How can I get remote\n   name from transport?\n\n - Make pushing async, could we?\n"},{"id":"107435","messageId":"1236569765-8882-2-git-send-email-j16sdiz+freenet@gmail.com","threadId":"18224","inReplyTo":"1236569765-8882-1-git-send-email-j16sdiz+freenet-Re5JQEeQqe8AvxtiuMwx3w@public.gmane.org","subject":"[PoC PATCH JGIT 1/2] Freenet Freenet Client Protocol (FCP) 2.0 Client","fromName":"Daniel Cheng (aka SDiZ)","fromEmail":"j16sdiz+freenet-re5jqeeqqe8avxtiumwx3w@public.gmane.org","sentAt":"2009-03-09T03:36:04Z","receivedAt":"2009-03-09T03:36:04Z","isPatch":true,"sender":{"key":"j16sdiz+freenet-re5jqeeqqe8avxtiumwx3w@public.gmane.org","avatar":null},"body":"\nSigned-off-by: Daniel Cheng (aka SDiZ) <j16sdiz+freenet-Re5JQEeQqe8AvxtiuMwx3w@public.gmane.org>\n---\n .../jgit/transport/fcpv2/FCPConnection.java        |  427 ++++++++++++++++++++\n 1 files changed, 427 insertions(+), 0 deletions(-)\n create mode 100644 org.spearce.jgit/src/org/spearce/jgit/transport/fcpv2/FCPConnection.java\n\ndiff --git a/org.spearce.jgit/src/org/spearce/jgit/transport/fcpv2/FCPConnection.java b/org.spearce.jgit/src/org/spearce/jgit/transport/fcpv2/FCPConnection.java\nnew file mode 100644\nindex 0000000..ce2ca90\n--- /dev/null\n+++ b/org.spearce.jgit/src/org/spearce/jgit/transport/fcpv2/FCPConnection.java\n@@ -0,0 +1,427 @@\n+/*\n+ * Copyright (C) 2008, CHENG Yuk-Pong, Daniel <j16sdiz+freenet-Re5JQEeQqe8AvxtiuMwx3w@public.gmane.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.transport.fcpv2;\n+\n+import java.io.BufferedInputStream;\n+import java.io.BufferedOutputStream;\n+import java.io.FileNotFoundException;\n+import java.io.IOException;\n+import java.io.InputStream;\n+import java.io.OutputStream;\n+import java.net.InetAddress;\n+import java.net.Socket;\n+import java.net.UnknownHostException;\n+import java.util.HashMap;\n+import java.util.Map;\n+\n+import org.spearce.jgit.lib.ProgressMonitor;\n+\n+/**\n+ * Freenet Client Protocol 2.0 Connection\n+ * \n+ * A simplified interface to Freenet 0.7 node using FCPv2. This is not a\n+ * full-blown interface to Freenet node, but rather some \"quick and dirty\" hack\n+ * just enough to upload/download simple file from the network.\n+ * <p>\n+ * See <a href=\"http://wiki.freenetproject.org/FreenetFCPSpec2Point0\">Freenet\n+ * Client Protocol 2.0 Specification</a> for detail on this protocol.\n+ */\n+public class FCPConnection {\n+\t/** Default FCP port */\n+\tpublic static final int DEFAULT_FCP_PORT = 9481;\n+\n+\tprotected InetAddress addr;\n+\n+\tprotected int port;\n+\n+\tprotected Socket socket;\n+\n+\tprotected InputStream is;\n+\n+\tprotected OutputStream os;\n+\n+\t/**\n+\t * Create a new FCP Connection to default host and port (\n+\t * <code>localhost:9481</code>)\n+\t * \n+\t * @throws UnknownHostException\n+\t *             if no IP address for the <code>localhost</code> could be\n+\t *             found.\n+\t */\n+\tpublic FCPConnection() throws UnknownHostException {\n+\t\tthis(InetAddress.getAllByName(\"127.0.0.1\")[0], DEFAULT_FCP_PORT);\n+\t}\n+\n+\t/**\n+\t * Create a new FCP Connection to the specified IP address and port\n+\t * \n+\t * @param address\n+\t *            the node IP address\n+\t * @param port\n+\t *            the port number\n+\t */\n+\tpublic FCPConnection(InetAddress address, int port) {\n+\t\tthis.addr = address;\n+\t\tthis.port = port;\n+\t}\n+\n+\t/**\n+\t * Connect to the node\n+\t * \n+\t * @throws IOException\n+\t *             if any I/O error occurred\n+\t */\n+\tpublic void connect() throws IOException {\n+\t\tsocket = new Socket(addr, port);\n+\n+\t\tis = new BufferedInputStream(socket.getInputStream());\n+\t\tos = new BufferedOutputStream(socket.getOutputStream());\n+\t}\n+\n+\t/**\n+\t * Hello Message\n+\t * \n+\t * Send handshake <code>ClientHello</code> message to node. Block until\n+\t * <code>NodeHello</code> is received.\n+\t * \n+\t * @param clientName\n+\t *            Client name, must be unique in the freenet node\n+\t * \n+\t * @throws IOException\n+\t *             if any I/O error occurred\n+\t */\n+\tpublic void hello(String clientName) throws IOException {\n+\t\tFCPMessage msg = new FCPMessage();\n+\t\tmsg.messageType = \"ClientHello\";\n+\t\tmsg.put(\"ExpectedVersion\", \"2.0\");\n+\t\tmsg.put(\"Name\", clientName);\n+\n+\t\tmsg.writeTo(os);\n+\n+\t\twhile (true) {\n+\t\t\tFCPMessage reply = FCPMessage.parse(is);\n+\t\t\tif (\"NodeHello\".equals(reply.messageType))\n+\t\t\t\treturn;\n+\t\t\tif (\"ProtocolError\".equals(reply.messageType))\n+\t\t\t\tthrow new IOException(\"Protocol error\");\n+\t\t}\n+\t}\n+\n+\t/**\n+\t * Simple FCP Put\n+\t * \n+\t * This method block until the data are fetchable or <code>PutFailed</code>\n+\t * received.\n+\t * \n+\t * @param freenetURI\n+\t *            the freenet URI\n+\t * @param data\n+\t *            the data to be put\n+\t * @param monitor\n+\t *            progress monitor to post write completion to during the\n+\t *            stream's close method.\n+\t * @param monitorTask\n+\t *            task name to display during the close method.\n+\t * @return <code>true</code> iff <code>PutFetchable</code>,\n+\t *         <code>PutSuccessful</code> or <code>PutFetchable</code> with\n+\t *         insert collision (data already inserted)\n+\t * @throws IOException\n+\t *             if any I/O error occurred\n+\t */\n+\tpublic boolean simplePut(String freenetURI, byte[] data,\n+\t\t\tProgressMonitor monitor, String monitorTask) throws IOException {\n+\t\tSystem.err.println(freenetURI);\n+\n+\t\tFCPMessage msg = new FCPMessage();\n+\t\tmsg.messageType = \"ClientPut\";\n+\t\tmsg.put(\"URI\", freenetURI);\n+\t\tmsg.put(\"Identifier\", freenetURI);\n+\t\tmsg.put(\"Verbosity\", \"1\");\n+\t\tmsg.put(\"Global\", \"false\");\n+\t\tmsg.put(\"UploadFrom\", \"direct\");\n+\t\tmsg.put(\"DataLength\", \"\" + data.length);\n+\t\tmsg.extraData = data;\n+\n+\t\tmsg.writeTo(os);\n+\n+\t\tint totalBlocks = -1;\n+\t\tint completedBlocks = 0;\n+\n+\t\ttry {\n+\t\t\twhile (true) {\n+\t\t\t\tFCPMessage reply = FCPMessage.parse(is);\n+\n+\t\t\t\tif (\"ProtocolError\".equals(reply.messageType))\n+\t\t\t\t\tthrow new IOException(\"Protocol error\");\n+\t\t\t\tif (!freenetURI.equals(reply.get(\"Identifier\"))) // identifier don't match, ignore it\n+\t\t\t\t\tcontinue;\n+\t\t\t\tif (\"IdentifierCollision\".equals(reply.messageType))\n+\t\t\t\t\tthrow new IOException(\"IdentifierCollision\");\n+\n+\t\t\t\tif (\"PutFailed\".equals(reply.messageType)) {\n+\t\t\t\t\tif (\"9\".equals(reply.get(\"Code\"))) // collision, data already inserted\n+\t\t\t\t\t\treturn true;\n+\t\t\t\t\treturn false;\n+\t\t\t\t}\n+\n+\t\t\t\tif (\"SimpleProgress\".equals(reply.messageType)) {\n+\t\t\t\t\tif (totalBlocks == -1) {\n+\t\t\t\t\t\ttotalBlocks = Integer.parseInt(reply.get(\"Total\"));\n+\t\t\t\t\t\tmonitor.beginTask(monitorTask, totalBlocks);\n+\t\t\t\t\t}\n+\t\t\t\t\tint tmp = Integer.parseInt(reply.get(\"Succeeded\"));\n+\t\t\t\t\tif (tmp < totalBlocks)\n+\t\t\t\t\t\tmonitor.update(tmp - completedBlocks);\n+\t\t\t\t\tcompletedBlocks = tmp;\n+\t\t\t\t}\n+\n+\t\t\t\tif (\"PutSuccessful\".equals(reply.messageType))\n+\t\t\t\t\treturn true;\n+\t\t\t\tif (\"PutFetchable\".equals(reply.messageType))\n+\t\t\t\t\treturn true;\n+\t\t\t}\n+\t\t} finally {\n+\t\t\tif (totalBlocks == -1)\n+\t\t\t\tmonitor.beginTask(monitorTask, 1);\n+\t\t\tmonitor.endTask();\n+\t\t}\n+\t}\n+\n+\t/**\n+\t * Simple FCP Get\n+\t * \n+\t * @param freenetURI\n+\t *            the freenet URI\n+\t * @return the data\n+\t * @throws IOException\n+\t *             if any I/O error occurred\n+\t * @throws FileNotFoundException\n+\t *             if data not found\n+\t */\n+\tpublic byte[] simpleGet(String freenetURI) throws IOException,\n+\t\t\tFileNotFoundException {\n+\t\tFCPMessage msg = new FCPMessage();\n+\t\tmsg.messageType = \"ClientGet\";\n+\t\tmsg.put(\"URI\", freenetURI);\n+\t\tmsg.put(\"Identifier\", freenetURI);\n+\t\tmsg.put(\"Verbosity\", \"1\");\n+\t\tmsg.put(\"MaxSize\", \"2147483647\");\n+\t\tmsg.put(\"Global\", \"false\");\n+\t\tmsg.put(\"ClientToken\", freenetURI);\n+\t\tmsg.put(\"ReturnType\", \"direct\");\n+\n+\t\tmsg.writeTo(os);\n+\n+\t\twhile (true) {\n+\t\t\tFCPMessage reply = FCPMessage.parse(is);\n+\t\t\tif (\"ProtocolError\".equals(reply.messageType))\n+\t\t\t\tthrow new IOException(\"Protocol error\");\n+\t\t\tif (!freenetURI.equals(reply.get(\"Identifier\"))) // identifier don't match, ignore it\n+\t\t\t\tcontinue;\n+\t\t\tif (\"IdentifierCollision\".equals(reply.messageType))\n+\t\t\t\tthrow new IOException(\"IdentifierCollision\");\n+\n+\t\t\tif (\"GetFailed\".equals(reply.messageType)) {\n+\t\t\t\tString redirectURI = reply.get(\"RedirectURI\");\n+\t\t\t\tif (redirectURI != null)\n+\t\t\t\t\treturn simpleGet(redirectURI);\n+\n+\t\t\t\tSystem.err.println(\" -> nf\");\n+\t\t\t\tthrow new FileNotFoundException();\n+\t\t\t}\n+\n+\t\t\tif (\"AllData\".equals(reply.messageType))\n+\t\t\t\treturn reply.extraData;\n+\t\t}\n+\t}\n+\n+\t/**\n+\t * Generate SSK Key Pair\n+\t * \n+\t * @return the generated SSK key pair. <code>key[0]</code> is the public\n+\t *         key, <code>key[1]</code> is the private key.\n+\t * @throws IOException\n+\t *             if any I/O error occurred\n+\t */\n+\tpublic String[] generateSSK() throws IOException {\n+\t\tFCPMessage msg = new FCPMessage();\n+\t\tmsg.messageType = \"GenerateSSK\";\n+\t\tmsg.put(\"Identifier\", \"GenerateSSK\");\n+\n+\t\tmsg.writeTo(os);\n+\n+\t\twhile (true) {\n+\t\t\tFCPMessage reply = FCPMessage.parse(is);\n+\t\t\tif (\"ProtocolError\".equals(reply.messageType))\n+\t\t\t\tthrow new IOException(\"Protocol error\");\n+\n+\t\t\tif (\"SSKKeypair\".equals(reply.messageType)) {\n+\t\t\t\tString[] keys = new String[2];\n+\t\t\t\tkeys[0] = reply.get(\"RequestURI\");\n+\t\t\t\tkeys[1] = reply.get(\"InsertURI\");\n+\t\t\t\treturn keys;\n+\t\t\t}\n+\t\t}\n+\t}\n+\n+\t/**\n+\t * Close the connection\n+\t * \n+\t * @throws IOException\n+\t *             if any I/O error occurred\n+\t */\n+\tpublic void close() throws IOException {\n+\t\tos.close();\n+\t\tis.close();\n+\t\tsocket.close();\n+\t}\n+\n+\tprivate static class FCPMessage {\n+\t\tprivate String messageType;\n+\n+\t\tprivate Map<String, String> parameter = new HashMap<String, String>();\n+\n+\t\tbyte[] extraData;\n+\n+\t\tFCPMessage() {\n+\t\t\t// default constructor\n+\t\t}\n+\n+\t\tprivate static FCPMessage parse(InputStream in) throws IOException {\n+\t\t\tFCPMessage ret = new FCPMessage();\n+\t\t\tString line = readLine(in);\n+\t\t\tret.messageType = line;\n+\n+\t\t\twhile (true) {\n+\t\t\t\tline = readLine(in);\n+\n+\t\t\t\tif (line == null)\n+\t\t\t\t\tthrow new IOException(\"Malformed FCP message\");\n+\t\t\t\tif (\"EndMessage\".equals(line))\n+\t\t\t\t\tbreak;\n+\t\t\t\tif (\"Data\".equals(line)) {\n+\t\t\t\t\tString strLen = ret.parameter.get(\"DataLength\");\n+\t\t\t\t\tif (strLen == null)\n+\t\t\t\t\t\tthrow new IOException(\"DataLength not found\");\n+\t\t\t\t\tint len;\n+\t\t\t\t\ttry {\n+\t\t\t\t\t\tlen = Integer.parseInt(strLen);\n+\t\t\t\t\t} catch (NumberFormatException e) {\n+\t\t\t\t\t\tthrow new IOException(\"DataLength malformed\");\n+\t\t\t\t\t}\n+\t\t\t\t\tret.extraData = readData(in, len);\n+\t\t\t\t\tbreak;\n+\t\t\t\t}\n+\n+\t\t\t\tString[] v = line.split(\"=\", 2);\n+\t\t\t\tif (v.length != 2)\n+\t\t\t\t\tthrow new IOException(\"No '=' found in: \" + line);\n+\t\t\t\tret.parameter.put(v[0], v[1]);\n+\t\t\t}\n+\n+\t\t\treturn ret;\n+\t\t}\n+\n+\t\tvoid put(String key, String value) {\n+\t\t\tparameter.put(key, value);\n+\t\t}\n+\n+\t\tString get(String key) {\n+\t\t\treturn parameter.get(key);\n+\t\t}\n+\n+\t\tvoid writeTo(OutputStream os) throws IOException {\n+\t\t\tos.write(messageType.getBytes(\"UTF-8\"));\n+\t\t\tos.write('\\n');\n+\n+\t\t\tfor (Map.Entry<String, String> e : parameter.entrySet()) {\n+\t\t\t\tString l = e.getKey() + '=' + e.getValue();\n+\t\t\t\tos.write(l.getBytes(\"UTF-8\"));\n+\t\t\t\tos.write('\\n');\n+\t\t\t}\n+\n+\t\t\tos.write(\"EndMessage\".getBytes(\"UTF-8\"));\n+\t\t\tos.write('\\n');\n+\n+\t\t\tif (extraData != null)\n+\t\t\t\tos.write(extraData);\n+\t\t\tos.flush();\n+\t\t}\n+\t}\n+\n+\tprivate static String readLine(InputStream in) throws IOException {\n+\t\tbyte[] buf = new byte[256];\n+\t\tint offset = 0;\n+\n+\t\twhile (true) {\n+\t\t\tint b = in.read();\n+\t\t\tif (b == -1)\n+\t\t\t\treturn null;\n+\t\t\tif (b == '\\n') {\n+\t\t\t\tif (offset == 0)\n+\t\t\t\t\tcontinue; // skip empty line\n+\t\t\t\tbreak;\n+\t\t\t}\n+\n+\t\t\tif (offset == buf.length) {\n+\t\t\t\tif (offset >= 4096)\n+\t\t\t\t\tthrow new IOException(\"line too long\");\n+\n+\t\t\t\tbyte[] buf2 = new byte[buf.length * 2];\n+\t\t\t\tSystem.arraycopy(buf, 0, buf2, 0, buf.length);\n+\t\t\t\tbuf = buf2;\n+\t\t\t}\n+\n+\t\t\tbuf[offset++] = (byte) b;\n+\t\t}\n+\n+\t\treturn new String(buf, 0, offset, \"UTF-8\");\n+\t}\n+\n+\tprivate static byte[] readData(InputStream in, int len) throws IOException {\n+\t\tbyte[] buf = new byte[len];\n+\t\tint read = 0;\n+\t\twhile (read < len) {\n+\t\t\tint r = in.read(buf, read, len - read);\n+\t\t\tif (r == -1)\n+\t\t\t\tthrow new IOException(\"Not enough data\");\n+\t\t\tread += r;\n+\t\t}\n+\t\treturn buf;\n+\t}\n+}\n-- \n1.6.2.rc2\n"},{"id":"107436","messageId":"1236569765-8882-3-git-send-email-j16sdiz+freenet@gmail.com","threadId":"18224","inReplyTo":"1236569765-8882-2-git-send-email-j16sdiz+freenet-Re5JQEeQqe8AvxtiuMwx3w@public.gmane.org","subject":"[PoC PATCH JGIT 2/2] PoC Freenet transport","fromName":"Daniel Cheng (aka SDiZ)","fromEmail":"j16sdiz+freenet-re5jqeeqqe8avxtiumwx3w@public.gmane.org","sentAt":"2009-03-09T03:36:05Z","receivedAt":"2009-03-09T03:36:05Z","isPatch":true,"sender":{"key":"j16sdiz+freenet-re5jqeeqqe8avxtiumwx3w@public.gmane.org","avatar":null},"body":"\nSigned-off-by: Daniel Cheng (aka SDiZ) <j16sdiz+freenet-Re5JQEeQqe8AvxtiuMwx3w@public.gmane.org>\n---\n .../src/org/spearce/jgit/transport/Transport.java  |    3 +\n .../org/spearce/jgit/transport/TransportFcp2.java  |  436 ++++++++++++++++++++\n 2 files changed, 439 insertions(+), 0 deletions(-)\n create mode 100644 org.spearce.jgit/src/org/spearce/jgit/transport/TransportFcp2.java\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 a0a2575..3fc1735 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@@ -206,6 +206,9 @@ else if (TransportGitAnon.canHandle(remote))\n \t\telse if (TransportAmazonS3.canHandle(remote))\n \t\t\treturn new TransportAmazonS3(local, remote);\n \n+\t\telse if (TransportFcp2.canHandle(remote))\n+\t\t\treturn new TransportFcp2(local, remote);\n+\t\t\n \t\telse if (TransportBundleFile.canHandle(remote))\n \t\t\treturn new TransportBundleFile(local, remote);\n \ndiff --git a/org.spearce.jgit/src/org/spearce/jgit/transport/TransportFcp2.java b/org.spearce.jgit/src/org/spearce/jgit/transport/TransportFcp2.java\nnew file mode 100644\nindex 0000000..ed12995\n--- /dev/null\n+++ b/org.spearce.jgit/src/org/spearce/jgit/transport/TransportFcp2.java\n@@ -0,0 +1,436 @@\n+/*\n+ * Copyright (C) 2009, CHENG Yuk-Pong, Daniel <j16sdiz+freenet-Re5JQEeQqe8AvxtiuMwx3w@public.gmane.org>\n+ * Copyright (C) 2008, Shawn O. Pearce <spearce-o8xcbExO1WpAfugRpC6u6w@public.gmane.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.transport;\n+\n+import java.io.BufferedReader;\n+import java.io.ByteArrayInputStream;\n+import java.io.ByteArrayOutputStream;\n+import java.io.FileNotFoundException;\n+import java.io.IOException;\n+import java.io.OutputStream;\n+import java.net.URISyntaxException;\n+import java.util.ArrayList;\n+import java.util.Collection;\n+import java.util.Map;\n+import java.util.TreeMap;\n+\n+import org.spearce.jgit.errors.NotSupportedException;\n+import org.spearce.jgit.errors.PackProtocolException;\n+import org.spearce.jgit.errors.TransportException;\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.ProgressMonitor;\n+import org.spearce.jgit.lib.Ref;\n+import org.spearce.jgit.lib.Repository;\n+import org.spearce.jgit.lib.Ref.Storage;\n+import org.spearce.jgit.transport.fcpv2.FCPConnection;\n+\n+/**\n+ * Transport over Freenet Client Protocol 2.0\n+ * <p>\n+ * The Freenet transport is similar to HTTP Transport, but more restrictive. To\n+ * workaround the freenet restrictions, path separator ('/') are replaced with\n+ * dash ('-').\n+ *<p>\n+ * URI are in forms of <code>fcp://USK@PUBLIC_KEY/identifier</code> (read-only)\n+ * or <code>fcp://USK@PUBLIC_KEY^PRIVATE_KEY/identifier</code>. The identifier\n+ * must not contain any slash. The Freenet URI should begin with\n+ * <code>SSK@</code>, all updatable component are converted to <code>USK@</code>\n+ * internally.\n+ * \n+ * @see WalkFetchConnection\n+ */\n+class TransportFcp2 extends WalkTransport {\n+\tstatic boolean canHandle(final URIish uri) {\n+\t\tif (!uri.isRemote())\n+\t\t\treturn false;\n+\t\tfinal String s = uri.getScheme();\n+\t\treturn \"fcp\".equals(s);\n+\t}\n+\n+\tprivate final String publicKey;\n+\n+\tprivate final String privateKey;\n+\n+\tprivate final String identifier;\n+\n+\tprivate FCPConnection fcp;\n+\n+\tTransportFcp2(final Repository local, final URIish uri)\n+\t\t\tthrows NotSupportedException {\n+\t\tsuper(local, uri);\n+\n+\t\tString uriString = uri.toString();\n+\t\tif (!uriString.startsWith(\"fcp://SSK@\"))\n+\t\t\tthrow new NotSupportedException(\"URL not SSK@ \" + uri);\n+\n+\t\tString[] uriSegment = uriString.split(\"\\\\/\");\n+\t\tif (uriSegment.length != 4)\n+\t\t\tthrow new NotSupportedException(\"Invalid URL \" + uri);\n+\n+\t\tString[] keys = uriSegment[2].split(\"\\\\^\");\n+\t\tif (keys.length > 2)\n+\t\t\tthrow new NotSupportedException(\"Invalid URL \" + uri);\n+\t\tpublicKey = keys[0].substring(4); // Remove 'SSK@' prefix\n+\t\tprivateKey = (keys.length == 2) ? keys[1] : null;\n+\t\tSystem.out.println(privateKey);\n+\t\tidentifier = uriSegment[3];\n+\t}\n+\n+\t@Override\n+\tpublic FetchConnection openFetch() throws TransportException {\n+\t\ttry {\n+\t\t\tfcp = new FCPConnection();\n+\t\t\tfcp.connect();\n+\t\t\tfcp.hello(\"JGit-\" + toString());\n+\n+\t\t\tfinal Fcp2ObjectDB c = new Fcp2ObjectDB(\"/objects\", false);\n+\t\t\tfinal WalkFetchConnection r = new WalkFetchConnection(this, c);\n+\t\t\tr.available(c.readAdvertisedRefs());\n+\t\t\treturn r;\n+\t\t} catch (IOException e) {\n+\t\t\tthrow new TransportException(\"IO Error\" + e, e);\n+\t\t}\n+\t}\n+\n+\t@Override\n+\tpublic PushConnection openPush() throws TransportException {\n+\t\ttry {\n+\t\t\tfcp = new FCPConnection();\n+\t\t\tfcp.connect();\n+\t\t\tfcp.hello(\"JGit-\" + toString());\n+\n+\t\t\tfinal Fcp2ObjectDB c = new Fcp2ObjectDB(\"/objects\", false);\n+\t\t\tfinal WalkPushConnection r = new WalkPushConnection(this, c);\n+\t\t\tr.available(c.readAdvertisedRefs());\n+\t\t\treturn r;\n+\t\t} catch (IOException e) {\n+\t\t\tthrow new TransportException(\"IO Error\" + e, e);\n+\t\t}\n+\t}\n+\n+\t@Override\n+\tpublic void close() {\n+\t\ttry {\n+\t\t\tif (fcp != null)\n+\t\t\t\tfcp.close();\n+\t\t} catch (IOException e) {\n+\t\t\t// Fall through.\n+\t\t}\n+\t\tfcp = null;\n+\t}\n+\n+\tclass Fcp2ObjectDB extends WalkRemoteObjectDatabase {\n+\t\tprivate final String path;\n+\t\tprivate boolean useArchive;\n+\n+\t\t@Override\n+\t\tvoid deleteFile(String path) throws IOException {\n+\t\t\t// TODO Auto-generated method stub\n+\t\t\tsuper.deleteFile(path);\n+\t\t}\n+\n+\t\t@Override\n+\t\tOutputStream writeFile(String path, ProgressMonitor monitor,\n+\t\t\t\tString monitorTask) throws IOException {\n+\t\t\tSystem.err.println(\" write -> \" + path);\n+\t\t\treturn new FcpOutputStream(path, monitor, monitorTask);\n+\t\t}\n+\n+\t\tclass FcpOutputStream extends OutputStream {\n+\t\t\tByteArrayOutputStream os = new ByteArrayOutputStream();\n+\n+\t\t\tprivate String path;\n+\n+\t\t\tprivate ProgressMonitor monitor;\n+\n+\t\t\tprivate String monitorTask;\n+\n+\t\t\tpublic FcpOutputStream(String path, ProgressMonitor monitor,\n+\t\t\t\t\tString monitorTask) {\n+\t\t\t\tif (monitor == null)\n+\t\t\t\t\tmonitor = NullProgressMonitor.INSTANCE;\n+\t\t\t\tif (monitorTask == null)\n+\t\t\t\t\tmonitorTask = \"Uploading \" + path;\n+\n+\t\t\t\tthis.path = path;\n+\t\t\t\tthis.monitor = monitor;\n+\t\t\t\tthis.monitorTask = monitorTask;\n+\t\t\t}\n+\n+\t\t\t@Override\n+\t\t\tpublic void write(int b) throws IOException {\n+\t\t\t\tos.write(b);\n+\t\t\t}\n+\n+\t\t\t@Override\n+\t\t\tpublic void close() throws IOException {\n+\t\t\t\tbyte[] b = os.toByteArray();\n+\t\t\t\tos = null;\n+\n+\t\t\t\tfcp.simplePut(getFreenetURI(path, true), b, monitor,\n+\t\t\t\t\t\tmonitorTask);\n+\t\t\t}\n+\t\t}\n+\n+\t\tFcp2ObjectDB(String path, boolean useArchive) {\n+\t\t\tif (path.endsWith(\"/\"))\n+\t\t\t\tpath = path.substring(0, path.length() - 1);\n+\t\t\tif (!path.startsWith(\"/\"))\n+\t\t\t\tpath = \"/\" + path;\n+\t\t\tthis.path = path;\n+\t\t\tthis.useArchive = useArchive;\n+\t\t}\n+\n+\t\tprivate String resolveKey(String subpath) {\n+\t\t\tif (subpath.endsWith(\"/\"))\n+\t\t\t\tsubpath = subpath.substring(0, subpath.length() - 1);\n+\t\t\tString k = path;\n+\t\t\twhile (subpath.startsWith(ROOT_DIR)) {\n+\t\t\t\tk = k.substring(0, k.lastIndexOf('/'));\n+\t\t\t\tsubpath = subpath.substring(3);\n+\t\t\t}\n+\t\t\treturn k + \"/\" + subpath;\n+\t\t}\n+\n+\t\t@Override\n+\t\tURIish getURI() {\n+\t\t\ttry {\n+\t\t\t\t// just a pseudo-URIish for logging, not really supported\n+\t\t\t\treturn new URIish(\"fcp://SSK@\" + publicKey + \"/\" + identifier\n+\t\t\t\t\t\t+ path);\n+\t\t\t} catch (URISyntaxException e) {\n+\t\t\t\treturn null;\n+\t\t\t}\n+\t\t}\n+\n+\t\t@Override\n+\t\tCollection<WalkRemoteObjectDatabase> getAlternates() throws IOException {\n+\t\t\tif (!useArchive)\t// try to use archive\n+\t\t\t\treturn java.util.Arrays.asList( \n+\t\t\t\t new WalkRemoteObjectDatabase[] { new  Fcp2ObjectDB(path, true) } );\n+\t\t\treturn null;\n+\t\t}\n+\n+\t\t@Override\n+\t\tWalkRemoteObjectDatabase openAlternate(final String location)\n+\t\t\t\tthrows IOException {\n+\t\t\treturn null;\n+\t\t}\n+\n+\t\t@Override\n+\t\tCollection<String> getPackNames() throws IOException {\n+\t\t\tfinal Collection<String> packs = new ArrayList<String>();\n+\t\t\ttry {\n+\t\t\t\tfinal BufferedReader br = openReader(INFO_PACKS);\n+\t\t\t\ttry {\n+\t\t\t\t\tfor (;;) {\n+\t\t\t\t\t\tfinal String s = br.readLine();\n+\t\t\t\t\t\tif (s == null || s.length() == 0)\n+\t\t\t\t\t\t\tbreak;\n+\t\t\t\t\t\tif (!s.startsWith(\"P pack-\") || !s.endsWith(\".pack\"))\n+\t\t\t\t\t\t\tthrow invalidAdvertisement(s);\n+\t\t\t\t\t\tpacks.add(s.substring(2));\n+\t\t\t\t\t}\n+\t\t\t\t\treturn packs;\n+\t\t\t\t} finally {\n+\t\t\t\t\tbr.close();\n+\t\t\t\t}\n+\t\t\t} catch (FileNotFoundException err) {\n+\t\t\t\treturn packs;\n+\t\t\t}\n+\t\t}\n+\n+\t\t@Override\n+\t\tFileStream open(final String path) throws IOException {\n+\t\t\tSystem.err.println(\" read -> \" + path);\n+\t\t\tbyte[] b = fcp.simpleGet(getFreenetURI(path, false));\n+\t\t\tif (b == null)\n+\t\t\t\tthrow new FileNotFoundException();\n+\n+\t\t\tByteArrayInputStream is = new ByteArrayInputStream(b);\n+\t\t\treturn new FileStream(is);\n+\t\t}\n+\n+\t\tprivate String getFreenetURI(final String path, boolean push) {\n+\t\t\tString key = push ? privateKey : publicKey;\n+\t\t\tString fPath = resolveKey(path);\n+\t\t\tString ret;\n+\n+\t\t\tif (useArchive)\n+\t\t\t\tret = \"USK@\" + key + \"/\" + identifier + \"/1/\" + fPath;\n+\t\t\telse\n+\t\t\t\tret = \"USK@\" + key + \"/\" + identifier + fPath.replaceAll(\"\\\\/\", \"-\") + \"/1/\";\n+\n+\t\t\tSystem.err.println(ret);\n+\t\t\treturn ret;\n+\t\t}\n+\n+\t\tMap<String, Ref> readAdvertisedRefs() throws TransportException {\n+\t\t\tfinal TreeMap<String, Ref> avail = new TreeMap<String, Ref>();\n+\t\t\ttry {\n+\t\t\t\treadPackedRefs(avail);\n+\t\t\t} catch (TransportException t) {}\n+\t\t\ttry {\n+\t\t\t\treadInfoRefs(avail);\n+\t\t\t} catch (TransportException t) {}\n+\t\t\ttry {\n+\t\t\t\treadRef(avail, Constants.HEAD);\n+\t\t\t} catch (TransportException t) {}\n+\n+\t\t\tif (avail.isEmpty() && !useArchive) {\n+\t\t\t\treturn  new Fcp2ObjectDB(path, true).readAdvertisedRefs();\n+\t\t\t}\n+\n+\t\t\treturn avail;\n+\t\t}\n+\n+\t\tprivate Map<String, Ref> readInfoRefs(TreeMap<String, Ref> avail)\n+\t\t\t\tthrows TransportException {\n+\t\t\ttry {\n+\t\t\t\tfinal BufferedReader br = openReader(INFO_REFS);\n+\t\t\t\tfor (;;) {\n+\t\t\t\t\tString line = br.readLine();\n+\t\t\t\t\tif (line == null)\n+\t\t\t\t\t\tbreak;\n+\n+\t\t\t\t\tfinal int tab = line.indexOf('\\t');\n+\t\t\t\t\tif (tab < 0)\n+\t\t\t\t\t\tthrow invalidAdvertisement(line);\n+\n+\t\t\t\t\tString name;\n+\t\t\t\t\tfinal ObjectId id;\n+\n+\t\t\t\t\tname = line.substring(tab + 1);\n+\t\t\t\t\tid = ObjectId.fromString(line.substring(0, tab));\n+\t\t\t\t\tif (name.endsWith(\"^{}\")) {\n+\t\t\t\t\t\tname = name.substring(0, name.length() - 3);\n+\t\t\t\t\t\tfinal Ref prior = avail.get(name);\n+\t\t\t\t\t\tif (prior == null)\n+\t\t\t\t\t\t\tthrow outOfOrderAdvertisement(name);\n+\n+\t\t\t\t\t\tif (prior.getPeeledObjectId() != null)\n+\t\t\t\t\t\t\tthrow duplicateAdvertisement(name + \"^{}\");\n+\n+\t\t\t\t\t\tavail.put(name, new Ref(Ref.Storage.NETWORK, name,\n+\t\t\t\t\t\t\t\tprior.getObjectId(), id, true));\n+\t\t\t\t\t} else {\n+\t\t\t\t\t\tfinal Ref prior = avail.put(name, new Ref(\n+\t\t\t\t\t\t\t\tRef.Storage.NETWORK, name, id));\n+\t\t\t\t\t\tif (prior != null)\n+\t\t\t\t\t\t\tthrow duplicateAdvertisement(name);\n+\t\t\t\t\t}\n+\t\t\t\t}\n+\t\t\t\treturn avail;\n+\t\t\t} catch (IOException err) {\n+\t\t\t\tthrow new TransportException(INFO_REFS\n+\t\t\t\t\t\t+ \": cannot read available refs\", err);\n+\t\t\t}\n+\t\t}\n+\n+\t\tprivate PackProtocolException outOfOrderAdvertisement(final String n) {\n+\t\t\treturn new PackProtocolException(\"advertisement of \" + n\n+\t\t\t\t\t+ \"^{} came before \" + n);\n+\t\t}\n+\n+\t\tprivate PackProtocolException duplicateAdvertisement(final String n) {\n+\t\t\treturn new PackProtocolException(\"duplicate advertisements of \" + n);\n+\t\t}\n+\n+\t\tprivate Ref readRef(final TreeMap<String, Ref> avail, final String rn)\n+\t\t\t\tthrows TransportException {\n+\t\t\tfinal String s;\n+\t\t\tString ref = ROOT_DIR + rn;\n+\t\t\ttry {\n+\t\t\t\tfinal BufferedReader br = openReader(ref);\n+\t\t\t\ttry {\n+\t\t\t\t\ts = br.readLine();\n+\t\t\t\t} finally {\n+\t\t\t\t\tbr.close();\n+\t\t\t\t}\n+\t\t\t} catch (FileNotFoundException noRef) {\n+\t\t\t\treturn null;\n+\t\t\t} catch (IOException err) {\n+\t\t\t\tthrow new TransportException(getURI(), \"read \" + ref, err);\n+\t\t\t}\n+\n+\t\t\tif (s == null)\n+\t\t\t\tthrow new TransportException(getURI(), \"Empty ref: \" + rn);\n+\n+\t\t\tif (s.startsWith(\"ref: \")) {\n+\t\t\t\tfinal String target = s.substring(\"ref: \".length());\n+\t\t\t\tRef r = avail.get(target);\n+\t\t\t\tif (r == null)\n+\t\t\t\t\tr = readRef(avail, target);\n+\t\t\t\tif (r == null)\n+\t\t\t\t\treturn null;\n+\t\t\t\tr = new Ref(r.getStorage(), rn, r.getObjectId(), r\n+\t\t\t\t\t\t.getPeeledObjectId(), r.isPeeled());\n+\t\t\t\tavail.put(r.getName(), r);\n+\t\t\t\treturn r;\n+\t\t\t}\n+\n+\t\t\tif (ObjectId.isId(s)) {\n+\t\t\t\t// FIXME this does not really works\n+\t\t\t\tfinal Ref r = new Ref(loose(avail.get(rn)), rn, ObjectId\n+\t\t\t\t\t\t.fromString(s));\n+\t\t\t\tavail.put(r.getName(), r);\n+\t\t\t\treturn r;\n+\t\t\t}\n+\n+\t\t\tthrow new TransportException(getURI(), \"Bad ref: \" + rn + \": \" + s);\n+\t\t}\n+\n+\t\tprivate Storage loose(final Ref r) {\n+\t\t\tif (r != null && r.getStorage() == Storage.PACKED)\n+\t\t\t\treturn Storage.LOOSE_PACKED;\n+\t\t\treturn Storage.LOOSE;\n+\t\t}\n+\n+\t\tprivate PackProtocolException invalidAdvertisement(final String n) {\n+\t\t\treturn new PackProtocolException(\"invalid advertisement of \" + n);\n+\t\t}\n+\n+\t\t@Override\n+\t\tvoid close() {\n+\t\t\t// We do not maintain persistent connections.\n+\t\t}\n+\t}\n+}\n-- \n1.6.2.rc2\n"},{"id":"107439","messageId":"ff6a9c820903082258q24fd6cefh4bc528e79cecedf3@mail.gmail.com","threadId":"18224","inReplyTo":"1236569765-8882-1-git-send-email-j16sdiz+freenet-Re5JQEeQqe8AvxtiuMwx3w@public.gmane.org","subject":"Re: [PoC PATCH JGIT 0/2] Proof of concept code for Git Freenet transport","fromName":"Daniel Cheng","fromEmail":"j16sdiz+freenet-re5jqeeqqe8avxtiumwx3w@public.gmane.org","sentAt":"2009-03-09T05:58:57Z","receivedAt":"2009-03-09T05:58:57Z","isPatch":true,"sender":{"key":"j16sdiz+freenet-re5jqeeqqe8avxtiumwx3w@public.gmane.org","avatar":null},"body":"On Mon, Mar 9, 2009 at 11:36 AM, Daniel Cheng (aka SDiZ)\n<j16sdiz+freenet-Re5JQEeQqe8AvxtiuMwx3w@public.gmane.org> wrote:\n> Hi JGit / Freenet community,\n>\n> Here is some proof-of-concept code for Git-over-Freenet.\n> I am sending this to see the feedback from communities.\n\nSorry, all. The code I have posted wasn't work.\n(the pack file is missing on push, only idx files are pushed)\n\nThe new, fixed code is now available at http://github.com/j16sdiz/egit-freenet/\nPlease use this repository if you want to actually try it.\n\n>\n> The code need some more cleanups, so it it is not ready for apply (yet).\n>\n> This is a real-life example\n>\n> Push:\n>   $ git remote add fcp fcp://SSK@[my public key]^[my private key]/test.git\n>   $ ./jgit push fcp refs/remotes/origin/stable:refs/heads/master\n>\n>  /ALTERNATIVLY/\n>\n>   Insert a bare repository under USK@<.....>/test.git/-1/\n>\n> Pull:\n>  $ ./jgit clone fcp://SSK@[my public key]^[my private key]/test.git\n>\n>\n> To workaround the metadata update problem, this client translate the\n> path seperator to \"-\", that means:\n>\n> On push:\n>   objects/aa/bbbbbbbb   --> USK@..../test.git-objects-aa-bbbbbbb/-1/\n>   refs/heads/xxx        --> USK@..../test.git-objects-ref-heads-xxx/-1/\n>\n> On pull:\n>   To support uploading from jSite,\n>    when we load the info/refs we first check USK@..../test.git-info-refs/-1/\n>    if it is unavailiable, we would use USK@..../test.git/-1/info/refs\n>\n>   The \"traditional\" type (USK@..../test.git/-1/objects) repository is\n>   always added as an alternative objects database. No other info/alternatives\n>   are supported\n>\n> FIXME:\n>  - How to store the private key of repository?\n>   Currently, we use URI of form fcp://SSK@<public key>^<private key>/some-id\n>   This is quite ugly. Could we use a per remote Config ? How can I get remote\n>   name from transport?\n>\n>  - Make pushing async, could we?\n>\n>\n>\n"},{"id":"107445","messageId":"alpine.DEB.1.00.0903091056510.10279@pacific.mpi-cbg.de","threadId":"18224","inReplyTo":"1236569765-8882-1-git-send-email-j16sdiz+freenet@gmail.com","subject":"Re: [PoC PATCH JGIT 0/2] Proof of concept code for Git Freenet transport","fromName":"Johannes Schindelin","fromEmail":"johannes.schindelin@gmx.de","sentAt":"2009-03-09T09:58:19Z","receivedAt":"2009-03-09T09:58:19Z","isPatch":true,"sender":{"key":"johannes.schindelin@gmx.de","avatar":"https://avatars.githubusercontent.com/u/127790?v=4"},"body":"Hi,\n\nOn Mon, 9 Mar 2009, Daniel Cheng (aka SDiZ) wrote:\n\n> Hi JGit / Freenet community,\n> \n> Here is some proof-of-concept code for Git-over-Freenet.\n\nThanks!\n\nI was talking to some Freenet guys at the GSoC mentor summit 2007, and \nalways wanted to give it a try (albeit using C Git, not JGit), but that \nitem just got pushed off my TODO list...\n\nCiao,\nDscho\n"},{"id":"107461","messageId":"20090309145955.GB11989@spearce.org","threadId":"18224","inReplyTo":"1236569765-8882-1-git-send-email-j16sdiz+freenet@gmail.com","subject":"Re: [PoC PATCH JGIT 0/2] Proof of concept code for Git Freenet transport","fromName":"Shawn O. Pearce","fromEmail":"spearce@spearce.org","sentAt":"2009-03-09T14:59:55Z","receivedAt":"2009-03-09T14:59:55Z","isPatch":true,"sender":{"key":"spearce@spearce.org","avatar":"https://avatars.githubusercontent.com/u/34844?v=4"},"body":"\"Daniel Cheng (aka SDiZ)\" <j16sdiz+freenet@gmail.com> wrote:\n> \n> Here is some proof-of-concept code for Git-over-Freenet.\n> I am sending this to see the feedback from communities.\n\nInteresting!\n\nI'm quite open to bringing this into JGit itself, so long as it\ndoesn't cause us to import 42 other libraries that we don't need.\n\n:-)\n \n> FIXME:\n>  - How to store the private key of repository?\n>    Currently, we use URI of form fcp://SSK@<public key>^<private key>/some-id\n>    This is quite ugly. Could we use a per remote Config ? How can I get remote\n>    name from transport?\n\nUse something like amazon-s3 does, where there is a file path under\n~/ specified by the host part of the URI, and store the data there?\n\nI did that rather than storing into ~/.gitconfig or GIT_DIR/config\nas the private key really should remain private.  Chmod'ing your\nconfig file to be private is a pain, and \"git config\" last I checked\nwouldn't preserve the permissions when it modified the file for you.\n\nThat said, the Transport API in JGit doesn't get access to the\nRemoteConfig it was created with, because it isn't always made\nfrom a remote (e.g. you can pass the URIish on the command line).\nWe could however optionally expose it, but in general I prefer\nto make everything that can be obtained via the RemoteConfig be\nsettable without one, so it can also come through from the CLI\nor some higher-level GUI.\n \n>  - Make pushing async, could we?\n\nI'm not sure I understand that.  Do you want to background the push\ntask?  Isn't that what your shell is for?  Or do you want to perform\nthe writes in an async fashion in parallel, to reduce the latency?\n\n-- \nShawn.\n"}]}