{"thread":{"id":"16690","subject":"[JGIT PATCH 00/15] More patch parsing support","startedAt":"2008-12-12T02:46:06Z","lastAt":"2008-12-12T23:18:15Z","messageCount":22,"participants":["Shawn O. Pearce","Sverre Rabbelier","Robin Rosenberg"],"isPatch":true,"patchVersion":1,"patchTotal":15},"messages":[{"id":"97661","messageId":"1229049981-14152-1-git-send-email-spearce@spearce.org","threadId":"16690","inReplyTo":null,"subject":"[JGIT PATCH 00/15] More patch parsing support","fromName":"Shawn O. Pearce","fromEmail":"spearce@spearce.org","sentAt":"2008-12-12T02:46:06Z","receivedAt":"2008-12-12T02:46:06Z","isPatch":true,"sender":{"key":"spearce@spearce.org","avatar":"https://avatars.githubusercontent.com/u/34844?v=4"},"body":"Unit test improvements, introduction of IntList (which Dscho also\nneeds for his diff code, its probably a drop-in replacement for him),\nand support for parsing the meta data of binary patches.\n\nI'm writing the \"diff --cc\" parser right now but the unit tests\nfor it still fail so I'm not including those changes just yet.\n\n\nShawn O. Pearce (15):\n  Correct use of TemporaryBuffer in Patch\n  Add tests for TemporaryBuffer\n  Add IntList as a more efficient representation of List<Integer>\n  Add lineMap computer to RawParseUtils to index locations of line\n    starts\n  Define FileHeader.PatchType to report the style of patch used\n  Test for non-git binary files and mark them as PatchType.BINARY\n  Set empty patches with no Git metadata to PatchType.BINARY\n  Always use the FileHeader buffer during Patch.parseHunks\n  Parse \"GIT binary patch\" style patch metadata\n  Record patch parsing errors for later inspection by applications\n  Fix Patch.parse to honor the end point passed in\n  Correctly handle hunk headers such as \"@@ -0,0 +1 @@\"\n  Patch parse test comparing \"git log -p\" output to \"git log --numstat\"\n  Abstract the hunk header testing into a method\n  Treat \"diff --combined\" the same as \"diff --cc\"\n\n .../spearce/jgit/patch/EGitPatchHistoryTest.java   |  221 ++++++++++++\n .../tst/org/spearce/jgit/patch/FileHeaderTest.java |   70 +++-\n .../tst/org/spearce/jgit/patch/PatchErrorTest.java |  174 +++++++++\n .../tst/org/spearce/jgit/patch/PatchTest.java      |   78 ++++\n .../spearce/jgit/patch/testError_BodyTooLong.patch |   17 +\n .../jgit/patch/testError_DisconnectedHunk.patch    |   30 ++\n .../jgit/patch/testError_GarbageBetweenFiles.patch |   33 ++\n .../patch/testError_GitBinaryNoForwardHunk.patch   |   10 +\n .../jgit/patch/testError_TruncatedNew.patch        |   15 +\n .../jgit/patch/testError_TruncatedOld.patch        |   15 +\n .../spearce/jgit/patch/testParse_GitBinary.patch   |  135 +++++++\n .../spearce/jgit/patch/testParse_NoBinary.patch    |   83 +++++\n .../tst/org/spearce/jgit/util/IntListTest.java     |  156 ++++++++\n .../jgit/util/RawParseUtils_LineMapTest.java       |   88 +++++\n .../org/spearce/jgit/util/TemporaryBufferTest.java |  374 ++++++++++++++++++++\n .../tst/org/spearce/jgit/util/TestRng.java         |   61 ++++\n .../src/org/spearce/jgit/patch/BinaryHunk.java     |  127 +++++++\n .../src/org/spearce/jgit/patch/FileHeader.java     |   94 +++++-\n .../src/org/spearce/jgit/patch/FormatError.java    |   95 +++++\n .../src/org/spearce/jgit/patch/HunkHeader.java     |   41 ++-\n .../src/org/spearce/jgit/patch/Patch.java          |  197 ++++++++---\n .../src/org/spearce/jgit/util/IntList.java         |  128 +++++++\n .../src/org/spearce/jgit/util/RawParseUtils.java   |   31 ++\n .../src/org/spearce/jgit/util/TemporaryBuffer.java |    4 +-\n 24 files changed, 2187 insertions(+), 90 deletions(-)\n create mode 100644 org.spearce.jgit.test/exttst/org/spearce/jgit/patch/EGitPatchHistoryTest.java\n create mode 100644 org.spearce.jgit.test/tst/org/spearce/jgit/patch/PatchErrorTest.java\n create mode 100644 org.spearce.jgit.test/tst/org/spearce/jgit/patch/testError_BodyTooLong.patch\n create mode 100644 org.spearce.jgit.test/tst/org/spearce/jgit/patch/testError_DisconnectedHunk.patch\n create mode 100644 org.spearce.jgit.test/tst/org/spearce/jgit/patch/testError_GarbageBetweenFiles.patch\n create mode 100644 org.spearce.jgit.test/tst/org/spearce/jgit/patch/testError_GitBinaryNoForwardHunk.patch\n create mode 100644 org.spearce.jgit.test/tst/org/spearce/jgit/patch/testError_TruncatedNew.patch\n create mode 100644 org.spearce.jgit.test/tst/org/spearce/jgit/patch/testError_TruncatedOld.patch\n create mode 100644 org.spearce.jgit.test/tst/org/spearce/jgit/patch/testParse_GitBinary.patch\n create mode 100644 org.spearce.jgit.test/tst/org/spearce/jgit/patch/testParse_NoBinary.patch\n create mode 100644 org.spearce.jgit.test/tst/org/spearce/jgit/util/IntListTest.java\n create mode 100644 org.spearce.jgit.test/tst/org/spearce/jgit/util/RawParseUtils_LineMapTest.java\n create mode 100644 org.spearce.jgit.test/tst/org/spearce/jgit/util/TemporaryBufferTest.java\n create mode 100644 org.spearce.jgit.test/tst/org/spearce/jgit/util/TestRng.java\n create mode 100644 org.spearce.jgit/src/org/spearce/jgit/patch/BinaryHunk.java\n create mode 100644 org.spearce.jgit/src/org/spearce/jgit/patch/FormatError.java\n create mode 100644 org.spearce.jgit/src/org/spearce/jgit/util/IntList.java\n"},{"id":"97670","messageId":"1229049981-14152-2-git-send-email-spearce@spearce.org","threadId":"16690","inReplyTo":"1229049981-14152-1-git-send-email-spearce@spearce.org","subject":"[JGIT PATCH 01/15] Correct use of TemporaryBuffer in Patch","fromName":"Shawn O. Pearce","fromEmail":"spearce@spearce.org","sentAt":"2008-12-12T02:46:07Z","receivedAt":"2008-12-12T02:46:07Z","isPatch":true,"sender":{"key":"spearce@spearce.org","avatar":"https://avatars.githubusercontent.com/u/34844?v=4"},"body":"We need to destroy the buffer in case it created a\ntemporary file to hold the content.\n\nSigned-off-by: Shawn O. Pearce <spearce@spearce.org>\n---\n .../src/org/spearce/jgit/patch/Patch.java          |   10 +++++++---\n 1 files changed, 7 insertions(+), 3 deletions(-)\n\ndiff --git a/org.spearce.jgit/src/org/spearce/jgit/patch/Patch.java b/org.spearce.jgit/src/org/spearce/jgit/patch/Patch.java\nindex 165058d..d59635a 100644\n--- a/org.spearce.jgit/src/org/spearce/jgit/patch/Patch.java\n+++ b/org.spearce.jgit/src/org/spearce/jgit/patch/Patch.java\n@@ -105,9 +105,13 @@ public void parse(final InputStream is) throws IOException {\n \n \tprivate static byte[] readFully(final InputStream is) throws IOException {\n \t\tfinal TemporaryBuffer b = new TemporaryBuffer();\n-\t\tb.copy(is);\n-\t\tfinal byte[] buf = b.toByteArray();\n-\t\treturn buf;\n+\t\ttry {\n+\t\t\tb.copy(is);\n+\t\t\tb.close();\n+\t\t\treturn b.toByteArray();\n+\t\t} finally {\n+\t\t\tb.destroy();\n+\t\t}\n \t}\n \n \t/**\n-- \n1.6.1.rc2.306.ge5d5e\n"},{"id":"97663","messageId":"1229049981-14152-3-git-send-email-spearce@spearce.org","threadId":"16690","inReplyTo":"1229049981-14152-2-git-send-email-spearce@spearce.org","subject":"[JGIT PATCH 02/15] Add tests for TemporaryBuffer","fromName":"Shawn O. Pearce","fromEmail":"spearce@spearce.org","sentAt":"2008-12-12T02:46:08Z","receivedAt":"2008-12-12T02:46:08Z","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 .../org/spearce/jgit/util/TemporaryBufferTest.java |  374 ++++++++++++++++++++\n .../tst/org/spearce/jgit/util/TestRng.java         |   61 ++++\n .../src/org/spearce/jgit/util/TemporaryBuffer.java |    4 +-\n 3 files changed, 437 insertions(+), 2 deletions(-)\n create mode 100644 org.spearce.jgit.test/tst/org/spearce/jgit/util/TemporaryBufferTest.java\n create mode 100644 org.spearce.jgit.test/tst/org/spearce/jgit/util/TestRng.java\n\ndiff --git a/org.spearce.jgit.test/tst/org/spearce/jgit/util/TemporaryBufferTest.java b/org.spearce.jgit.test/tst/org/spearce/jgit/util/TemporaryBufferTest.java\nnew file mode 100644\nindex 0000000..e532d98\n--- /dev/null\n+++ b/org.spearce.jgit.test/tst/org/spearce/jgit/util/TemporaryBufferTest.java\n@@ -0,0 +1,374 @@\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.util;\n+\n+import java.io.ByteArrayInputStream;\n+import java.io.ByteArrayOutputStream;\n+import java.io.IOException;\n+import java.util.Arrays;\n+\n+import junit.framework.TestCase;\n+\n+public class TemporaryBufferTest extends TestCase {\n+\tpublic void testEmpty() throws IOException {\n+\t\tfinal TemporaryBuffer b = new TemporaryBuffer();\n+\t\ttry {\n+\t\t\tb.close();\n+\t\t\tassertEquals(0, b.length());\n+\t\t\tfinal byte[] r = b.toByteArray();\n+\t\t\tassertNotNull(r);\n+\t\t\tassertEquals(0, r.length);\n+\t\t} finally {\n+\t\t\tb.destroy();\n+\t\t}\n+\t}\n+\n+\tpublic void testOneByte() throws IOException {\n+\t\tfinal TemporaryBuffer b = new TemporaryBuffer();\n+\t\tfinal byte test = (byte) new TestRng(getName()).nextInt();\n+\t\ttry {\n+\t\t\tb.write(test);\n+\t\t\tb.close();\n+\t\t\tassertEquals(1, b.length());\n+\t\t\t{\n+\t\t\t\tfinal byte[] r = b.toByteArray();\n+\t\t\t\tassertNotNull(r);\n+\t\t\t\tassertEquals(1, r.length);\n+\t\t\t\tassertEquals(test, r[0]);\n+\t\t\t}\n+\t\t\t{\n+\t\t\t\tfinal ByteArrayOutputStream o = new ByteArrayOutputStream();\n+\t\t\t\tb.writeTo(o, null);\n+\t\t\t\to.close();\n+\t\t\t\tfinal byte[] r = o.toByteArray();\n+\t\t\t\tassertEquals(1, r.length);\n+\t\t\t\tassertEquals(test, r[0]);\n+\t\t\t}\n+\t\t} finally {\n+\t\t\tb.destroy();\n+\t\t}\n+\t}\n+\n+\tpublic void testOneBlock_BulkWrite() throws IOException {\n+\t\tfinal TemporaryBuffer b = new TemporaryBuffer();\n+\t\tfinal byte[] test = new TestRng(getName())\n+\t\t\t\t.nextBytes(TemporaryBuffer.Block.SZ);\n+\t\ttry {\n+\t\t\tb.write(test, 0, 2);\n+\t\t\tb.write(test, 2, 4);\n+\t\t\tb.write(test, 6, test.length - 6 - 2);\n+\t\t\tb.write(test, test.length - 2, 2);\n+\t\t\tb.close();\n+\t\t\tassertEquals(test.length, b.length());\n+\t\t\t{\n+\t\t\t\tfinal byte[] r = b.toByteArray();\n+\t\t\t\tassertNotNull(r);\n+\t\t\t\tassertEquals(test.length, r.length);\n+\t\t\t\tassertTrue(Arrays.equals(test, r));\n+\t\t\t}\n+\t\t\t{\n+\t\t\t\tfinal ByteArrayOutputStream o = new ByteArrayOutputStream();\n+\t\t\t\tb.writeTo(o, null);\n+\t\t\t\to.close();\n+\t\t\t\tfinal byte[] r = o.toByteArray();\n+\t\t\t\tassertEquals(test.length, r.length);\n+\t\t\t\tassertTrue(Arrays.equals(test, r));\n+\t\t\t}\n+\t\t} finally {\n+\t\t\tb.destroy();\n+\t\t}\n+\t}\n+\n+\tpublic void testOneBlockAndHalf_BulkWrite() throws IOException {\n+\t\tfinal TemporaryBuffer b = new TemporaryBuffer();\n+\t\tfinal byte[] test = new TestRng(getName())\n+\t\t\t\t.nextBytes(TemporaryBuffer.Block.SZ * 3 / 2);\n+\t\ttry {\n+\t\t\tb.write(test, 0, 2);\n+\t\t\tb.write(test, 2, 4);\n+\t\t\tb.write(test, 6, test.length - 6 - 2);\n+\t\t\tb.write(test, test.length - 2, 2);\n+\t\t\tb.close();\n+\t\t\tassertEquals(test.length, b.length());\n+\t\t\t{\n+\t\t\t\tfinal byte[] r = b.toByteArray();\n+\t\t\t\tassertNotNull(r);\n+\t\t\t\tassertEquals(test.length, r.length);\n+\t\t\t\tassertTrue(Arrays.equals(test, r));\n+\t\t\t}\n+\t\t\t{\n+\t\t\t\tfinal ByteArrayOutputStream o = new ByteArrayOutputStream();\n+\t\t\t\tb.writeTo(o, null);\n+\t\t\t\to.close();\n+\t\t\t\tfinal byte[] r = o.toByteArray();\n+\t\t\t\tassertEquals(test.length, r.length);\n+\t\t\t\tassertTrue(Arrays.equals(test, r));\n+\t\t\t}\n+\t\t} finally {\n+\t\t\tb.destroy();\n+\t\t}\n+\t}\n+\n+\tpublic void testOneBlockAndHalf_SingleWrite() throws IOException {\n+\t\tfinal TemporaryBuffer b = new TemporaryBuffer();\n+\t\tfinal byte[] test = new TestRng(getName())\n+\t\t\t\t.nextBytes(TemporaryBuffer.Block.SZ * 3 / 2);\n+\t\ttry {\n+\t\t\tfor (int i = 0; i < test.length; i++)\n+\t\t\t\tb.write(test[i]);\n+\t\t\tb.close();\n+\t\t\tassertEquals(test.length, b.length());\n+\t\t\t{\n+\t\t\t\tfinal byte[] r = b.toByteArray();\n+\t\t\t\tassertNotNull(r);\n+\t\t\t\tassertEquals(test.length, r.length);\n+\t\t\t\tassertTrue(Arrays.equals(test, r));\n+\t\t\t}\n+\t\t\t{\n+\t\t\t\tfinal ByteArrayOutputStream o = new ByteArrayOutputStream();\n+\t\t\t\tb.writeTo(o, null);\n+\t\t\t\to.close();\n+\t\t\t\tfinal byte[] r = o.toByteArray();\n+\t\t\t\tassertEquals(test.length, r.length);\n+\t\t\t\tassertTrue(Arrays.equals(test, r));\n+\t\t\t}\n+\t\t} finally {\n+\t\t\tb.destroy();\n+\t\t}\n+\t}\n+\n+\tpublic void testOneBlockAndHalf_Copy() throws IOException {\n+\t\tfinal TemporaryBuffer b = new TemporaryBuffer();\n+\t\tfinal byte[] test = new TestRng(getName())\n+\t\t\t\t.nextBytes(TemporaryBuffer.Block.SZ * 3 / 2);\n+\t\ttry {\n+\t\t\tfinal ByteArrayInputStream in = new ByteArrayInputStream(test);\n+\t\t\tb.write(in.read());\n+\t\t\tb.copy(in);\n+\t\t\tb.close();\n+\t\t\tassertEquals(test.length, b.length());\n+\t\t\t{\n+\t\t\t\tfinal byte[] r = b.toByteArray();\n+\t\t\t\tassertNotNull(r);\n+\t\t\t\tassertEquals(test.length, r.length);\n+\t\t\t\tassertTrue(Arrays.equals(test, r));\n+\t\t\t}\n+\t\t\t{\n+\t\t\t\tfinal ByteArrayOutputStream o = new ByteArrayOutputStream();\n+\t\t\t\tb.writeTo(o, null);\n+\t\t\t\to.close();\n+\t\t\t\tfinal byte[] r = o.toByteArray();\n+\t\t\t\tassertEquals(test.length, r.length);\n+\t\t\t\tassertTrue(Arrays.equals(test, r));\n+\t\t\t}\n+\t\t} finally {\n+\t\t\tb.destroy();\n+\t\t}\n+\t}\n+\n+\tpublic void testLarge_SingleWrite() throws IOException {\n+\t\tfinal TemporaryBuffer b = new TemporaryBuffer();\n+\t\tfinal byte[] test = new TestRng(getName())\n+\t\t\t\t.nextBytes(TemporaryBuffer.DEFAULT_IN_CORE_LIMIT * 3);\n+\t\ttry {\n+\t\t\tb.write(test);\n+\t\t\tb.close();\n+\t\t\tassertEquals(test.length, b.length());\n+\t\t\t{\n+\t\t\t\tfinal byte[] r = b.toByteArray();\n+\t\t\t\tassertNotNull(r);\n+\t\t\t\tassertEquals(test.length, r.length);\n+\t\t\t\tassertTrue(Arrays.equals(test, r));\n+\t\t\t}\n+\t\t\t{\n+\t\t\t\tfinal ByteArrayOutputStream o = new ByteArrayOutputStream();\n+\t\t\t\tb.writeTo(o, null);\n+\t\t\t\to.close();\n+\t\t\t\tfinal byte[] r = o.toByteArray();\n+\t\t\t\tassertEquals(test.length, r.length);\n+\t\t\t\tassertTrue(Arrays.equals(test, r));\n+\t\t\t}\n+\t\t} finally {\n+\t\t\tb.destroy();\n+\t\t}\n+\t}\n+\n+\tpublic void testInCoreLimit_SwitchOnAppendByte() throws IOException {\n+\t\tfinal TemporaryBuffer b = new TemporaryBuffer();\n+\t\tfinal byte[] test = new TestRng(getName())\n+\t\t\t\t.nextBytes(TemporaryBuffer.DEFAULT_IN_CORE_LIMIT + 1);\n+\t\ttry {\n+\t\t\tb.write(test, 0, test.length - 1);\n+\t\t\tb.write(test[test.length - 1]);\n+\t\t\tb.close();\n+\t\t\tassertEquals(test.length, b.length());\n+\t\t\t{\n+\t\t\t\tfinal byte[] r = b.toByteArray();\n+\t\t\t\tassertNotNull(r);\n+\t\t\t\tassertEquals(test.length, r.length);\n+\t\t\t\tassertTrue(Arrays.equals(test, r));\n+\t\t\t}\n+\t\t\t{\n+\t\t\t\tfinal ByteArrayOutputStream o = new ByteArrayOutputStream();\n+\t\t\t\tb.writeTo(o, null);\n+\t\t\t\to.close();\n+\t\t\t\tfinal byte[] r = o.toByteArray();\n+\t\t\t\tassertEquals(test.length, r.length);\n+\t\t\t\tassertTrue(Arrays.equals(test, r));\n+\t\t\t}\n+\t\t} finally {\n+\t\t\tb.destroy();\n+\t\t}\n+\t}\n+\n+\tpublic void testInCoreLimit_SwitchBeforeAppendByte() throws IOException {\n+\t\tfinal TemporaryBuffer b = new TemporaryBuffer();\n+\t\tfinal byte[] test = new TestRng(getName())\n+\t\t\t\t.nextBytes(TemporaryBuffer.DEFAULT_IN_CORE_LIMIT * 3);\n+\t\ttry {\n+\t\t\tb.write(test, 0, test.length - 1);\n+\t\t\tb.write(test[test.length - 1]);\n+\t\t\tb.close();\n+\t\t\tassertEquals(test.length, b.length());\n+\t\t\t{\n+\t\t\t\tfinal byte[] r = b.toByteArray();\n+\t\t\t\tassertNotNull(r);\n+\t\t\t\tassertEquals(test.length, r.length);\n+\t\t\t\tassertTrue(Arrays.equals(test, r));\n+\t\t\t}\n+\t\t\t{\n+\t\t\t\tfinal ByteArrayOutputStream o = new ByteArrayOutputStream();\n+\t\t\t\tb.writeTo(o, null);\n+\t\t\t\to.close();\n+\t\t\t\tfinal byte[] r = o.toByteArray();\n+\t\t\t\tassertEquals(test.length, r.length);\n+\t\t\t\tassertTrue(Arrays.equals(test, r));\n+\t\t\t}\n+\t\t} finally {\n+\t\t\tb.destroy();\n+\t\t}\n+\t}\n+\n+\tpublic void testInCoreLimit_SwitchOnCopy() throws IOException {\n+\t\tfinal TemporaryBuffer b = new TemporaryBuffer();\n+\t\tfinal byte[] test = new TestRng(getName())\n+\t\t\t\t.nextBytes(TemporaryBuffer.DEFAULT_IN_CORE_LIMIT * 2);\n+\t\ttry {\n+\t\t\tfinal ByteArrayInputStream in = new ByteArrayInputStream(test,\n+\t\t\t\t\tTemporaryBuffer.DEFAULT_IN_CORE_LIMIT, test.length\n+\t\t\t\t\t\t\t- TemporaryBuffer.DEFAULT_IN_CORE_LIMIT);\n+\t\t\tb.write(test, 0, TemporaryBuffer.DEFAULT_IN_CORE_LIMIT);\n+\t\t\tb.copy(in);\n+\t\t\tb.close();\n+\t\t\tassertEquals(test.length, b.length());\n+\t\t\t{\n+\t\t\t\tfinal byte[] r = b.toByteArray();\n+\t\t\t\tassertNotNull(r);\n+\t\t\t\tassertEquals(test.length, r.length);\n+\t\t\t\tassertTrue(Arrays.equals(test, r));\n+\t\t\t}\n+\t\t\t{\n+\t\t\t\tfinal ByteArrayOutputStream o = new ByteArrayOutputStream();\n+\t\t\t\tb.writeTo(o, null);\n+\t\t\t\to.close();\n+\t\t\t\tfinal byte[] r = o.toByteArray();\n+\t\t\t\tassertEquals(test.length, r.length);\n+\t\t\t\tassertTrue(Arrays.equals(test, r));\n+\t\t\t}\n+\t\t} finally {\n+\t\t\tb.destroy();\n+\t\t}\n+\t}\n+\n+\tpublic void testDestroyWhileOpen() throws IOException {\n+\t\tfinal TemporaryBuffer b = new TemporaryBuffer();\n+\t\ttry {\n+\t\t\tb.write(new TestRng(getName())\n+\t\t\t\t\t.nextBytes(TemporaryBuffer.DEFAULT_IN_CORE_LIMIT * 2));\n+\t\t} finally {\n+\t\t\tb.destroy();\n+\t\t}\n+\t}\n+\n+\tpublic void testRandomWrites() throws IOException {\n+\t\tfinal TemporaryBuffer b = new TemporaryBuffer();\n+\t\tfinal TestRng rng = new TestRng(getName());\n+\t\tfinal int max = TemporaryBuffer.DEFAULT_IN_CORE_LIMIT * 2;\n+\t\tfinal byte[] expect = new byte[max];\n+\t\ttry {\n+\t\t\tint written = 0;\n+\t\t\tboolean onebyte = true;\n+\t\t\twhile (written < max) {\n+\t\t\t\tif (onebyte) {\n+\t\t\t\t\tfinal byte v = (byte) rng.nextInt();\n+\t\t\t\t\tb.write(v);\n+\t\t\t\t\texpect[written++] = v;\n+\t\t\t\t} else {\n+\t\t\t\t\tfinal int len = Math\n+\t\t\t\t\t\t\t.min(rng.nextInt() & 127, max - written);\n+\t\t\t\t\tfinal byte[] tmp = rng.nextBytes(len);\n+\t\t\t\t\tb.write(tmp, 0, len);\n+\t\t\t\t\tSystem.arraycopy(tmp, 0, expect, written, len);\n+\t\t\t\t\twritten += len;\n+\t\t\t\t}\n+\t\t\t\tonebyte = !onebyte;\n+\t\t\t}\n+\t\t\tassertEquals(expect.length, written);\n+\t\t\tb.close();\n+\n+\t\t\tassertEquals(expect.length, b.length());\n+\t\t\t{\n+\t\t\t\tfinal byte[] r = b.toByteArray();\n+\t\t\t\tassertNotNull(r);\n+\t\t\t\tassertEquals(expect.length, r.length);\n+\t\t\t\tassertTrue(Arrays.equals(expect, r));\n+\t\t\t}\n+\t\t\t{\n+\t\t\t\tfinal ByteArrayOutputStream o = new ByteArrayOutputStream();\n+\t\t\t\tb.writeTo(o, null);\n+\t\t\t\to.close();\n+\t\t\t\tfinal byte[] r = o.toByteArray();\n+\t\t\t\tassertEquals(expect.length, r.length);\n+\t\t\t\tassertTrue(Arrays.equals(expect, r));\n+\t\t\t}\n+\t\t} finally {\n+\t\t\tb.destroy();\n+\t\t}\n+\t}\n+\n+}\ndiff --git a/org.spearce.jgit.test/tst/org/spearce/jgit/util/TestRng.java b/org.spearce.jgit.test/tst/org/spearce/jgit/util/TestRng.java\nnew file mode 100644\nindex 0000000..d74a534\n--- /dev/null\n+++ b/org.spearce.jgit.test/tst/org/spearce/jgit/util/TestRng.java\n@@ -0,0 +1,61 @@\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.util;\n+\n+/** Toy RNG to ensure we get predictable numbers during unit tests. */\n+public class TestRng {\n+\tprivate int next;\n+\n+\tpublic TestRng(final String seed) {\n+\t\tnext = 0;\n+\t\tfor (int i = 0; i < seed.length(); i++)\n+\t\t\tnext = next * 11 + seed.charAt(i);\n+\t}\n+\n+\tpublic byte[] nextBytes(final int cnt) {\n+\t\tfinal byte[] r = new byte[cnt];\n+\t\tfor (int i = 0; i < cnt; i++)\n+\t\t\tr[i] = (byte) nextInt();\n+\t\treturn r;\n+\t}\n+\n+\tpublic int nextInt() {\n+\t\tnext = next * 1103515245 + 12345;\n+\t\treturn next;\n+\t}\n+}\ndiff --git a/org.spearce.jgit/src/org/spearce/jgit/util/TemporaryBuffer.java b/org.spearce.jgit/src/org/spearce/jgit/util/TemporaryBuffer.java\nindex 8f91246..6267fb5 100644\n--- a/org.spearce.jgit/src/org/spearce/jgit/util/TemporaryBuffer.java\n+++ b/org.spearce.jgit/src/org/spearce/jgit/util/TemporaryBuffer.java\n@@ -60,7 +60,7 @@\n  * after this stream has been properly closed by {@link #close()}.\n  */\n public class TemporaryBuffer extends OutputStream {\n-\tprivate static final int DEFAULT_IN_CORE_LIMIT = 1024 * 1024;\n+\tstatic final int DEFAULT_IN_CORE_LIMIT = 1024 * 1024;\n \n \t/** Chain of data, if we are still completely in-core; otherwise null. */\n \tprivate ArrayList<Block> blocks;\n@@ -297,7 +297,7 @@ public void destroy() {\n \t\t}\n \t}\n \n-\tprivate static class Block {\n+\tstatic class Block {\n \t\tstatic final int SZ = 8 * 1024;\n \n \t\tfinal byte[] buffer = new byte[SZ];\n-- \n1.6.1.rc2.306.ge5d5e\n"},{"id":"97662","messageId":"1229049981-14152-4-git-send-email-spearce@spearce.org","threadId":"16690","inReplyTo":"1229049981-14152-3-git-send-email-spearce@spearce.org","subject":"[JGIT PATCH 03/15] Add IntList as a more efficient representation of List<Integer>","fromName":"Shawn O. Pearce","fromEmail":"spearce@spearce.org","sentAt":"2008-12-12T02:46:09Z","receivedAt":"2008-12-12T02:46:09Z","isPatch":true,"sender":{"key":"spearce@spearce.org","avatar":"https://avatars.githubusercontent.com/u/34844?v=4"},"body":"Java's generic container types can only handle reference values,\nwhich means making a List of ints requires boxing each value. A\nboxed int typically requires at least 12 bytes more space per\nvalue over an unboxed int, as it has an additional object header\nand a cell to hold the object reference.\n\nIntList uses an int[] internally to hold the values, rather than\nan Object[] like List<Integer> would use.\n\nWe don't conform to the List (or even Collection) APIs as doing\nso would require that we box return values, which is even less\nefficient than just using ArrayList<Integer>, because we would\nbe boxing every return value each time it is accessed.  Instead\nwe use an API that smells the same, so there is some finger feel\nto using the class.\n\nSigned-off-by: Shawn O. Pearce <spearce@spearce.org>\n---\n .../tst/org/spearce/jgit/util/IntListTest.java     |  132 ++++++++++++++++++++\n .../src/org/spearce/jgit/util/IntList.java         |  113 +++++++++++++++++\n 2 files changed, 245 insertions(+), 0 deletions(-)\n create mode 100644 org.spearce.jgit.test/tst/org/spearce/jgit/util/IntListTest.java\n create mode 100644 org.spearce.jgit/src/org/spearce/jgit/util/IntList.java\n\ndiff --git a/org.spearce.jgit.test/tst/org/spearce/jgit/util/IntListTest.java b/org.spearce.jgit.test/tst/org/spearce/jgit/util/IntListTest.java\nnew file mode 100644\nindex 0000000..f943075\n--- /dev/null\n+++ b/org.spearce.jgit.test/tst/org/spearce/jgit/util/IntListTest.java\n@@ -0,0 +1,132 @@\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.util;\n+\n+import junit.framework.TestCase;\n+\n+public class IntListTest extends TestCase {\n+\tpublic void testEmpty_DefaultCapacity() {\n+\t\tfinal IntList i = new IntList();\n+\t\tassertEquals(0, i.size());\n+\t\ttry {\n+\t\t\ti.get(0);\n+\t\t\tfail(\"Accepted 0 index on empty list\");\n+\t\t} catch (ArrayIndexOutOfBoundsException e) {\n+\t\t\tassertTrue(true);\n+\t\t}\n+\t}\n+\n+\tpublic void testEmpty_SpecificCapacity() {\n+\t\tfinal IntList i = new IntList(5);\n+\t\tassertEquals(0, i.size());\n+\t\ttry {\n+\t\t\ti.get(0);\n+\t\t\tfail(\"Accepted 0 index on empty list\");\n+\t\t} catch (ArrayIndexOutOfBoundsException e) {\n+\t\t\tassertTrue(true);\n+\t\t}\n+\t}\n+\n+\tpublic void testAdd_SmallGroup() {\n+\t\tfinal IntList i = new IntList();\n+\t\tfinal int n = 5;\n+\t\tfor (int v = 0; v < n; v++)\n+\t\t\ti.add(10 + v);\n+\t\tassertEquals(n, i.size());\n+\n+\t\tfor (int v = 0; v < n; v++)\n+\t\t\tassertEquals(10 + v, i.get(v));\n+\t\ttry {\n+\t\t\ti.get(n);\n+\t\t\tfail(\"Accepted out of bound index on list\");\n+\t\t} catch (ArrayIndexOutOfBoundsException e) {\n+\t\t\tassertTrue(true);\n+\t\t}\n+\t}\n+\n+\tpublic void testAdd_ZeroCapacity() {\n+\t\tfinal IntList i = new IntList(0);\n+\t\tassertEquals(0, i.size());\n+\t\ti.add(1);\n+\t\tassertEquals(1, i.get(0));\n+\t}\n+\n+\tpublic void testAdd_LargeGroup() {\n+\t\tfinal IntList i = new IntList();\n+\t\tfinal int n = 500;\n+\t\tfor (int v = 0; v < n; v++)\n+\t\t\ti.add(10 + v);\n+\t\tassertEquals(n, i.size());\n+\n+\t\tfor (int v = 0; v < n; v++)\n+\t\t\tassertEquals(10 + v, i.get(v));\n+\t\ttry {\n+\t\t\ti.get(n);\n+\t\t\tfail(\"Accepted out of bound index on list\");\n+\t\t} catch (ArrayIndexOutOfBoundsException e) {\n+\t\t\tassertTrue(true);\n+\t\t}\n+\t}\n+\n+\tpublic void testClear() {\n+\t\tfinal IntList i = new IntList();\n+\t\tfinal int n = 5;\n+\t\tfor (int v = 0; v < n; v++)\n+\t\t\ti.add(10 + v);\n+\t\tassertEquals(n, i.size());\n+\n+\t\ti.clear();\n+\t\tassertEquals(0, i.size());\n+\t\ttry {\n+\t\t\ti.get(0);\n+\t\t\tfail(\"Accepted 0 index on empty list\");\n+\t\t} catch (ArrayIndexOutOfBoundsException e) {\n+\t\t\tassertTrue(true);\n+\t\t}\n+\t}\n+\n+\tpublic void testToString() {\n+\t\tfinal IntList i = new IntList();\n+\t\ti.add(1);\n+\t\tassertEquals(\"[1]\", i.toString());\n+\t\ti.add(13);\n+\t\ti.add(5);\n+\t\tassertEquals(\"[1, 13, 5]\", i.toString());\n+\t}\n+\n+}\ndiff --git a/org.spearce.jgit/src/org/spearce/jgit/util/IntList.java b/org.spearce.jgit/src/org/spearce/jgit/util/IntList.java\nnew file mode 100644\nindex 0000000..1445f88\n--- /dev/null\n+++ b/org.spearce.jgit/src/org/spearce/jgit/util/IntList.java\n@@ -0,0 +1,113 @@\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.util;\n+\n+/** A more efficient List<Integer> using a primitive integer array. */\n+public class IntList {\n+\tprivate int[] entries;\n+\n+\tprivate int count;\n+\n+\t/** Create an empty list with a default capacity. */\n+\tpublic IntList() {\n+\t\tthis(10);\n+\t}\n+\n+\t/**\n+\t * Create an empty list with the specified capacity.\n+\t * \n+\t * @param capacity\n+\t *            number of entries the list can initially hold.\n+\t */\n+\tpublic IntList(final int capacity) {\n+\t\tentries = new int[capacity];\n+\t}\n+\n+\t/** @return number of entries in this list */\n+\tpublic int size() {\n+\t\treturn count;\n+\t}\n+\n+\t/**\n+\t * @param i\n+\t *            index to read, must be in the range [0, {@link #size()}).\n+\t * @return the number at the specified index\n+\t * @throws ArrayIndexOutOfBoundsException\n+\t *             the index outside the valid range\n+\t */\n+\tpublic int get(final int i) {\n+\t\tif (count <= i)\n+\t\t\tthrow new ArrayIndexOutOfBoundsException(i);\n+\t\treturn entries[i];\n+\t}\n+\n+\t/** Empty this list */\n+\tpublic void clear() {\n+\t\tcount = 0;\n+\t}\n+\n+\t/**\n+\t * Add an entry to the end of the list.\n+\t * \n+\t * @param n\n+\t *            the number to add.\n+\t */\n+\tpublic void add(final int n) {\n+\t\tif (count == entries.length)\n+\t\t\tgrow();\n+\t\tentries[count++] = n;\n+\t}\n+\n+\tprivate void grow() {\n+\t\tfinal int[] n = new int[(entries.length + 16) * 3 / 2];\n+\t\tSystem.arraycopy(entries, 0, n, 0, count);\n+\t\tentries = n;\n+\t}\n+\n+\tpublic String toString() {\n+\t\tfinal StringBuilder r = new StringBuilder();\n+\t\tr.append('[');\n+\t\tfor (int i = 0; i < count; i++) {\n+\t\t\tif (i > 0)\n+\t\t\t\tr.append(\", \");\n+\t\t\tr.append(entries[i]);\n+\t\t}\n+\t\tr.append(']');\n+\t\treturn r.toString();\n+\t}\n+}\n-- \n1.6.1.rc2.306.ge5d5e\n"},{"id":"97664","messageId":"1229049981-14152-5-git-send-email-spearce@spearce.org","threadId":"16690","inReplyTo":"1229049981-14152-4-git-send-email-spearce@spearce.org","subject":"[JGIT PATCH 04/15] Add lineMap computer to RawParseUtils to index locations of line starts","fromName":"Shawn O. Pearce","fromEmail":"spearce@spearce.org","sentAt":"2008-12-12T02:46:10Z","receivedAt":"2008-12-12T02:46:10Z","isPatch":true,"sender":{"key":"spearce@spearce.org","avatar":"https://avatars.githubusercontent.com/u/34844?v=4"},"body":"Some algorithms like a diff or patch require efficient lookup of lines\nwithin a source file, using a 1 based line number.  lineMap produces a\nlist of offsets within the file where each line starts, providing O(1)\nlookup time to locate those positions.\n\nSigned-off-by: Shawn O. Pearce <spearce@spearce.org>\n---\n .../tst/org/spearce/jgit/util/IntListTest.java     |   24 ++++++\n .../jgit/util/RawParseUtils_LineMapTest.java       |   88 ++++++++++++++++++++\n .../src/org/spearce/jgit/util/IntList.java         |   15 ++++\n .../src/org/spearce/jgit/util/RawParseUtils.java   |   31 +++++++\n 4 files changed, 158 insertions(+), 0 deletions(-)\n create mode 100644 org.spearce.jgit.test/tst/org/spearce/jgit/util/RawParseUtils_LineMapTest.java\n\ndiff --git a/org.spearce.jgit.test/tst/org/spearce/jgit/util/IntListTest.java b/org.spearce.jgit.test/tst/org/spearce/jgit/util/IntListTest.java\nindex f943075..c470d55 100644\n--- a/org.spearce.jgit.test/tst/org/spearce/jgit/util/IntListTest.java\n+++ b/org.spearce.jgit.test/tst/org/spearce/jgit/util/IntListTest.java\n@@ -103,6 +103,30 @@ public void testAdd_LargeGroup() {\n \t\t}\n \t}\n \n+\tpublic void testFillTo0() {\n+\t\tfinal IntList i = new IntList();\n+\t\ti.fillTo(0, Integer.MIN_VALUE);\n+\t\tassertEquals(0, i.size());\n+\t}\n+\n+\tpublic void testFillTo1() {\n+\t\tfinal IntList i = new IntList();\n+\t\ti.fillTo(1, Integer.MIN_VALUE);\n+\t\tassertEquals(1, i.size());\n+\t\ti.add(0);\n+\t\tassertEquals(Integer.MIN_VALUE, i.get(0));\n+\t\tassertEquals(0, i.get(1));\n+\t}\n+\n+\tpublic void testFillTo100() {\n+\t\tfinal IntList i = new IntList();\n+\t\ti.fillTo(100, Integer.MIN_VALUE);\n+\t\tassertEquals(100, i.size());\n+\t\ti.add(3);\n+\t\tassertEquals(Integer.MIN_VALUE, i.get(99));\n+\t\tassertEquals(3, i.get(100));\n+\t}\n+\n \tpublic void testClear() {\n \t\tfinal IntList i = new IntList();\n \t\tfinal int n = 5;\ndiff --git a/org.spearce.jgit.test/tst/org/spearce/jgit/util/RawParseUtils_LineMapTest.java b/org.spearce.jgit.test/tst/org/spearce/jgit/util/RawParseUtils_LineMapTest.java\nnew file mode 100644\nindex 0000000..3f562a4\n--- /dev/null\n+++ b/org.spearce.jgit.test/tst/org/spearce/jgit/util/RawParseUtils_LineMapTest.java\n@@ -0,0 +1,88 @@\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.util;\n+\n+import java.io.UnsupportedEncodingException;\n+\n+import junit.framework.TestCase;\n+\n+public class RawParseUtils_LineMapTest extends TestCase {\n+\tpublic void testEmpty() {\n+\t\tfinal IntList map = RawParseUtils.lineMap(new byte[] {}, 0, 0);\n+\t\tassertNotNull(map);\n+\t\tassertEquals(1, map.size());\n+\t\tassertEquals(Integer.MIN_VALUE, map.get(0));\n+\t}\n+\n+\tpublic void testOneBlankLine() {\n+\t\tfinal IntList map = RawParseUtils.lineMap(new byte[] { '\\n' }, 0, 1);\n+\t\tassertEquals(2, map.size());\n+\t\tassertEquals(Integer.MIN_VALUE, map.get(0));\n+\t\tassertEquals(0, map.get(1));\n+\t}\n+\n+\tpublic void testTwoLineFooBar() throws UnsupportedEncodingException {\n+\t\tfinal byte[] buf = \"foo\\nbar\\n\".getBytes(\"ISO-8859-1\");\n+\t\tfinal IntList map = RawParseUtils.lineMap(buf, 0, buf.length);\n+\t\tassertEquals(3, map.size());\n+\t\tassertEquals(Integer.MIN_VALUE, map.get(0));\n+\t\tassertEquals(0, map.get(1));\n+\t\tassertEquals(4, map.get(2));\n+\t}\n+\n+\tpublic void testTwoLineNoLF() throws UnsupportedEncodingException {\n+\t\tfinal byte[] buf = \"foo\\nbar\".getBytes(\"ISO-8859-1\");\n+\t\tfinal IntList map = RawParseUtils.lineMap(buf, 0, buf.length);\n+\t\tassertEquals(3, map.size());\n+\t\tassertEquals(Integer.MIN_VALUE, map.get(0));\n+\t\tassertEquals(0, map.get(1));\n+\t\tassertEquals(4, map.get(2));\n+\t}\n+\n+\tpublic void testFourLineBlanks() throws UnsupportedEncodingException {\n+\t\tfinal byte[] buf = \"foo\\n\\n\\nbar\\n\".getBytes(\"ISO-8859-1\");\n+\t\tfinal IntList map = RawParseUtils.lineMap(buf, 0, buf.length);\n+\t\tassertEquals(5, map.size());\n+\t\tassertEquals(Integer.MIN_VALUE, map.get(0));\n+\t\tassertEquals(0, map.get(1));\n+\t\tassertEquals(4, map.get(2));\n+\t\tassertEquals(5, map.get(3));\n+\t\tassertEquals(6, map.get(4));\n+\t}\n+\n+}\ndiff --git a/org.spearce.jgit/src/org/spearce/jgit/util/IntList.java b/org.spearce.jgit/src/org/spearce/jgit/util/IntList.java\nindex 1445f88..2824074 100644\n--- a/org.spearce.jgit/src/org/spearce/jgit/util/IntList.java\n+++ b/org.spearce.jgit/src/org/spearce/jgit/util/IntList.java\n@@ -93,6 +93,21 @@ public void add(final int n) {\n \t\tentries[count++] = n;\n \t}\n \n+\t/**\n+\t * Pad the list with entries.\n+\t * \n+\t * @param toIndex\n+\t *            index position to stop filling at. 0 inserts no filler. 1\n+\t *            ensures the list has a size of 1, adding <code>val</code> if\n+\t *            the list is currently empty.\n+\t * @param val\n+\t *            value to insert into padded positions.\n+\t */\n+\tpublic void fillTo(int toIndex, final int val) {\n+\t\twhile (count < toIndex)\n+\t\t\tadd(val);\n+\t}\n+\n \tprivate void grow() {\n \t\tfinal int[] n = new int[(entries.length + 16) * 3 / 2];\n \t\tSystem.arraycopy(entries, 0, n, 0, count);\ndiff --git a/org.spearce.jgit/src/org/spearce/jgit/util/RawParseUtils.java b/org.spearce.jgit/src/org/spearce/jgit/util/RawParseUtils.java\nindex 8896d38..6143188 100644\n--- a/org.spearce.jgit/src/org/spearce/jgit/util/RawParseUtils.java\n+++ b/org.spearce.jgit/src/org/spearce/jgit/util/RawParseUtils.java\n@@ -266,6 +266,37 @@ public static final int nextLF(final byte[] b, int ptr, final char chrA) {\n \t}\n \n \t/**\n+\t * Index the region between <code>[ptr, end)</code> to find line starts.\n+\t * <p>\n+\t * The returned list is 1 indexed. Index 0 contains\n+\t * {@link Integer#MIN_VALUE} to pad the list out.\n+\t * <p>\n+\t * Using a 1 indexed list means that line numbers can be directly accessed\n+\t * from the list, so <code>list.get(1)</code> (aka get line 1) returns\n+\t * <code>ptr</code>.\n+\t * \n+\t * @param buf\n+\t *            buffer to scan.\n+\t * @param ptr\n+\t *            position within the buffer corresponding to the first byte of\n+\t *            line 1.\n+\t * @param end\n+\t *            1 past the end of the content within <code>buf</code>.\n+\t * @return a line map indexing the start position of each line.\n+\t */\n+\tpublic static final IntList lineMap(final byte[] buf, int ptr, int end) {\n+\t\t// Experimentally derived from multiple source repositories\n+\t\t// the average number of bytes/line is 36. Its a rough guess\n+\t\t// to initially size our map close to the target.\n+\t\t//\n+\t\tfinal IntList map = new IntList((end - ptr) / 36);\n+\t\tmap.fillTo(1, Integer.MIN_VALUE);\n+\t\tfor (; ptr < end; ptr = nextLF(buf, ptr))\n+\t\t\tmap.add(ptr);\n+\t\treturn map;\n+\t}\n+\n+\t/**\n \t * Locate the \"author \" header line data.\n \t * \n \t * @param b\n-- \n1.6.1.rc2.306.ge5d5e\n"},{"id":"97675","messageId":"1229049981-14152-6-git-send-email-spearce@spearce.org","threadId":"16690","inReplyTo":"1229049981-14152-5-git-send-email-spearce@spearce.org","subject":"[JGIT PATCH 05/15] Define FileHeader.PatchType to report the style of patch used","fromName":"Shawn O. Pearce","fromEmail":"spearce@spearce.org","sentAt":"2008-12-12T02:46:11Z","receivedAt":"2008-12-12T02:46:11Z","isPatch":true,"sender":{"key":"spearce@spearce.org","avatar":"https://avatars.githubusercontent.com/u/34844?v=4"},"body":"Patches in a Git world come in at least three flavors:\n\n  * Traditional unified patch\n  * Binary patch with no data (\"Binary files a/a and b/a differ\")\n  * Git binary patch with forward and reverse deltas\n\nThe PatchType indicates which of these flavors a given FileHeader\nis looking at.  Right now we assume UNIFIED by default as that is\nthe most common form used.\n\nSigned-off-by: Shawn O. Pearce <spearce@spearce.org>\n---\n .../tst/org/spearce/jgit/patch/FileHeaderTest.java |    6 +++++\n .../tst/org/spearce/jgit/patch/PatchTest.java      |    4 +++\n .../src/org/spearce/jgit/patch/FileHeader.java     |   21 ++++++++++++++++++++\n 3 files changed, 31 insertions(+), 0 deletions(-)\n\ndiff --git a/org.spearce.jgit.test/tst/org/spearce/jgit/patch/FileHeaderTest.java b/org.spearce.jgit.test/tst/org/spearce/jgit/patch/FileHeaderTest.java\nindex 4094a5c..d8696a9 100644\n--- a/org.spearce.jgit.test/tst/org/spearce/jgit/patch/FileHeaderTest.java\n+++ b/org.spearce.jgit.test/tst/org/spearce/jgit/patch/FileHeaderTest.java\n@@ -134,6 +134,7 @@ public void testParseUnicodeName_NewFile() {\n \t\tassertEquals(\"\\u00c5ngstr\\u00f6m\", fh.getNewName());\n \n \t\tassertSame(FileHeader.ChangeType.ADD, fh.getChangeType());\n+\t\tassertSame(FileHeader.PatchType.UNIFIED, fh.getPatchType());\n \n \t\tassertNull(fh.getOldMode());\n \t\tassertSame(FileMode.REGULAR_FILE, fh.getNewMode());\n@@ -157,6 +158,7 @@ public void testParseUnicodeName_DeleteFile() {\n \t\tassertSame(FileHeader.DEV_NULL, fh.getNewName());\n \n \t\tassertSame(FileHeader.ChangeType.DELETE, fh.getChangeType());\n+\t\tassertSame(FileHeader.PatchType.UNIFIED, fh.getPatchType());\n \n \t\tassertSame(FileMode.REGULAR_FILE, fh.getOldMode());\n \t\tassertNull(fh.getNewMode());\n@@ -174,6 +176,7 @@ public void testParseModeChange() {\n \t\tassertEquals(\"a b\", fh.getNewName());\n \n \t\tassertSame(FileHeader.ChangeType.MODIFY, fh.getChangeType());\n+\t\tassertSame(FileHeader.PatchType.UNIFIED, fh.getPatchType());\n \n \t\tassertNull(fh.getOldId());\n \t\tassertNull(fh.getNewId());\n@@ -200,6 +203,7 @@ public void testParseRename100_NewStyle() {\n \t\tassertEquals(\" c/\\u00c5ngstr\\u00f6m\", fh.getNewName());\n \n \t\tassertSame(FileHeader.ChangeType.RENAME, fh.getChangeType());\n+\t\tassertSame(FileHeader.PatchType.UNIFIED, fh.getPatchType());\n \n \t\tassertNull(fh.getOldId());\n \t\tassertNull(fh.getNewId());\n@@ -227,6 +231,7 @@ public void testParseRename100_OldStyle() {\n \t\tassertEquals(\" c/\\u00c5ngstr\\u00f6m\", fh.getNewName());\n \n \t\tassertSame(FileHeader.ChangeType.RENAME, fh.getChangeType());\n+\t\tassertSame(FileHeader.PatchType.UNIFIED, fh.getPatchType());\n \n \t\tassertNull(fh.getOldId());\n \t\tassertNull(fh.getNewId());\n@@ -254,6 +259,7 @@ public void testParseCopy100() {\n \t\tassertEquals(\" c/\\u00c5ngstr\\u00f6m\", fh.getNewName());\n \n \t\tassertSame(FileHeader.ChangeType.COPY, fh.getChangeType());\n+\t\tassertSame(FileHeader.PatchType.UNIFIED, fh.getPatchType());\n \n \t\tassertNull(fh.getOldId());\n \t\tassertNull(fh.getNewId());\ndiff --git a/org.spearce.jgit.test/tst/org/spearce/jgit/patch/PatchTest.java b/org.spearce.jgit.test/tst/org/spearce/jgit/patch/PatchTest.java\nindex 8ddaadc..3afe0a1 100644\n--- a/org.spearce.jgit.test/tst/org/spearce/jgit/patch/PatchTest.java\n+++ b/org.spearce.jgit.test/tst/org/spearce/jgit/patch/PatchTest.java\n@@ -70,6 +70,8 @@ assertEquals(\n \n \t\tassertEquals(\"da7e704\", fRepositoryConfigTest.getOldId().name());\n \t\tassertEquals(\"34ce04a\", fRepositoryConfigTest.getNewId().name());\n+\t\tassertSame(FileHeader.PatchType.UNIFIED, fRepositoryConfigTest\n+\t\t\t\t.getPatchType());\n \t\tassertSame(FileMode.REGULAR_FILE, fRepositoryConfigTest.getOldMode());\n \t\tassertSame(FileMode.REGULAR_FILE, fRepositoryConfigTest.getNewMode());\n \t\tassertEquals(1, fRepositoryConfigTest.getHunks().size());\n@@ -90,6 +92,8 @@ assertEquals(\n \n \t\tassertEquals(\"45c2f8a\", fRepositoryConfig.getOldId().name());\n \t\tassertEquals(\"3291bba\", fRepositoryConfig.getNewId().name());\n+\t\tassertSame(FileHeader.PatchType.UNIFIED, fRepositoryConfig\n+\t\t\t\t.getPatchType());\n \t\tassertSame(FileMode.REGULAR_FILE, fRepositoryConfig.getOldMode());\n \t\tassertSame(FileMode.REGULAR_FILE, fRepositoryConfig.getNewMode());\n \t\tassertEquals(3, fRepositoryConfig.getHunks().size());\ndiff --git a/org.spearce.jgit/src/org/spearce/jgit/patch/FileHeader.java b/org.spearce.jgit/src/org/spearce/jgit/patch/FileHeader.java\nindex b49cb7b..5ff7cb7 100644\n--- a/org.spearce.jgit/src/org/spearce/jgit/patch/FileHeader.java\n+++ b/org.spearce.jgit/src/org/spearce/jgit/patch/FileHeader.java\n@@ -107,6 +107,18 @@\n \t\tCOPY;\n \t}\n \n+\t/** Type of patch used by this file. */\n+\tpublic static enum PatchType {\n+\t\t/** A traditional unified diff style patch of a text file. */\n+\t\tUNIFIED,\n+\n+\t\t/** An empty patch with a message \"Binary files ... differ\" */\n+\t\tBINARY,\n+\n+\t\t/** A Git binary patch, holding pre and post image deltas */\n+\t\tGIT_BINARY;\n+\t}\n+\n \t/** Buffer holding the patch data for this file. */\n \tfinal byte[] buf;\n \n@@ -140,6 +152,9 @@\n \t/** ObjectId listed on the index line for the new (post-image) */\n \tprivate AbbreviatedObjectId newId;\n \n+\t/** Type of patch used to modify this file */\n+\tprivate PatchType patchType;\n+\n \t/** The hunks of this file */\n \tprivate List<HunkHeader> hunks;\n \n@@ -147,6 +162,7 @@ FileHeader(final byte[] b, final int offset) {\n \t\tbuf = b;\n \t\tstartOffset = offset;\n \t\tchangeType = ChangeType.MODIFY; // unless otherwise designated\n+\t\tpatchType = PatchType.UNIFIED;\n \t}\n \n \t/**\n@@ -229,6 +245,11 @@ public AbbreviatedObjectId getNewId() {\n \t\treturn newId;\n \t}\n \n+\t/** @return style of patch used to modify this file */\n+\tpublic PatchType getPatchType() {\n+\t\treturn patchType;\n+\t}\n+\n \t/** @return hunks altering this file; in order of appearance in patch */\n \tpublic List<HunkHeader> getHunks() {\n \t\tif (hunks == null)\n-- \n1.6.1.rc2.306.ge5d5e\n"},{"id":"97673","messageId":"1229049981-14152-7-git-send-email-spearce@spearce.org","threadId":"16690","inReplyTo":"1229049981-14152-6-git-send-email-spearce@spearce.org","subject":"[JGIT PATCH 06/15] Test for non-git binary files and mark them as PatchType.BINARY","fromName":"Shawn O. Pearce","fromEmail":"spearce@spearce.org","sentAt":"2008-12-12T02:46:12Z","receivedAt":"2008-12-12T02:46:12Z","isPatch":true,"sender":{"key":"spearce@spearce.org","avatar":"https://avatars.githubusercontent.com/u/34844?v=4"},"body":"By marking non-git binary patches as binary callers can examine\nthe patch type to determine if there is meaningful content on\nthis particular file.\n\nSigned-off-by: Shawn O. Pearce <spearce@spearce.org>\n---\n .../tst/org/spearce/jgit/patch/PatchTest.java      |   26 ++++++\n .../spearce/jgit/patch/testParse_NoBinary.patch    |   83 ++++++++++++++++++++\n .../src/org/spearce/jgit/patch/FileHeader.java     |    2 +-\n .../src/org/spearce/jgit/patch/Patch.java          |   26 ++++++-\n 4 files changed, 135 insertions(+), 2 deletions(-)\n create mode 100644 org.spearce.jgit.test/tst/org/spearce/jgit/patch/testParse_NoBinary.patch\n\ndiff --git a/org.spearce.jgit.test/tst/org/spearce/jgit/patch/PatchTest.java b/org.spearce.jgit.test/tst/org/spearce/jgit/patch/PatchTest.java\nindex 3afe0a1..833bf5d 100644\n--- a/org.spearce.jgit.test/tst/org/spearce/jgit/patch/PatchTest.java\n+++ b/org.spearce.jgit.test/tst/org/spearce/jgit/patch/PatchTest.java\n@@ -141,6 +141,32 @@ assertSame(FileHeader.PatchType.UNIFIED, fRepositoryConfig\n \t\t}\n \t}\n \n+\tpublic void testParse_NoBinary() throws IOException {\n+\t\tfinal Patch p = parseTestPatchFile();\n+\t\tassertEquals(5, p.getFiles().size());\n+\n+\t\tfor (int i = 0; i < 4; i++) {\n+\t\t\tfinal FileHeader fh = p.getFiles().get(i);\n+\t\t\tassertSame(FileHeader.ChangeType.ADD, fh.getChangeType());\n+\t\t\tassertNotNull(fh.getOldId());\n+\t\t\tassertNotNull(fh.getNewId());\n+\t\t\tassertEquals(\"0000000\", fh.getOldId().name());\n+\t\t\tassertSame(FileMode.REGULAR_FILE, fh.getNewMode());\n+\t\t\tassertTrue(fh.getNewName().startsWith(\n+\t\t\t\t\t\"org.spearce.egit.ui/icons/toolbar/\"));\n+\t\t\tassertSame(FileHeader.PatchType.BINARY, fh.getPatchType());\n+\t\t\tassertTrue(fh.getHunks().isEmpty());\n+\t\t}\n+\n+\t\tfinal FileHeader fh = p.getFiles().get(4);\n+\t\tassertEquals(\"org.spearce.egit.ui/plugin.xml\", fh.getNewName());\n+\t\tassertSame(FileHeader.ChangeType.MODIFY, fh.getChangeType());\n+\t\tassertSame(FileHeader.PatchType.UNIFIED, fh.getPatchType());\n+\t\tassertEquals(\"ee8a5a0\", fh.getNewId().name());\n+\t\tassertEquals(1, fh.getHunks().size());\n+\t\tassertEquals(272, fh.getHunks().get(0).getOldStartLine());\n+\t}\n+\n \tprivate Patch parseTestPatchFile() throws IOException {\n \t\tfinal String patchFile = getName() + \".patch\";\n \t\tfinal InputStream in = getClass().getResourceAsStream(patchFile);\ndiff --git a/org.spearce.jgit.test/tst/org/spearce/jgit/patch/testParse_NoBinary.patch b/org.spearce.jgit.test/tst/org/spearce/jgit/patch/testParse_NoBinary.patch\nnew file mode 100644\nindex 0000000..684b13c\n--- /dev/null\n+++ b/org.spearce.jgit.test/tst/org/spearce/jgit/patch/testParse_NoBinary.patch\n@@ -0,0 +1,83 @@\n+From 8363f12135a7d0ff0b5fea7d5a35d294c0479518 Mon Sep 17 00:00:00 2001\n+From: Robin Rosenberg <robin.rosenberg.lists@dewire.com>\n+Date: Tue, 23 Sep 2008 22:19:19 +0200\n+Subject: [PATCH] Push and fetch icons for the toolbar\n+\n+Signed-off-by: Robin Rosenberg <robin.rosenberg@dewire.com>\n+Signed-off-by: Shawn O. Pearce <spearce@spearce.org>\n+---\n+ org.spearce.egit.ui/icons/toolbar/fetchd.png |  Bin 0 -> 359 bytes\n+ org.spearce.egit.ui/icons/toolbar/fetche.png |  Bin 0 -> 393 bytes\n+ org.spearce.egit.ui/icons/toolbar/pushd.png  |  Bin 0 -> 372 bytes\n+ org.spearce.egit.ui/icons/toolbar/pushe.png  |  Bin 0 -> 404 bytes\n+ org.spearce.egit.ui/plugin.xml               |   32 ++++++++++++++-----------\n+ 5 files changed, 18 insertions(+), 14 deletions(-)\n+ create mode 100644 org.spearce.egit.ui/icons/toolbar/fetchd.png\n+ create mode 100644 org.spearce.egit.ui/icons/toolbar/fetche.png\n+ create mode 100644 org.spearce.egit.ui/icons/toolbar/pushd.png\n+ create mode 100644 org.spearce.egit.ui/icons/toolbar/pushe.png\n+\n+diff --git a/org.spearce.egit.ui/icons/toolbar/fetchd.png b/org.spearce.egit.ui/icons/toolbar/fetchd.png\n+new file mode 100644\n+index 0000000..4433c54\n+Binary files /dev/null and b/org.spearce.egit.ui/icons/toolbar/fetchd.png differ\n+diff --git a/org.spearce.egit.ui/icons/toolbar/fetche.png b/org.spearce.egit.ui/icons/toolbar/fetche.png\n+new file mode 100644\n+index 0000000..0ffeb41\n+Binary files /dev/null and b/org.spearce.egit.ui/icons/toolbar/fetche.png differ\n+diff --git a/org.spearce.egit.ui/icons/toolbar/pushd.png b/org.spearce.egit.ui/icons/toolbar/pushd.png\n+new file mode 100644\n+index 0000000..22c3f7b\n+Binary files /dev/null and b/org.spearce.egit.ui/icons/toolbar/pushd.png differ\n+diff --git a/org.spearce.egit.ui/icons/toolbar/pushe.png b/org.spearce.egit.ui/icons/toolbar/pushe.png\n+new file mode 100644\n+index 0000000..1f99f0a\n+Binary files /dev/null and b/org.spearce.egit.ui/icons/toolbar/pushe.png differ\n+diff --git a/org.spearce.egit.ui/plugin.xml b/org.spearce.egit.ui/plugin.xml\n+index 7c98688..ee8a5a0 100644\n+--- a/org.spearce.egit.ui/plugin.xml\n++++ b/org.spearce.egit.ui/plugin.xml\n+@@ -272,22 +272,26 @@\n+         </separator>\n+ \t    </menu>\n+ \t\t<action\n+-\t\t       class=\"org.spearce.egit.ui.internal.actions.FetchAction\"\n+-\t\t       id=\"org.spearce.egit.ui.actionfetch\"\n+-\t\t       label=\"%FetchAction_label\"\n+-\t\t       style=\"push\"\n+-\t\t       menubarPath=\"org.spearce.egit.ui.gitmenu/repo\"\n+-\t\t       toolbarPath=\"org.spearce.egit.ui\"\n+-\t\t       tooltip=\"%FetchAction_tooltip\">\n++        class=\"org.spearce.egit.ui.internal.actions.FetchAction\"\n++        disabledIcon=\"icons/toolbar/fetchd.png\"\n++        icon=\"icons/toolbar/fetche.png\"\n++        id=\"org.spearce.egit.ui.actionfetch\"\n++        label=\"%FetchAction_label\"\n++        menubarPath=\"org.spearce.egit.ui.gitmenu/repo\"\n++        style=\"push\"\n++        toolbarPath=\"org.spearce.egit.ui\"\n++        tooltip=\"%FetchAction_tooltip\">\n+ \t\t</action>\n+ \t\t<action\n+-\t\t       class=\"org.spearce.egit.ui.internal.actions.PushAction\"\n+-\t\t       id=\"org.spearce.egit.ui.actionpush\"\n+-\t\t       label=\"%PushAction_label\"\n+-\t\t       style=\"push\"\n+-\t\t       menubarPath=\"org.spearce.egit.ui.gitmenu/repo\"\n+-\t\t       toolbarPath=\"org.spearce.egit.ui\"\n+-\t\t       tooltip=\"%PushAction_tooltip\">\n++        class=\"org.spearce.egit.ui.internal.actions.PushAction\"\n++        disabledIcon=\"icons/toolbar/pushd.png\"\n++        icon=\"icons/toolbar/pushe.png\"\n++        id=\"org.spearce.egit.ui.actionpush\"\n++        label=\"%PushAction_label\"\n++        menubarPath=\"org.spearce.egit.ui.gitmenu/repo\"\n++        style=\"push\"\n++        toolbarPath=\"org.spearce.egit.ui\"\n++        tooltip=\"%PushAction_tooltip\">\n+ \t\t</action>\n+ \t\t<action\n+ \t\t       class=\"org.spearce.egit.ui.internal.actions.BranchAction\"\n+-- \n+1.6.1.rc2.306.ge5d5e\n+\ndiff --git a/org.spearce.jgit/src/org/spearce/jgit/patch/FileHeader.java b/org.spearce.jgit/src/org/spearce/jgit/patch/FileHeader.java\nindex 5ff7cb7..4bb6b7e 100644\n--- a/org.spearce.jgit/src/org/spearce/jgit/patch/FileHeader.java\n+++ b/org.spearce.jgit/src/org/spearce/jgit/patch/FileHeader.java\n@@ -153,7 +153,7 @@\n \tprivate AbbreviatedObjectId newId;\n \n \t/** Type of patch used to modify this file */\n-\tprivate PatchType patchType;\n+\tPatchType patchType;\n \n \t/** The hunks of this file */\n \tprivate List<HunkHeader> hunks;\ndiff --git a/org.spearce.jgit/src/org/spearce/jgit/patch/Patch.java b/org.spearce.jgit/src/org/spearce/jgit/patch/Patch.java\nindex d59635a..6e9ae77 100644\n--- a/org.spearce.jgit/src/org/spearce/jgit/patch/Patch.java\n+++ b/org.spearce.jgit/src/org/spearce/jgit/patch/Patch.java\n@@ -57,6 +57,11 @@\n \n \tprivate static final byte[] DIFF_CC = encodeASCII(\"diff --cc \");\n \n+\tprivate static final byte[][] BIN_HEADERS = new byte[][] {\n+\t\t\tencodeASCII(\"Binary files \"), encodeASCII(\"Files \"), };\n+\n+\tprivate static final byte[] BIN_TRAILER = encodeASCII(\" differ\\n\");\n+\n \tstatic final byte[] SIG_FOOTER = encodeASCII(\"-- \\n\");\n \n \t/** The files, in the order they were parsed out of the input. */\n@@ -261,11 +266,30 @@ private int parseHunks(final FileHeader fh, final byte[] buf, int c) {\n \t\t\t\tcontinue;\n \t\t\t}\n \n+\t\t\tfinal int eol = nextLF(buf, c);\n+\t\t\tif (fh.getHunks().isEmpty() && BIN_TRAILER.length < eol - c\n+\t\t\t\t\t&& match(buf, eol - BIN_TRAILER.length, BIN_TRAILER) >= 0\n+\t\t\t\t\t&& matchAny(buf, c, BIN_HEADERS)) {\n+\t\t\t\t// The patch is a binary file diff, with no deltas.\n+\t\t\t\t//\n+\t\t\t\tfh.patchType = FileHeader.PatchType.BINARY;\n+\t\t\t\treturn eol;\n+\t\t\t}\n+\n \t\t\t// Skip this line and move to the next. Its probably garbage\n \t\t\t// after the last hunk of a file.\n \t\t\t//\n-\t\t\tc = nextLF(buf, c);\n+\t\t\tc = eol;\n \t\t}\n \t\treturn c;\n \t}\n+\n+\tprivate static boolean matchAny(final byte[] buf, final int c,\n+\t\t\tfinal byte[][] srcs) {\n+\t\tfor (final byte[] s : srcs) {\n+\t\t\tif (match(buf, c, s) >= 0)\n+\t\t\t\treturn true;\n+\t\t}\n+\t\treturn false;\n+\t}\n }\n-- \n1.6.1.rc2.306.ge5d5e\n"},{"id":"97672","messageId":"1229049981-14152-8-git-send-email-spearce@spearce.org","threadId":"16690","inReplyTo":"1229049981-14152-7-git-send-email-spearce@spearce.org","subject":"[JGIT PATCH 07/15] Set empty patches with no Git metadata to PatchType.BINARY","fromName":"Shawn O. Pearce","fromEmail":"spearce@spearce.org","sentAt":"2008-12-12T02:46:13Z","receivedAt":"2008-12-12T02:46:13Z","isPatch":true,"sender":{"key":"spearce@spearce.org","avatar":"https://avatars.githubusercontent.com/u/34844?v=4"},"body":"If a patch has no Git specific metadata and it has no hunks then\nit is very likely a binary patch with a \"Binary files ... differ\"\nwarning message in a different language, or the message has been\nmangled by an editor.  We should consider such patches to be the\nsame as a binary patch, as there is nothing here to perform an\naction on.\n\nSigned-off-by: Shawn O. Pearce <spearce@spearce.org>\n---\n .../tst/org/spearce/jgit/patch/FileHeaderTest.java |   18 ++++++++++++++++++\n .../tst/org/spearce/jgit/patch/PatchTest.java      |    2 ++\n .../src/org/spearce/jgit/patch/FileHeader.java     |    5 +++++\n .../src/org/spearce/jgit/patch/Patch.java          |   18 ++++++++++++++----\n 4 files changed, 39 insertions(+), 4 deletions(-)\n\ndiff --git a/org.spearce.jgit.test/tst/org/spearce/jgit/patch/FileHeaderTest.java b/org.spearce.jgit.test/tst/org/spearce/jgit/patch/FileHeaderTest.java\nindex d8696a9..4c2140a 100644\n--- a/org.spearce.jgit.test/tst/org/spearce/jgit/patch/FileHeaderTest.java\n+++ b/org.spearce.jgit.test/tst/org/spearce/jgit/patch/FileHeaderTest.java\n@@ -48,6 +48,7 @@ public void testParseGitFileName_Empty() {\n \t\tassertEquals(-1, fh.parseGitFileName(0));\n \t\tassertNotNull(fh.getHunks());\n \t\tassertTrue(fh.getHunks().isEmpty());\n+\t\tassertFalse(fh.hasMetaDataChanges());\n \t}\n \n \tpublic void testParseGitFileName_NoLF() {\n@@ -68,6 +69,7 @@ public void testParseGitFileName_Foo() {\n \t\tassertEquals(gitLine(name).length(), fh.parseGitFileName(0));\n \t\tassertEquals(name, fh.getOldName());\n \t\tassertSame(fh.getOldName(), fh.getNewName());\n+\t\tassertFalse(fh.hasMetaDataChanges());\n \t}\n \n \tpublic void testParseGitFileName_FailFooBar() {\n@@ -75,6 +77,7 @@ public void testParseGitFileName_FailFooBar() {\n \t\tassertTrue(fh.parseGitFileName(0) > 0);\n \t\tassertNull(fh.getOldName());\n \t\tassertNull(fh.getNewName());\n+\t\tassertFalse(fh.hasMetaDataChanges());\n \t}\n \n \tpublic void testParseGitFileName_FooSpBar() {\n@@ -83,6 +86,7 @@ public void testParseGitFileName_FooSpBar() {\n \t\tassertEquals(gitLine(name).length(), fh.parseGitFileName(0));\n \t\tassertEquals(name, fh.getOldName());\n \t\tassertSame(fh.getOldName(), fh.getNewName());\n+\t\tassertFalse(fh.hasMetaDataChanges());\n \t}\n \n \tpublic void testParseGitFileName_DqFooTabBar() {\n@@ -92,6 +96,7 @@ public void testParseGitFileName_DqFooTabBar() {\n \t\tassertEquals(dqGitLine(dqName).length(), fh.parseGitFileName(0));\n \t\tassertEquals(name, fh.getOldName());\n \t\tassertSame(fh.getOldName(), fh.getNewName());\n+\t\tassertFalse(fh.hasMetaDataChanges());\n \t}\n \n \tpublic void testParseGitFileName_DqFooSpLfNulBar() {\n@@ -101,6 +106,7 @@ public void testParseGitFileName_DqFooSpLfNulBar() {\n \t\tassertEquals(dqGitLine(dqName).length(), fh.parseGitFileName(0));\n \t\tassertEquals(name, fh.getOldName());\n \t\tassertSame(fh.getOldName(), fh.getNewName());\n+\t\tassertFalse(fh.hasMetaDataChanges());\n \t}\n \n \tpublic void testParseGitFileName_SrcFooC() {\n@@ -109,6 +115,7 @@ public void testParseGitFileName_SrcFooC() {\n \t\tassertEquals(gitLine(name).length(), fh.parseGitFileName(0));\n \t\tassertEquals(name, fh.getOldName());\n \t\tassertSame(fh.getOldName(), fh.getNewName());\n+\t\tassertFalse(fh.hasMetaDataChanges());\n \t}\n \n \tpublic void testParseGitFileName_SrcFooCNonStandardPrefix() {\n@@ -118,6 +125,7 @@ public void testParseGitFileName_SrcFooCNonStandardPrefix() {\n \t\tassertEquals(header.length(), fh.parseGitFileName(0));\n \t\tassertEquals(name, fh.getOldName());\n \t\tassertSame(fh.getOldName(), fh.getNewName());\n+\t\tassertFalse(fh.hasMetaDataChanges());\n \t}\n \n \tpublic void testParseUnicodeName_NewFile() {\n@@ -135,6 +143,7 @@ public void testParseUnicodeName_NewFile() {\n \n \t\tassertSame(FileHeader.ChangeType.ADD, fh.getChangeType());\n \t\tassertSame(FileHeader.PatchType.UNIFIED, fh.getPatchType());\n+\t\tassertTrue(fh.hasMetaDataChanges());\n \n \t\tassertNull(fh.getOldMode());\n \t\tassertSame(FileMode.REGULAR_FILE, fh.getNewMode());\n@@ -159,6 +168,7 @@ public void testParseUnicodeName_DeleteFile() {\n \n \t\tassertSame(FileHeader.ChangeType.DELETE, fh.getChangeType());\n \t\tassertSame(FileHeader.PatchType.UNIFIED, fh.getPatchType());\n+\t\tassertTrue(fh.hasMetaDataChanges());\n \n \t\tassertSame(FileMode.REGULAR_FILE, fh.getOldMode());\n \t\tassertNull(fh.getNewMode());\n@@ -177,6 +187,7 @@ public void testParseModeChange() {\n \n \t\tassertSame(FileHeader.ChangeType.MODIFY, fh.getChangeType());\n \t\tassertSame(FileHeader.PatchType.UNIFIED, fh.getPatchType());\n+\t\tassertTrue(fh.hasMetaDataChanges());\n \n \t\tassertNull(fh.getOldId());\n \t\tassertNull(fh.getNewId());\n@@ -204,6 +215,7 @@ public void testParseRename100_NewStyle() {\n \n \t\tassertSame(FileHeader.ChangeType.RENAME, fh.getChangeType());\n \t\tassertSame(FileHeader.PatchType.UNIFIED, fh.getPatchType());\n+\t\tassertTrue(fh.hasMetaDataChanges());\n \n \t\tassertNull(fh.getOldId());\n \t\tassertNull(fh.getNewId());\n@@ -232,6 +244,7 @@ public void testParseRename100_OldStyle() {\n \n \t\tassertSame(FileHeader.ChangeType.RENAME, fh.getChangeType());\n \t\tassertSame(FileHeader.PatchType.UNIFIED, fh.getPatchType());\n+\t\tassertTrue(fh.hasMetaDataChanges());\n \n \t\tassertNull(fh.getOldId());\n \t\tassertNull(fh.getNewId());\n@@ -260,6 +273,7 @@ public void testParseCopy100() {\n \n \t\tassertSame(FileHeader.ChangeType.COPY, fh.getChangeType());\n \t\tassertSame(FileHeader.PatchType.UNIFIED, fh.getPatchType());\n+\t\tassertTrue(fh.hasMetaDataChanges());\n \n \t\tassertNull(fh.getOldId());\n \t\tassertNull(fh.getNewId());\n@@ -282,6 +296,7 @@ public void testParseFullIndexLine_WithMode() {\n \n \t\tassertSame(FileMode.REGULAR_FILE, fh.getOldMode());\n \t\tassertSame(FileMode.REGULAR_FILE, fh.getNewMode());\n+\t\tassertFalse(fh.hasMetaDataChanges());\n \n \t\tassertNotNull(fh.getOldId());\n \t\tassertNotNull(fh.getNewId());\n@@ -302,6 +317,7 @@ public void testParseFullIndexLine_NoMode() {\n \n \t\tassertEquals(\"a\", fh.getOldName());\n \t\tassertEquals(\"a\", fh.getNewName());\n+\t\tassertFalse(fh.hasMetaDataChanges());\n \n \t\tassertNull(fh.getOldMode());\n \t\tassertNull(fh.getNewMode());\n@@ -330,6 +346,7 @@ public void testParseAbbrIndexLine_WithMode() {\n \n \t\tassertSame(FileMode.REGULAR_FILE, fh.getOldMode());\n \t\tassertSame(FileMode.REGULAR_FILE, fh.getNewMode());\n+\t\tassertFalse(fh.hasMetaDataChanges());\n \n \t\tassertNotNull(fh.getOldId());\n \t\tassertNotNull(fh.getNewId());\n@@ -358,6 +375,7 @@ public void testParseAbbrIndexLine_NoMode() {\n \n \t\tassertNull(fh.getOldMode());\n \t\tassertNull(fh.getNewMode());\n+\t\tassertFalse(fh.hasMetaDataChanges());\n \n \t\tassertNotNull(fh.getOldId());\n \t\tassertNotNull(fh.getNewId());\ndiff --git a/org.spearce.jgit.test/tst/org/spearce/jgit/patch/PatchTest.java b/org.spearce.jgit.test/tst/org/spearce/jgit/patch/PatchTest.java\nindex 833bf5d..bf37063 100644\n--- a/org.spearce.jgit.test/tst/org/spearce/jgit/patch/PatchTest.java\n+++ b/org.spearce.jgit.test/tst/org/spearce/jgit/patch/PatchTest.java\n@@ -156,12 +156,14 @@ assertTrue(fh.getNewName().startsWith(\n \t\t\t\t\t\"org.spearce.egit.ui/icons/toolbar/\"));\n \t\t\tassertSame(FileHeader.PatchType.BINARY, fh.getPatchType());\n \t\t\tassertTrue(fh.getHunks().isEmpty());\n+\t\t\tassertTrue(fh.hasMetaDataChanges());\n \t\t}\n \n \t\tfinal FileHeader fh = p.getFiles().get(4);\n \t\tassertEquals(\"org.spearce.egit.ui/plugin.xml\", fh.getNewName());\n \t\tassertSame(FileHeader.ChangeType.MODIFY, fh.getChangeType());\n \t\tassertSame(FileHeader.PatchType.UNIFIED, fh.getPatchType());\n+\t\tassertFalse(fh.hasMetaDataChanges());\n \t\tassertEquals(\"ee8a5a0\", fh.getNewId().name());\n \t\tassertEquals(1, fh.getHunks().size());\n \t\tassertEquals(272, fh.getHunks().get(0).getOldStartLine());\ndiff --git a/org.spearce.jgit/src/org/spearce/jgit/patch/FileHeader.java b/org.spearce.jgit/src/org/spearce/jgit/patch/FileHeader.java\nindex 4bb6b7e..bf8d23a 100644\n--- a/org.spearce.jgit/src/org/spearce/jgit/patch/FileHeader.java\n+++ b/org.spearce.jgit/src/org/spearce/jgit/patch/FileHeader.java\n@@ -250,6 +250,11 @@ public PatchType getPatchType() {\n \t\treturn patchType;\n \t}\n \n+\t/** @return true if this patch modifies metadata about a file */\n+\tpublic boolean hasMetaDataChanges() {\n+\t\treturn changeType != ChangeType.MODIFY || newMode != oldMode;\n+\t}\n+\n \t/** @return hunks altering this file; in order of appearance in patch */\n \tpublic List<HunkHeader> getHunks() {\n \t\tif (hunks == null)\ndiff --git a/org.spearce.jgit/src/org/spearce/jgit/patch/Patch.java b/org.spearce.jgit/src/org/spearce/jgit/patch/Patch.java\nindex 6e9ae77..c940a00 100644\n--- a/org.spearce.jgit/src/org/spearce/jgit/patch/Patch.java\n+++ b/org.spearce.jgit/src/org/spearce/jgit/patch/Patch.java\n@@ -245,13 +245,13 @@ private int parseHunks(final FileHeader fh, final byte[] buf, int c) {\n \t\t\t// with this position so it can be parsed again later.\n \t\t\t//\n \t\t\tif (match(buf, c, DIFF_GIT) >= 0)\n-\t\t\t\treturn c;\n+\t\t\t\tbreak;\n \t\t\tif (match(buf, c, DIFF_CC) >= 0)\n-\t\t\t\treturn c;\n+\t\t\t\tbreak;\n \t\t\tif (match(buf, c, OLD_NAME) >= 0)\n-\t\t\t\treturn c;\n+\t\t\t\tbreak;\n \t\t\tif (match(buf, c, NEW_NAME) >= 0)\n-\t\t\t\treturn c;\n+\t\t\t\tbreak;\n \n \t\t\tif (match(buf, c, HUNK_HDR) >= 0) {\n \t\t\t\tfinal HunkHeader h = new HunkHeader(fh, c);\n@@ -281,6 +281,16 @@ private int parseHunks(final FileHeader fh, final byte[] buf, int c) {\n \t\t\t//\n \t\t\tc = eol;\n \t\t}\n+\n+\t\tif (fh.getHunks().isEmpty()\n+\t\t\t\t&& fh.getPatchType() == FileHeader.PatchType.UNIFIED\n+\t\t\t\t&& !fh.hasMetaDataChanges()) {\n+\t\t\t// Hmm, an empty patch? If there is no metadata here we\n+\t\t\t// really have a binary patch that we didn't notice above.\n+\t\t\t//\n+\t\t\tfh.patchType = FileHeader.PatchType.BINARY;\n+\t\t}\n+\n \t\treturn c;\n \t}\n \n-- \n1.6.1.rc2.306.ge5d5e\n"},{"id":"97666","messageId":"1229049981-14152-9-git-send-email-spearce@spearce.org","threadId":"16690","inReplyTo":"1229049981-14152-8-git-send-email-spearce@spearce.org","subject":"[JGIT PATCH 08/15] Always use the FileHeader buffer during Patch.parseHunks","fromName":"Shawn O. Pearce","fromEmail":"spearce@spearce.org","sentAt":"2008-12-12T02:46:14Z","receivedAt":"2008-12-12T02:46:14Z","isPatch":true,"sender":{"key":"spearce@spearce.org","avatar":"https://avatars.githubusercontent.com/u/34844?v=4"},"body":"This ensures that if the code is ever refactored in the future to\npermit different entry path into parseHunks that we won't read the\nwrong buffer and potentially setup an invalid FileHeader.\n\nSigned-off-by: Shawn O. Pearce <spearce@spearce.org>\n---\n .../src/org/spearce/jgit/patch/Patch.java          |    7 ++++---\n 1 files changed, 4 insertions(+), 3 deletions(-)\n\ndiff --git a/org.spearce.jgit/src/org/spearce/jgit/patch/Patch.java b/org.spearce.jgit/src/org/spearce/jgit/patch/Patch.java\nindex c940a00..164ad96 100644\n--- a/org.spearce.jgit/src/org/spearce/jgit/patch/Patch.java\n+++ b/org.spearce.jgit/src/org/spearce/jgit/patch/Patch.java\n@@ -202,7 +202,7 @@ private int parseDiffGit(final byte[] buf, final int startOffset) {\n \t\t\treturn skipFile(buf, startOffset);\n \n \t\tptr = fh.parseGitHeaders(ptr);\n-\t\tptr = parseHunks(fh, buf, ptr);\n+\t\tptr = parseHunks(fh, ptr);\n \t\tfh.endOffset = ptr;\n \t\taddFile(fh);\n \t\treturn ptr;\n@@ -224,7 +224,7 @@ private int parseDiffCC(final byte[] buf, final int startOffset) {\n \tprivate int parseTraditionalPatch(final byte[] buf, final int startOffset) {\n \t\tfinal FileHeader fh = new FileHeader(buf, startOffset);\n \t\tint ptr = fh.parseTraditionalHeaders(startOffset);\n-\t\tptr = parseHunks(fh, buf, ptr);\n+\t\tptr = parseHunks(fh, ptr);\n \t\tfh.endOffset = ptr;\n \t\taddFile(fh);\n \t\treturn ptr;\n@@ -237,7 +237,8 @@ private static int skipFile(final byte[] buf, int ptr) {\n \t\treturn ptr;\n \t}\n \n-\tprivate int parseHunks(final FileHeader fh, final byte[] buf, int c) {\n+\tprivate int parseHunks(final FileHeader fh, int c) {\n+\t\tfinal byte[] buf = fh.buf;\n \t\tfinal int sz = buf.length;\n \t\twhile (c < sz) {\n \t\t\t// If we see a file header at this point, we have all of the\n-- \n1.6.1.rc2.306.ge5d5e\n"},{"id":"97665","messageId":"1229049981-14152-10-git-send-email-spearce@spearce.org","threadId":"16690","inReplyTo":"1229049981-14152-9-git-send-email-spearce@spearce.org","subject":"[JGIT PATCH 09/15] Parse \"GIT binary patch\" style patch metadata","fromName":"Shawn O. Pearce","fromEmail":"spearce@spearce.org","sentAt":"2008-12-12T02:46:15Z","receivedAt":"2008-12-12T02:46:15Z","isPatch":true,"sender":{"key":"spearce@spearce.org","avatar":"https://avatars.githubusercontent.com/u/34844?v=4"},"body":"Git can produce binary patches that carry the full content or\ndelta information encoded in a base-85 text payload.  We parse\nthe headers out and produce a BinaryHunk wrapper for these so\napplications can identify that the hunk exists, but we do not\nattempt to decode the binary information.\n\nSigned-off-by: Shawn O. Pearce <spearce@spearce.org>\n---\n .../tst/org/spearce/jgit/patch/PatchTest.java      |   42 ++++++\n .../spearce/jgit/patch/testParse_GitBinary.patch   |  135 ++++++++++++++++++++\n .../src/org/spearce/jgit/patch/BinaryHunk.java     |  128 +++++++++++++++++++\n .../src/org/spearce/jgit/patch/FileHeader.java     |   16 +++\n .../src/org/spearce/jgit/patch/Patch.java          |   32 +++++\n 5 files changed, 353 insertions(+), 0 deletions(-)\n create mode 100644 org.spearce.jgit.test/tst/org/spearce/jgit/patch/testParse_GitBinary.patch\n create mode 100644 org.spearce.jgit/src/org/spearce/jgit/patch/BinaryHunk.java\n\ndiff --git a/org.spearce.jgit.test/tst/org/spearce/jgit/patch/PatchTest.java b/org.spearce.jgit.test/tst/org/spearce/jgit/patch/PatchTest.java\nindex bf37063..453d88e 100644\n--- a/org.spearce.jgit.test/tst/org/spearce/jgit/patch/PatchTest.java\n+++ b/org.spearce.jgit.test/tst/org/spearce/jgit/patch/PatchTest.java\n@@ -43,6 +43,7 @@\n import junit.framework.TestCase;\n \n import org.spearce.jgit.lib.FileMode;\n+import org.spearce.jgit.lib.ObjectId;\n \n public class PatchTest extends TestCase {\n \tpublic void testEmpty() {\n@@ -157,6 +158,45 @@ assertTrue(fh.getNewName().startsWith(\n \t\t\tassertSame(FileHeader.PatchType.BINARY, fh.getPatchType());\n \t\t\tassertTrue(fh.getHunks().isEmpty());\n \t\t\tassertTrue(fh.hasMetaDataChanges());\n+\n+\t\t\tassertNull(fh.getForwardBinaryHunk());\n+\t\t\tassertNull(fh.getReverseBinaryHunk());\n+\t\t}\n+\n+\t\tfinal FileHeader fh = p.getFiles().get(4);\n+\t\tassertEquals(\"org.spearce.egit.ui/plugin.xml\", fh.getNewName());\n+\t\tassertSame(FileHeader.ChangeType.MODIFY, fh.getChangeType());\n+\t\tassertSame(FileHeader.PatchType.UNIFIED, fh.getPatchType());\n+\t\tassertFalse(fh.hasMetaDataChanges());\n+\t\tassertEquals(\"ee8a5a0\", fh.getNewId().name());\n+\t\tassertNull(fh.getForwardBinaryHunk());\n+\t\tassertNull(fh.getReverseBinaryHunk());\n+\t\tassertEquals(1, fh.getHunks().size());\n+\t\tassertEquals(272, fh.getHunks().get(0).getOldStartLine());\n+\t}\n+\n+\tpublic void testParse_GitBinary() throws IOException {\n+\t\tfinal Patch p = parseTestPatchFile();\n+\t\tfinal int[] binsizes = { 359, 393, 372, 404 };\n+\t\tassertEquals(5, p.getFiles().size());\n+\n+\t\tfor (int i = 0; i < 4; i++) {\n+\t\t\tfinal FileHeader fh = p.getFiles().get(i);\n+\t\t\tassertSame(FileHeader.ChangeType.ADD, fh.getChangeType());\n+\t\t\tassertNotNull(fh.getOldId());\n+\t\t\tassertNotNull(fh.getNewId());\n+\t\t\tassertEquals(ObjectId.zeroId().name(), fh.getOldId().name());\n+\t\t\tassertSame(FileMode.REGULAR_FILE, fh.getNewMode());\n+\t\t\tassertTrue(fh.getNewName().startsWith(\n+\t\t\t\t\t\"org.spearce.egit.ui/icons/toolbar/\"));\n+\t\t\tassertSame(FileHeader.PatchType.GIT_BINARY, fh.getPatchType());\n+\t\t\tassertTrue(fh.getHunks().isEmpty());\n+\t\t\tassertTrue(fh.hasMetaDataChanges());\n+\n+\t\t\tassertNotNull(fh.getForwardBinaryHunk());\n+\t\t\tassertNotNull(fh.getReverseBinaryHunk());\n+\t\t\tassertEquals(binsizes[i], fh.getForwardBinaryHunk().getSize());\n+\t\t\tassertEquals(0, fh.getReverseBinaryHunk().getSize());\n \t\t}\n \n \t\tfinal FileHeader fh = p.getFiles().get(4);\n@@ -165,6 +205,8 @@ assertTrue(fh.getNewName().startsWith(\n \t\tassertSame(FileHeader.PatchType.UNIFIED, fh.getPatchType());\n \t\tassertFalse(fh.hasMetaDataChanges());\n \t\tassertEquals(\"ee8a5a0\", fh.getNewId().name());\n+\t\tassertNull(fh.getForwardBinaryHunk());\n+\t\tassertNull(fh.getReverseBinaryHunk());\n \t\tassertEquals(1, fh.getHunks().size());\n \t\tassertEquals(272, fh.getHunks().get(0).getOldStartLine());\n \t}\ndiff --git a/org.spearce.jgit.test/tst/org/spearce/jgit/patch/testParse_GitBinary.patch b/org.spearce.jgit.test/tst/org/spearce/jgit/patch/testParse_GitBinary.patch\nnew file mode 100644\nindex 0000000..ab7b235\n--- /dev/null\n+++ b/org.spearce.jgit.test/tst/org/spearce/jgit/patch/testParse_GitBinary.patch\n@@ -0,0 +1,135 @@\n+From 8363f12135a7d0ff0b5fea7d5a35d294c0479518 Mon Sep 17 00:00:00 2001\n+From: Robin Rosenberg <robin.rosenberg.lists@dewire.com>\n+Date: Tue, 23 Sep 2008 22:19:19 +0200\n+Subject: [PATCH] Push and fetch icons for the toolbar\n+\n+Signed-off-by: Robin Rosenberg <robin.rosenberg@dewire.com>\n+Signed-off-by: Shawn O. Pearce <spearce@spearce.org>\n+---\n+ org.spearce.egit.ui/icons/toolbar/fetchd.png |  Bin 0 -> 359 bytes\n+ org.spearce.egit.ui/icons/toolbar/fetche.png |  Bin 0 -> 393 bytes\n+ org.spearce.egit.ui/icons/toolbar/pushd.png  |  Bin 0 -> 372 bytes\n+ org.spearce.egit.ui/icons/toolbar/pushe.png  |  Bin 0 -> 404 bytes\n+ org.spearce.egit.ui/plugin.xml               |   32 ++++++++++++++-----------\n+ 5 files changed, 18 insertions(+), 14 deletions(-)\n+ create mode 100644 org.spearce.egit.ui/icons/toolbar/fetchd.png\n+ create mode 100644 org.spearce.egit.ui/icons/toolbar/fetche.png\n+ create mode 100644 org.spearce.egit.ui/icons/toolbar/pushd.png\n+ create mode 100644 org.spearce.egit.ui/icons/toolbar/pushe.png\n+\n+diff --git a/org.spearce.egit.ui/icons/toolbar/fetchd.png b/org.spearce.egit.ui/icons/toolbar/fetchd.png\n+new file mode 100644\n+index 0000000000000000000000000000000000000000..4433c543f2a52b586a3ed5e31b138244107bc239\n+GIT binary patch\n+literal 359\n+zcmV-t0hs=YP)<h;3K|Lk000e1NJLTq000mG000mO1^@s6AM^iV0003lNkl<ZIE|%~\n+zF{;8q6h+U7BBn4qm_`dxuy6}@!NzsCfLTKp;5yPsN=p|I(rKe&p~M74Bq5ObRRW{%\n+zje4sYc=ybGk2gYV%`a+{hvjnl6{!Lo4hNJ{A44jDl#(C_K2BmJ09dV7A3>VW2}+n!\n+zO1rKr0DRxqm&=7b&q>piUayDaIKlvw2}+e_&-3(PFkmzq(Q380-|r+zg6Da9KA(#Q\n+zPa2QMl^Gz*GK3HmMPby1VQ9_(U^bf>W`H=3+3)uhMZxuY<#al++wJIfyXFi47K?>p\n+z2GCj~rNp*vI-L&fb{osGh@!|$@ci;S2_arv_(sil1xb<+1Odb0kjLY}`F!ShJaW6;\n+z>Lr*?r);;|7ihoV2SC*MjhoFzuh;A9KAB9aMXCk(Pk$Loi}X0uxcmSB002ovPDHLk\n+FV1lxPoI3yj\n+\n+literal 0\n+HcmV?d00001\n+\n+diff --git a/org.spearce.egit.ui/icons/toolbar/fetche.png b/org.spearce.egit.ui/icons/toolbar/fetche.png\n+new file mode 100644\n+index 0000000000000000000000000000000000000000..0ffeb419e6ab302caa5e58661854b33853dc43dc\n+GIT binary patch\n+literal 393\n+zcmV;40e1e0P)<h;3K|Lk000e1NJLTq000mG000mO1^@s6AM^iV0003{Nkl<ZIE|%}\n+z%Su8~6o$WjJd|h&)kLg8qatclklHDbhzOh%4SI>*L3{^aqJcvRqCsRQ6~SndH7ExK\n+zk=KcyE?#y6(aupYt$(ujUi|ChXYD1Vl>A3Z=W-tL|B2KE=%pm#uoxNA1!yxqxEMW&\n+zB>{jQO^yT+ogtn_{8Ep$Aq3h-C?o|y>g-6?-!e46K4}{7I2X6^?w$w$wKqXWo#uE<\n+zlN$@u$mIiCW0N$hIYc2#Jf_L5pe_`875HfeP>nhW1zLv1R!iSvNdTZ7`q(*62#YbV\n+zQhB;#V#z_Hl;tD;jPm%3!!_Fv=xqj&EpW_lqPo^m>_wFE9KxQ3t1@8v1#@h(gk?2k\n+zU%h_@BTD_vVB{6b=^Lij^3<ya#!DI7eU*yg9xg#(&qL<HX{n_QH=dOmU|OU>Dkk>j\n+n^=YB|UiI3T3toz$0fY1nZ1068v8@+b00000NkvXXu0mjfWwNMg\n+\n+literal 0\n+HcmV?d00001\n+\n+diff --git a/org.spearce.egit.ui/icons/toolbar/pushd.png b/org.spearce.egit.ui/icons/toolbar/pushd.png\n+new file mode 100644\n+index 0000000000000000000000000000000000000000..22c3f7bf17b8b5a931c7527d768af13607b03bce\n+GIT binary patch\n+literal 372\n+zcmV-)0gL{LP)<h;3K|Lk000e1NJLTq000mG000mO1^@s6AM^iV0003yNkl<ZIE|%~\n+zF{%PF6h%)&8w)Kgg)~~o6c%=-wG$iH;R0NP3veAG$Sh>A(?&%>K?5S92zkHxGyeyN\n+zQSnxRymxZ%OQJ-CZ<LQ0VHnEcaNzNHaJ${8)oOIRUG)l}M1;v?B8^6aVzEe}P~dX8\n+zV6A1h+tKg$Ga)&E`~8km3g?`+IiJs8M#ur2PA68Y70x-1$0OVAmgRCusZ@FoAR=h3\n+zDVNJsDix~LD)oBZD;$r<sngD7(Utm(zh18y4u?;WOu&C>t%;)O$w?l-T1yl~1VO;{\n+zdS$=gv)ODopU<8HfZ1#YAcMg`B@Q~B4vWRYJJDL}%|UCO8Yd6XZnu?)$aFeQidwCf\n+z_mE--u|}hjN&o=H7-fukIg4hqnKXNVchu|kh_lCf`xbzwX88RJ-{>O;Y5D>6^@Sy#\n+SDlMe|0000<MNUMnLSTZnn4{zX\n+\n+literal 0\n+HcmV?d00001\n+\n+diff --git a/org.spearce.egit.ui/icons/toolbar/pushe.png b/org.spearce.egit.ui/icons/toolbar/pushe.png\n+new file mode 100644\n+index 0000000000000000000000000000000000000000..1f99f0a702983d674f15eedae5f1218f0a30b5a0\n+GIT binary patch\n+literal 404\n+zcmV;F0c-w=P)<h;3K|Lk000e1NJLTq000mG000mO1^@s6AM^iV00047Nkl<ZIE|%~\n+z%Su8~6o$WjJO~|4C>w|sG%6%VHSk(UL<B+v4SI>*L41dvA&3Z?G>L*BB*n0rWDTl8\n+zL8Pb?Jzc!)CSJ}#$rF8}#a?Uu`(JCbg_M&2pmu`H$+oP&=V*R^(bPY1%&ibu+ZV$G\n+zgp`tt<A@B;jw3Z6E&C{q>NBF4=c=f%6i@vsq5!CR9fSfc-IT0lZ>^0`E2vbS?r{1v\n+z8l^m+g%^~^H#FDePyq!%wm_SSqPmu`yJKk6QAXzdroz+R(7<gg074jZz1Vo3Dy2y#\n+zMW2W=)MJ~7I|%3fPE-KBpis_UGqzZuUe(cG%h>L#RCJHY0YK_74TR+C&ZX!&h^>3c\n+zJvdA^W^@l;f6eS*z&I*^D|{frVpE>&7273F76LY=;y1$BWF(Q0qALI}5jqkZAq&fh\n+y^_oorR)}l`>CE22@+$y+&Cvb}|KU##2Jr)k?t0Dap2#Es0000<MNUMnLSTZgH?cGT\n+\n+literal 0\n+HcmV?d00001\n+\n+diff --git a/org.spearce.egit.ui/plugin.xml b/org.spearce.egit.ui/plugin.xml\n+index 7c98688..ee8a5a0 100644\n+--- a/org.spearce.egit.ui/plugin.xml\n++++ b/org.spearce.egit.ui/plugin.xml\n+@@ -272,22 +272,26 @@\n+         </separator>\n+ \t    </menu>\n+ \t\t<action\n+-\t\t       class=\"org.spearce.egit.ui.internal.actions.FetchAction\"\n+-\t\t       id=\"org.spearce.egit.ui.actionfetch\"\n+-\t\t       label=\"%FetchAction_label\"\n+-\t\t       style=\"push\"\n+-\t\t       menubarPath=\"org.spearce.egit.ui.gitmenu/repo\"\n+-\t\t       toolbarPath=\"org.spearce.egit.ui\"\n+-\t\t       tooltip=\"%FetchAction_tooltip\">\n++        class=\"org.spearce.egit.ui.internal.actions.FetchAction\"\n++        disabledIcon=\"icons/toolbar/fetchd.png\"\n++        icon=\"icons/toolbar/fetche.png\"\n++        id=\"org.spearce.egit.ui.actionfetch\"\n++        label=\"%FetchAction_label\"\n++        menubarPath=\"org.spearce.egit.ui.gitmenu/repo\"\n++        style=\"push\"\n++        toolbarPath=\"org.spearce.egit.ui\"\n++        tooltip=\"%FetchAction_tooltip\">\n+ \t\t</action>\n+ \t\t<action\n+-\t\t       class=\"org.spearce.egit.ui.internal.actions.PushAction\"\n+-\t\t       id=\"org.spearce.egit.ui.actionpush\"\n+-\t\t       label=\"%PushAction_label\"\n+-\t\t       style=\"push\"\n+-\t\t       menubarPath=\"org.spearce.egit.ui.gitmenu/repo\"\n+-\t\t       toolbarPath=\"org.spearce.egit.ui\"\n+-\t\t       tooltip=\"%PushAction_tooltip\">\n++        class=\"org.spearce.egit.ui.internal.actions.PushAction\"\n++        disabledIcon=\"icons/toolbar/pushd.png\"\n++        icon=\"icons/toolbar/pushe.png\"\n++        id=\"org.spearce.egit.ui.actionpush\"\n++        label=\"%PushAction_label\"\n++        menubarPath=\"org.spearce.egit.ui.gitmenu/repo\"\n++        style=\"push\"\n++        toolbarPath=\"org.spearce.egit.ui\"\n++        tooltip=\"%PushAction_tooltip\">\n+ \t\t</action>\n+ \t\t<action\n+ \t\t       class=\"org.spearce.egit.ui.internal.actions.BranchAction\"\n+-- \n+1.6.1.rc2.306.ge5d5e\n+\ndiff --git a/org.spearce.jgit/src/org/spearce/jgit/patch/BinaryHunk.java b/org.spearce.jgit/src/org/spearce/jgit/patch/BinaryHunk.java\nnew file mode 100644\nindex 0000000..ce35286\n--- /dev/null\n+++ b/org.spearce.jgit/src/org/spearce/jgit/patch/BinaryHunk.java\n@@ -0,0 +1,128 @@\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.patch;\n+\n+import static org.spearce.jgit.lib.Constants.encodeASCII;\n+import static org.spearce.jgit.util.RawParseUtils.match;\n+import static org.spearce.jgit.util.RawParseUtils.nextLF;\n+import static org.spearce.jgit.util.RawParseUtils.parseBase10;\n+\n+/** Part of a \"GIT binary patch\" to describe the pre-image or post-image */\n+public class BinaryHunk {\n+\tprivate static final byte[] LITERAL = encodeASCII(\"literal \");\n+\n+\tprivate static final byte[] DELTA = encodeASCII(\"delta \");\n+\n+\t/** Type of information stored in a binary hunk. */\n+\tpublic static enum Type {\n+\t\t/** The full content is stored, deflated. */\n+\t\tLITERAL_DEFLATED,\n+\n+\t\t/** A Git pack-style delta is stored, deflated. */\n+\t\tDELTA_DEFLATED;\n+\t}\n+\n+\tprivate final FileHeader file;\n+\n+\t/** Offset within {@link #file}.buf to the \"literal\" or \"delta \" line. */\n+\tfinal int startOffset;\n+\n+\t/** Position 1 past the end of this hunk within {@link #file}'s buf. */\n+\tint endOffset;\n+\n+\t/** Type of the data meaning. */\n+\tprivate Type type;\n+\n+\t/** Inflated length of the data. */\n+\tprivate int length;\n+\n+\tBinaryHunk(final FileHeader fh, final int offset) {\n+\t\tfile = fh;\n+\t\tstartOffset = offset;\n+\t}\n+\n+\t/** @return header for the file this hunk applies to */\n+\tpublic FileHeader getFileHeader() {\n+\t\treturn file;\n+\t}\n+\n+\t/** @return type of this binary hunk */\n+\tpublic Type getType() {\n+\t\treturn type;\n+\t}\n+\n+\t/** @return inflated size of this hunk's data */\n+\tpublic int getSize() {\n+\t\treturn length;\n+\t}\n+\n+\tint parseHunk(int ptr) {\n+\t\tfinal byte[] buf = file.buf;\n+\n+\t\tif (match(buf, ptr, LITERAL) >= 0) {\n+\t\t\ttype = Type.LITERAL_DEFLATED;\n+\t\t\tlength = parseBase10(buf, ptr + LITERAL.length, null);\n+\n+\t\t} else if (match(buf, ptr, DELTA) >= 0) {\n+\t\t\ttype = Type.DELTA_DEFLATED;\n+\t\t\tlength = parseBase10(buf, ptr + LITERAL.length, null);\n+\n+\t\t} else {\n+\t\t\t// Not a valid binary hunk. Signal to the caller that\n+\t\t\t// we cannot parse any further and that this line should\n+\t\t\t// be treated otherwise.\n+\t\t\t//\n+\t\t\treturn -1;\n+\t\t}\n+\t\tptr = nextLF(buf, ptr);\n+\n+\t\t// Skip until the first blank line; that is the end of the binary\n+\t\t// encoded information in this hunk. To save time we don't do a\n+\t\t// validation of the binary data at this point.\n+\t\t//\n+\t\tfinal int sz = buf.length;\n+\t\twhile (ptr < sz) {\n+\t\t\tfinal boolean empty = buf[ptr] == '\\n';\n+\t\t\tptr = nextLF(buf, ptr);\n+\t\t\tif (empty)\n+\t\t\t\tbreak;\n+\t\t}\n+\n+\t\treturn ptr;\n+\t}\n+}\ndiff --git a/org.spearce.jgit/src/org/spearce/jgit/patch/FileHeader.java b/org.spearce.jgit/src/org/spearce/jgit/patch/FileHeader.java\nindex bf8d23a..a6ff4a6 100644\n--- a/org.spearce.jgit/src/org/spearce/jgit/patch/FileHeader.java\n+++ b/org.spearce.jgit/src/org/spearce/jgit/patch/FileHeader.java\n@@ -158,6 +158,12 @@\n \t/** The hunks of this file */\n \tprivate List<HunkHeader> hunks;\n \n+\t/** If {@link #patchType} is {@link PatchType#GIT_BINARY}, the new image */\n+\tBinaryHunk forwardBinaryHunk;\n+\n+\t/** If {@link #patchType} is {@link PatchType#GIT_BINARY}, the old image */\n+\tBinaryHunk reverseBinaryHunk;\n+\n \tFileHeader(final byte[] b, final int offset) {\n \t\tbuf = b;\n \t\tstartOffset = offset;\n@@ -270,6 +276,16 @@ void addHunk(final HunkHeader h) {\n \t\thunks.add(h);\n \t}\n \n+\t/** @return if a {@link PatchType#GIT_BINARY}, the new-image delta/literal */\n+\tpublic BinaryHunk getForwardBinaryHunk() {\n+\t\treturn forwardBinaryHunk;\n+\t}\n+\n+\t/** @return if a {@link PatchType#GIT_BINARY}, the old-image delta/literal */\n+\tpublic BinaryHunk getReverseBinaryHunk() {\n+\t\treturn reverseBinaryHunk;\n+\t}\n+\n \t/**\n \t * Parse a \"diff --git\" or \"diff --cc\" line.\n \t * \ndiff --git a/org.spearce.jgit/src/org/spearce/jgit/patch/Patch.java b/org.spearce.jgit/src/org/spearce/jgit/patch/Patch.java\nindex 164ad96..56eb327 100644\n--- a/org.spearce.jgit/src/org/spearce/jgit/patch/Patch.java\n+++ b/org.spearce.jgit/src/org/spearce/jgit/patch/Patch.java\n@@ -62,6 +62,8 @@\n \n \tprivate static final byte[] BIN_TRAILER = encodeASCII(\" differ\\n\");\n \n+\tprivate static final byte[] GIT_BINARY = encodeASCII(\"GIT binary patch\\n\");\n+\n \tstatic final byte[] SIG_FOOTER = encodeASCII(\"-- \\n\");\n \n \t/** The files, in the order they were parsed out of the input. */\n@@ -268,6 +270,11 @@ private int parseHunks(final FileHeader fh, int c) {\n \t\t\t}\n \n \t\t\tfinal int eol = nextLF(buf, c);\n+\t\t\tif (fh.getHunks().isEmpty() && match(buf, c, GIT_BINARY) >= 0) {\n+\t\t\t\tfh.patchType = FileHeader.PatchType.GIT_BINARY;\n+\t\t\t\treturn parseGitBinary(fh, eol);\n+\t\t\t}\n+\n \t\t\tif (fh.getHunks().isEmpty() && BIN_TRAILER.length < eol - c\n \t\t\t\t\t&& match(buf, eol - BIN_TRAILER.length, BIN_TRAILER) >= 0\n \t\t\t\t\t&& matchAny(buf, c, BIN_HEADERS)) {\n@@ -295,6 +302,31 @@ private int parseHunks(final FileHeader fh, int c) {\n \t\treturn c;\n \t}\n \n+\tprivate int parseGitBinary(final FileHeader fh, int c) {\n+\t\tfinal BinaryHunk postImage = new BinaryHunk(fh, c);\n+\t\tfinal int nEnd = postImage.parseHunk(c);\n+\t\tif (nEnd < 0) {\n+\t\t\t// Not a binary hunk.\n+\t\t\t//\n+\n+\t\t\t// TODO handle invalid binary hunks\n+\t\t\treturn c;\n+\t\t}\n+\t\tc = nEnd;\n+\t\tpostImage.endOffset = c;\n+\t\tfh.forwardBinaryHunk = postImage;\n+\n+\t\tfinal BinaryHunk preImage = new BinaryHunk(fh, c);\n+\t\tfinal int oEnd = preImage.parseHunk(c);\n+\t\tif (oEnd >= 0) {\n+\t\t\tc = oEnd;\n+\t\t\tpreImage.endOffset = c;\n+\t\t\tfh.reverseBinaryHunk = preImage;\n+\t\t}\n+\n+\t\treturn c;\n+\t}\n+\n \tprivate static boolean matchAny(final byte[] buf, final int c,\n \t\t\tfinal byte[][] srcs) {\n \t\tfor (final byte[] s : srcs) {\n-- \n1.6.1.rc2.306.ge5d5e\n"},{"id":"97668","messageId":"1229049981-14152-11-git-send-email-spearce@spearce.org","threadId":"16690","inReplyTo":"1229049981-14152-10-git-send-email-spearce@spearce.org","subject":"[JGIT PATCH 10/15] Record patch parsing errors for later inspection by applications","fromName":"Shawn O. Pearce","fromEmail":"spearce@spearce.org","sentAt":"2008-12-12T02:46:16Z","receivedAt":"2008-12-12T02:46:16Z","isPatch":true,"sender":{"key":"spearce@spearce.org","avatar":"https://avatars.githubusercontent.com/u/34844?v=4"},"body":"Errors identified while reading a patch script are now collected\ninto FormatError objects within the errors collection of a Patch.\nThese can be inspected to determine if a common form of breakage\nis found within the patch script once its basic metadata is read.\n\nSigned-off-by: Shawn O. Pearce <spearce@spearce.org>\n---\n .../tst/org/spearce/jgit/patch/PatchErrorTest.java |  174 ++++++++++++++++++++\n .../tst/org/spearce/jgit/patch/PatchTest.java      |    4 +\n .../spearce/jgit/patch/testError_BodyTooLong.patch |   17 ++\n .../jgit/patch/testError_DisconnectedHunk.patch    |   30 ++++\n .../jgit/patch/testError_GarbageBetweenFiles.patch |   33 ++++\n .../patch/testError_GitBinaryNoForwardHunk.patch   |   10 +\n .../jgit/patch/testError_TruncatedNew.patch        |   15 ++\n .../jgit/patch/testError_TruncatedOld.patch        |   15 ++\n .../src/org/spearce/jgit/patch/FormatError.java    |   95 +++++++++++\n .../src/org/spearce/jgit/patch/HunkHeader.java     |   20 ++-\n .../src/org/spearce/jgit/patch/Patch.java          |   38 ++++-\n 11 files changed, 441 insertions(+), 10 deletions(-)\n create mode 100644 org.spearce.jgit.test/tst/org/spearce/jgit/patch/PatchErrorTest.java\n create mode 100644 org.spearce.jgit.test/tst/org/spearce/jgit/patch/testError_BodyTooLong.patch\n create mode 100644 org.spearce.jgit.test/tst/org/spearce/jgit/patch/testError_DisconnectedHunk.patch\n create mode 100644 org.spearce.jgit.test/tst/org/spearce/jgit/patch/testError_GarbageBetweenFiles.patch\n create mode 100644 org.spearce.jgit.test/tst/org/spearce/jgit/patch/testError_GitBinaryNoForwardHunk.patch\n create mode 100644 org.spearce.jgit.test/tst/org/spearce/jgit/patch/testError_TruncatedNew.patch\n create mode 100644 org.spearce.jgit.test/tst/org/spearce/jgit/patch/testError_TruncatedOld.patch\n create mode 100644 org.spearce.jgit/src/org/spearce/jgit/patch/FormatError.java\n\ndiff --git a/org.spearce.jgit.test/tst/org/spearce/jgit/patch/PatchErrorTest.java b/org.spearce.jgit.test/tst/org/spearce/jgit/patch/PatchErrorTest.java\nnew file mode 100644\nindex 0000000..3d7e6b2\n--- /dev/null\n+++ b/org.spearce.jgit.test/tst/org/spearce/jgit/patch/PatchErrorTest.java\n@@ -0,0 +1,174 @@\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.patch;\n+\n+import java.io.IOException;\n+import java.io.InputStream;\n+\n+import junit.framework.TestCase;\n+\n+public class PatchErrorTest extends TestCase {\n+\tpublic void testError_DisconnectedHunk() throws IOException {\n+\t\tfinal Patch p = parseTestPatchFile();\n+\t\tassertEquals(1, p.getFiles().size());\n+\t\t{\n+\t\t\tfinal FileHeader fh = p.getFiles().get(0);\n+\t\t\tassertEquals(\n+\t\t\t\t\t\"org.spearce.jgit/src/org/spearce/jgit/lib/RepositoryConfig.java\",\n+\t\t\t\t\tfh.getNewName());\n+\t\t\tassertEquals(1, fh.getHunks().size());\n+\t\t}\n+\n+\t\tassertEquals(1, p.getErrors().size());\n+\t\tfinal FormatError e = p.getErrors().get(0);\n+\t\tassertSame(FormatError.Severity.ERROR, e.getSeverity());\n+\t\tassertEquals(\"Hunk disconnected from file\", e.getMessage());\n+\t\tassertEquals(18, e.getOffset());\n+\t\tassertTrue(e.getLineText().startsWith(\"@@ -109,4 +109,11 @@ assert\"));\n+\t}\n+\n+\tpublic void testError_TruncatedOld() throws IOException {\n+\t\tfinal Patch p = parseTestPatchFile();\n+\t\tassertEquals(1, p.getFiles().size());\n+\t\tassertEquals(1, p.getErrors().size());\n+\n+\t\tfinal FormatError e = p.getErrors().get(0);\n+\t\tassertSame(FormatError.Severity.ERROR, e.getSeverity());\n+\t\tassertEquals(\"Truncated hunk, at least 1 old lines is missing\", e\n+\t\t\t\t.getMessage());\n+\t\tassertEquals(313, e.getOffset());\n+\t\tassertTrue(e.getLineText().startsWith(\"@@ -236,9 +236,9 @@ protected \"));\n+\t}\n+\n+\tpublic void testError_TruncatedNew() throws IOException {\n+\t\tfinal Patch p = parseTestPatchFile();\n+\t\tassertEquals(1, p.getFiles().size());\n+\t\tassertEquals(1, p.getErrors().size());\n+\n+\t\tfinal FormatError e = p.getErrors().get(0);\n+\t\tassertSame(FormatError.Severity.ERROR, e.getSeverity());\n+\t\tassertEquals(\"Truncated hunk, at least 1 new lines is missing\", e\n+\t\t\t\t.getMessage());\n+\t\tassertEquals(313, e.getOffset());\n+\t\tassertTrue(e.getLineText().startsWith(\"@@ -236,9 +236,9 @@ protected \"));\n+\t}\n+\n+\tpublic void testError_BodyTooLong() throws IOException {\n+\t\tfinal Patch p = parseTestPatchFile();\n+\t\tassertEquals(1, p.getFiles().size());\n+\t\tassertEquals(1, p.getErrors().size());\n+\n+\t\tfinal FormatError e = p.getErrors().get(0);\n+\t\tassertSame(FormatError.Severity.WARNING, e.getSeverity());\n+\t\tassertEquals(\"Hunk header 4:11 does not match body line count of 4:12\",\n+\t\t\t\te.getMessage());\n+\t\tassertEquals(349, e.getOffset());\n+\t\tassertTrue(e.getLineText().startsWith(\"@@ -109,4 +109,11 @@ assert\"));\n+\t}\n+\n+\tpublic void testError_GarbageBetweenFiles() throws IOException {\n+\t\tfinal Patch p = parseTestPatchFile();\n+\t\tassertEquals(2, p.getFiles().size());\n+\t\t{\n+\t\t\tfinal FileHeader fh = p.getFiles().get(0);\n+\t\t\tassertEquals(\n+\t\t\t\t\t\"org.spearce.jgit.test/tst/org/spearce/jgit/lib/RepositoryConfigTest.java\",\n+\t\t\t\t\tfh.getNewName());\n+\t\t\tassertEquals(1, fh.getHunks().size());\n+\t\t}\n+\t\t{\n+\t\t\tfinal FileHeader fh = p.getFiles().get(1);\n+\t\t\tassertEquals(\n+\t\t\t\t\t\"org.spearce.jgit/src/org/spearce/jgit/lib/RepositoryConfig.java\",\n+\t\t\t\t\tfh.getNewName());\n+\t\t\tassertEquals(1, fh.getHunks().size());\n+\t\t}\n+\n+\t\tassertEquals(1, p.getErrors().size());\n+\t\tfinal FormatError e = p.getErrors().get(0);\n+\t\tassertSame(FormatError.Severity.WARNING, e.getSeverity());\n+\t\tassertEquals(\"Unexpected hunk trailer\", e.getMessage());\n+\t\tassertEquals(926, e.getOffset());\n+\t\tassertEquals(\"I AM NOT HERE\\n\", e.getLineText());\n+\t}\n+\n+\tpublic void testError_GitBinaryNoForwardHunk() throws IOException {\n+\t\tfinal Patch p = parseTestPatchFile();\n+\t\tassertEquals(2, p.getFiles().size());\n+\t\t{\n+\t\t\tfinal FileHeader fh = p.getFiles().get(0);\n+\t\t\tassertEquals(\"org.spearce.egit.ui/icons/toolbar/fetchd.png\", fh\n+\t\t\t\t\t.getNewName());\n+\t\t\tassertSame(FileHeader.PatchType.GIT_BINARY, fh.getPatchType());\n+\t\t\tassertTrue(fh.getHunks().isEmpty());\n+\t\t\tassertNull(fh.getForwardBinaryHunk());\n+\t\t}\n+\t\t{\n+\t\t\tfinal FileHeader fh = p.getFiles().get(1);\n+\t\t\tassertEquals(\"org.spearce.egit.ui/icons/toolbar/fetche.png\", fh\n+\t\t\t\t\t.getNewName());\n+\t\t\tassertSame(FileHeader.PatchType.UNIFIED, fh.getPatchType());\n+\t\t\tassertTrue(fh.getHunks().isEmpty());\n+\t\t\tassertNull(fh.getForwardBinaryHunk());\n+\t\t}\n+\n+\t\tassertEquals(1, p.getErrors().size());\n+\t\tfinal FormatError e = p.getErrors().get(0);\n+\t\tassertSame(FormatError.Severity.ERROR, e.getSeverity());\n+\t\tassertEquals(\"Missing forward-image in GIT binary patch\", e\n+\t\t\t\t.getMessage());\n+\t\tassertEquals(297, e.getOffset());\n+\t\tassertEquals(\"\\n\", e.getLineText());\n+\t}\n+\n+\tprivate Patch parseTestPatchFile() throws IOException {\n+\t\tfinal String patchFile = getName() + \".patch\";\n+\t\tfinal InputStream in = getClass().getResourceAsStream(patchFile);\n+\t\tif (in == null) {\n+\t\t\tfail(\"No \" + patchFile + \" test vector\");\n+\t\t\treturn null; // Never happens\n+\t\t}\n+\t\ttry {\n+\t\t\tfinal Patch p = new Patch();\n+\t\t\tp.parse(in);\n+\t\t\treturn p;\n+\t\t} finally {\n+\t\t\tin.close();\n+\t\t}\n+\t}\n+}\ndiff --git a/org.spearce.jgit.test/tst/org/spearce/jgit/patch/PatchTest.java b/org.spearce.jgit.test/tst/org/spearce/jgit/patch/PatchTest.java\nindex 453d88e..7c69fff 100644\n--- a/org.spearce.jgit.test/tst/org/spearce/jgit/patch/PatchTest.java\n+++ b/org.spearce.jgit.test/tst/org/spearce/jgit/patch/PatchTest.java\n@@ -49,11 +49,13 @@\n \tpublic void testEmpty() {\n \t\tfinal Patch p = new Patch();\n \t\tassertTrue(p.getFiles().isEmpty());\n+\t\tassertTrue(p.getErrors().isEmpty());\n \t}\n \n \tpublic void testParse_ConfigCaseInsensitive() throws IOException {\n \t\tfinal Patch p = parseTestPatchFile();\n \t\tassertEquals(2, p.getFiles().size());\n+\t\tassertTrue(p.getErrors().isEmpty());\n \n \t\tfinal FileHeader fRepositoryConfigTest = p.getFiles().get(0);\n \t\tfinal FileHeader fRepositoryConfig = p.getFiles().get(1);\n@@ -145,6 +147,7 @@ assertSame(FileHeader.PatchType.UNIFIED, fRepositoryConfig\n \tpublic void testParse_NoBinary() throws IOException {\n \t\tfinal Patch p = parseTestPatchFile();\n \t\tassertEquals(5, p.getFiles().size());\n+\t\tassertTrue(p.getErrors().isEmpty());\n \n \t\tfor (int i = 0; i < 4; i++) {\n \t\t\tfinal FileHeader fh = p.getFiles().get(i);\n@@ -179,6 +182,7 @@ public void testParse_GitBinary() throws IOException {\n \t\tfinal Patch p = parseTestPatchFile();\n \t\tfinal int[] binsizes = { 359, 393, 372, 404 };\n \t\tassertEquals(5, p.getFiles().size());\n+\t\tassertTrue(p.getErrors().isEmpty());\n \n \t\tfor (int i = 0; i < 4; i++) {\n \t\t\tfinal FileHeader fh = p.getFiles().get(i);\ndiff --git a/org.spearce.jgit.test/tst/org/spearce/jgit/patch/testError_BodyTooLong.patch b/org.spearce.jgit.test/tst/org/spearce/jgit/patch/testError_BodyTooLong.patch\nnew file mode 100644\nindex 0000000..1d0b1c4\n--- /dev/null\n+++ b/org.spearce.jgit.test/tst/org/spearce/jgit/patch/testError_BodyTooLong.patch\n@@ -0,0 +1,17 @@\n+diff --git a/org.spearce.jgit.test/tst/org/spearce/jgit/lib/RepositoryConfigTest.java b/org.spearce.jgit.test/tst/org/spearce/jgit/lib/RepositoryConfigTest.java\n+index da7e704..34ce04a 100644\n+--- a/org.spearce.jgit.test/tst/org/spearce/jgit/lib/RepositoryConfigTest.java\n++++ b/org.spearce.jgit.test/tst/org/spearce/jgit/lib/RepositoryConfigTest.java\n+@@ -109,4 +109,11 @@ assertTrue(Arrays.equals(values.toArray(), repositoryConfig\n+        .getStringList(\"my\", null, \"somename\")));\n+    checkFile(cfgFile, \"[my]\\n\\tsomename = value1\\n\\tsomename = value2\\n\");\n+  }\n++\n++ public void test006_readCaseInsensitive() throws IOException {\n++   final File path = writeTrashFile(\"config_001\", \"[Foo]\\nBar\\n\");\n++   RepositoryConfig repositoryConfig = new RepositoryConfig(null, path);\n++BAD LINE\n++   assertEquals(true, repositoryConfig.getBoolean(\"foo\", null, \"bar\", false));\n++   assertEquals(\"\", repositoryConfig.getString(\"foo\", null, \"bar\"));\n++ }\n+ }\ndiff --git a/org.spearce.jgit.test/tst/org/spearce/jgit/patch/testError_DisconnectedHunk.patch b/org.spearce.jgit.test/tst/org/spearce/jgit/patch/testError_DisconnectedHunk.patch\nnew file mode 100644\nindex 0000000..4762928\n--- /dev/null\n+++ b/org.spearce.jgit.test/tst/org/spearce/jgit/patch/testError_DisconnectedHunk.patch\n@@ -0,0 +1,30 @@\n+From: A. U. Thor\n+\n+@@ -109,4 +109,11 @@ assertTrue(Arrays.equals(values.toArray(), repositoryConfig\n+        .getStringList(\"my\", null, \"somename\")));\n+    checkFile(cfgFile, \"[my]\\n\\tsomename = value1\\n\\tsomename = value2\\n\");\n+  }\n++\n++ public void test006_readCaseInsensitive() throws IOException {\n++   final File path = writeTrashFile(\"config_001\", \"[Foo]\\nBar\\n\");\n++   RepositoryConfig repositoryConfig = new RepositoryConfig(null, path);\n++   assertEquals(true, repositoryConfig.getBoolean(\"foo\", null, \"bar\", false));\n++   assertEquals(\"\", repositoryConfig.getString(\"foo\", null, \"bar\"));\n++ }\n+ }\n+diff --git a/org.spearce.jgit/src/org/spearce/jgit/lib/RepositoryConfig.java b/org.spearce.jgit/src/org/spearce/jgit/lib/RepositoryConfig.java\n+index 45c2f8a..3291bba 100644\n+--- a/org.spearce.jgit/src/org/spearce/jgit/lib/RepositoryConfig.java\n++++ b/org.spearce.jgit/src/org/spearce/jgit/lib/RepositoryConfig.java\n+@@ -236,9 +236,9 @@ protected boolean getBoolean(final String section, String subsection,\n+      return defaultValue;\n+ \n+    n = n.toLowerCase();\n+-   if (MAGIC_EMPTY_VALUE.equals(n) || \"yes\".equals(n) || \"true\".equals(n) || \"1\".equals(n)) {\n++   if (MAGIC_EMPTY_VALUE.equals(n) || \"yes\".equalsIgnoreCase(n) || \"true\".equalsIgnoreCase(n) || \"1\".equals(n)) {\n+      return true;\n+-   } else if (\"no\".equals(n) || \"false\".equals(n) || \"0\".equals(n)) {\n++   } else if (\"no\".equalsIgnoreCase(n) || \"false\".equalsIgnoreCase(n) || \"0\".equalsIgnoreCase(n)) {\n+      return false;\n+    } else {\n+      throw new IllegalArgumentException(\"Invalid boolean value: \"\ndiff --git a/org.spearce.jgit.test/tst/org/spearce/jgit/patch/testError_GarbageBetweenFiles.patch b/org.spearce.jgit.test/tst/org/spearce/jgit/patch/testError_GarbageBetweenFiles.patch\nnew file mode 100644\nindex 0000000..163357e\n--- /dev/null\n+++ b/org.spearce.jgit.test/tst/org/spearce/jgit/patch/testError_GarbageBetweenFiles.patch\n@@ -0,0 +1,33 @@\n+diff --git a/org.spearce.jgit.test/tst/org/spearce/jgit/lib/RepositoryConfigTest.java b/org.spearce.jgit.test/tst/org/spearce/jgit/lib/RepositoryConfigTest.java\n+index da7e704..34ce04a 100644\n+--- a/org.spearce.jgit.test/tst/org/spearce/jgit/lib/RepositoryConfigTest.java\n++++ b/org.spearce.jgit.test/tst/org/spearce/jgit/lib/RepositoryConfigTest.java\n+@@ -109,4 +109,11 @@ assertTrue(Arrays.equals(values.toArray(), repositoryConfig\n+        .getStringList(\"my\", null, \"somename\")));\n+    checkFile(cfgFile, \"[my]\\n\\tsomename = value1\\n\\tsomename = value2\\n\");\n+  }\n++\n++ public void test006_readCaseInsensitive() throws IOException {\n++   final File path = writeTrashFile(\"config_001\", \"[Foo]\\nBar\\n\");\n++   RepositoryConfig repositoryConfig = new RepositoryConfig(null, path);\n++   assertEquals(true, repositoryConfig.getBoolean(\"foo\", null, \"bar\", false));\n++   assertEquals(\"\", repositoryConfig.getString(\"foo\", null, \"bar\"));\n++ }\n+ }\n+I AM NOT HERE\n+diff --git a/org.spearce.jgit/src/org/spearce/jgit/lib/RepositoryConfig.java b/org.spearce.jgit/src/org/spearce/jgit/lib/RepositoryConfig.java\n+index 45c2f8a..3291bba 100644\n+--- a/org.spearce.jgit/src/org/spearce/jgit/lib/RepositoryConfig.java\n++++ b/org.spearce.jgit/src/org/spearce/jgit/lib/RepositoryConfig.java\n+@@ -236,9 +236,9 @@ protected boolean getBoolean(final String section, String subsection,\n+      return defaultValue;\n+ \n+    n = n.toLowerCase();\n+-   if (MAGIC_EMPTY_VALUE.equals(n) || \"yes\".equals(n) || \"true\".equals(n) || \"1\".equals(n)) {\n++   if (MAGIC_EMPTY_VALUE.equals(n) || \"yes\".equalsIgnoreCase(n) || \"true\".equalsIgnoreCase(n) || \"1\".equals(n)) {\n+      return true;\n+-   } else if (\"no\".equals(n) || \"false\".equals(n) || \"0\".equals(n)) {\n++   } else if (\"no\".equalsIgnoreCase(n) || \"false\".equalsIgnoreCase(n) || \"0\".equalsIgnoreCase(n)) {\n+      return false;\n+    } else {\n+      throw new IllegalArgumentException(\"Invalid boolean value: \"\ndiff --git a/org.spearce.jgit.test/tst/org/spearce/jgit/patch/testError_GitBinaryNoForwardHunk.patch b/org.spearce.jgit.test/tst/org/spearce/jgit/patch/testError_GitBinaryNoForwardHunk.patch\nnew file mode 100644\nindex 0000000..e3f3307\n--- /dev/null\n+++ b/org.spearce.jgit.test/tst/org/spearce/jgit/patch/testError_GitBinaryNoForwardHunk.patch\n@@ -0,0 +1,10 @@\n+ create mode 100644 org.spearce.egit.ui/icons/toolbar/pushe.png\n+\n+diff --git a/org.spearce.egit.ui/icons/toolbar/fetchd.png b/org.spearce.egit.ui/icons/toolbar/fetchd.png\n+new file mode 100644\n+index 0000000000000000000000000000000000000000..4433c543f2a52b586a3ed5e31b138244107bc239\n+GIT binary patch\n+\n+diff --git a/org.spearce.egit.ui/icons/toolbar/fetche.png b/org.spearce.egit.ui/icons/toolbar/fetche.png\n+new file mode 100644\n+index 0000000000000000000000000000000000000000..0ffeb419e6ab302caa5e58661854b33853dc43dc\ndiff --git a/org.spearce.jgit.test/tst/org/spearce/jgit/patch/testError_TruncatedNew.patch b/org.spearce.jgit.test/tst/org/spearce/jgit/patch/testError_TruncatedNew.patch\nnew file mode 100644\nindex 0000000..6bbb73d\n--- /dev/null\n+++ b/org.spearce.jgit.test/tst/org/spearce/jgit/patch/testError_TruncatedNew.patch\n@@ -0,0 +1,15 @@\n+diff --git a/org.spearce.jgit/src/org/spearce/jgit/lib/RepositoryConfig.java b/org.spearce.jgit/src/org/spearce/jgit/lib/RepositoryConfig.java\n+index 45c2f8a..3291bba 100644\n+--- a/org.spearce.jgit/src/org/spearce/jgit/lib/RepositoryConfig.java\n++++ b/org.spearce.jgit/src/org/spearce/jgit/lib/RepositoryConfig.java\n+@@ -236,9 +236,9 @@ protected boolean getBoolean(final String section, String subsection,\n+      return defaultValue;\n+ \n+    n = n.toLowerCase();\n+-   if (MAGIC_EMPTY_VALUE.equals(n) || \"yes\".equals(n) || \"true\".equals(n) || \"1\".equals(n)) {\n+      return true;\n+-   } else if (\"no\".equals(n) || \"false\".equals(n) || \"0\".equals(n)) {\n++   } else if (\"no\".equalsIgnoreCase(n) || \"false\".equalsIgnoreCase(n) || \"0\".equalsIgnoreCase(n)) {\n+      return false;\n+    } else {\n+      throw new IllegalArgumentException(\"Invalid boolean value: \"\ndiff --git a/org.spearce.jgit.test/tst/org/spearce/jgit/patch/testError_TruncatedOld.patch b/org.spearce.jgit.test/tst/org/spearce/jgit/patch/testError_TruncatedOld.patch\nnew file mode 100644\nindex 0000000..c8fbdc1\n--- /dev/null\n+++ b/org.spearce.jgit.test/tst/org/spearce/jgit/patch/testError_TruncatedOld.patch\n@@ -0,0 +1,15 @@\n+diff --git a/org.spearce.jgit/src/org/spearce/jgit/lib/RepositoryConfig.java b/org.spearce.jgit/src/org/spearce/jgit/lib/RepositoryConfig.java\n+index 45c2f8a..3291bba 100644\n+--- a/org.spearce.jgit/src/org/spearce/jgit/lib/RepositoryConfig.java\n++++ b/org.spearce.jgit/src/org/spearce/jgit/lib/RepositoryConfig.java\n+@@ -236,9 +236,9 @@ protected boolean getBoolean(final String section, String subsection,\n+      return defaultValue;\n+ \n+    n = n.toLowerCase();\n+-   if (MAGIC_EMPTY_VALUE.equals(n) || \"yes\".equals(n) || \"true\".equals(n) || \"1\".equals(n)) {\n++   if (MAGIC_EMPTY_VALUE.equals(n) || \"yes\".equalsIgnoreCase(n) || \"true\".equalsIgnoreCase(n) || \"1\".equals(n)) {\n+      return true;\n++   } else if (\"no\".equalsIgnoreCase(n) || \"false\".equalsIgnoreCase(n) || \"0\".equalsIgnoreCase(n)) {\n+      return false;\n+    } else {\n+      throw new IllegalArgumentException(\"Invalid boolean value: \"\ndiff --git a/org.spearce.jgit/src/org/spearce/jgit/patch/FormatError.java b/org.spearce.jgit/src/org/spearce/jgit/patch/FormatError.java\nnew file mode 100644\nindex 0000000..e6f0a03\n--- /dev/null\n+++ b/org.spearce.jgit/src/org/spearce/jgit/patch/FormatError.java\n@@ -0,0 +1,95 @@\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.patch;\n+\n+import org.spearce.jgit.lib.Constants;\n+import org.spearce.jgit.util.RawParseUtils;\n+\n+/** An error in a patch script */\n+public class FormatError {\n+\t/** Classification of an error. */\n+\tpublic static enum Severity {\n+\t\t/** The error is unexpected, but can be worked around. */\n+\t\tWARNING,\n+\n+\t\t/** The error indicates the script is severely flawed. */\n+\t\tERROR;\n+\t}\n+\n+\tprivate final byte[] buf;\n+\n+\tprivate final int offset;\n+\n+\tprivate final Severity severity;\n+\n+\tprivate final String message;\n+\n+\tFormatError(final byte[] buffer, final int ptr, final Severity sev,\n+\t\t\tfinal String msg) {\n+\t\tbuf = buffer;\n+\t\toffset = ptr;\n+\t\tseverity = sev;\n+\t\tmessage = msg;\n+\t}\n+\n+\t/** @return the severity of the error. */\n+\tpublic Severity getSeverity() {\n+\t\treturn severity;\n+\t}\n+\n+\t/** @return a message describing the error. */\n+\tpublic String getMessage() {\n+\t\treturn message;\n+\t}\n+\n+\t/** @return the byte buffer holding the patch script. */\n+\tpublic byte[] getBuffer() {\n+\t\treturn buf;\n+\t}\n+\n+\t/** @return byte offset within {@link #getBuffer()} where the error is */\n+\tpublic int getOffset() {\n+\t\treturn offset;\n+\t}\n+\n+\t/** @return line of the patch script the error appears on. */\n+\tpublic String getLineText() {\n+\t\tfinal int eol = RawParseUtils.nextLF(buf, offset);\n+\t\treturn RawParseUtils.decode(Constants.CHARSET, buf, offset, eol);\n+\t}\n+}\ndiff --git a/org.spearce.jgit/src/org/spearce/jgit/patch/HunkHeader.java b/org.spearce.jgit/src/org/spearce/jgit/patch/HunkHeader.java\nindex fc606c3..20dd6e2 100644\n--- a/org.spearce.jgit/src/org/spearce/jgit/patch/HunkHeader.java\n+++ b/org.spearce.jgit/src/org/spearce/jgit/patch/HunkHeader.java\n@@ -132,7 +132,7 @@ void parseHeader() {\n \t\tnewLineCount = parseBase10(buf, ptr.value + 1, ptr);\n \t}\n \n-\tint parseBody() {\n+\tint parseBody(final Patch script) {\n \t\tfinal byte[] buf = file.buf;\n \t\tfinal int sz = buf.length;\n \t\tint c = nextLF(buf, startOffset), last = c;\n@@ -175,9 +175,21 @@ int parseBody() {\n \t\t\treturn last;\n \t\t}\n \n-\t\tif (nContext + nDeleted != oldLineCount\n-\t\t\t\t|| nContext + nAdded != newLineCount) {\n-\t\t\t// TODO report on truncated hunk\n+\t\tif (nContext + nDeleted < oldLineCount) {\n+\t\t\tfinal int missingCount = oldLineCount - (nContext + nDeleted);\n+\t\t\tscript.error(buf, startOffset, \"Truncated hunk, at least \"\n+\t\t\t\t\t+ missingCount + \" old lines is missing\");\n+\n+\t\t} else if (nContext + nAdded < newLineCount) {\n+\t\t\tfinal int missingCount = newLineCount - (nContext + nAdded);\n+\t\t\tscript.error(buf, startOffset, \"Truncated hunk, at least \"\n+\t\t\t\t\t+ missingCount + \" new lines is missing\");\n+\n+\t\t} else if (nContext + nDeleted > oldLineCount\n+\t\t\t\t|| nContext + nAdded > newLineCount) {\n+\t\t\tscript.warn(buf, startOffset, \"Hunk header \" + oldLineCount + \":\"\n+\t\t\t\t\t+ newLineCount + \" does not match body line count of \"\n+\t\t\t\t\t+ (nContext + nDeleted) + \":\" + (nContext + nAdded));\n \t\t}\n \n \t\treturn c;\ndiff --git a/org.spearce.jgit/src/org/spearce/jgit/patch/Patch.java b/org.spearce.jgit/src/org/spearce/jgit/patch/Patch.java\nindex 56eb327..5cc208c 100644\n--- a/org.spearce.jgit/src/org/spearce/jgit/patch/Patch.java\n+++ b/org.spearce.jgit/src/org/spearce/jgit/patch/Patch.java\n@@ -69,9 +69,13 @@\n \t/** The files, in the order they were parsed out of the input. */\n \tprivate final List<FileHeader> files;\n \n+\t/** Formatting errors, if any were identified. */\n+\tprivate final List<FormatError> errors;\n+\n \t/** Create an empty patch. */\n \tpublic Patch() {\n \t\tfiles = new ArrayList<FileHeader>();\n+\t\terrors = new ArrayList<FormatError>(0);\n \t}\n \n \t/**\n@@ -93,6 +97,21 @@ public void addFile(final FileHeader fh) {\n \t}\n \n \t/**\n+\t * Add a formatting error to this patch script.\n+\t * \n+\t * @param err\n+\t *            the error description.\n+\t */\n+\tpublic void addError(final FormatError err) {\n+\t\terrors.add(err);\n+\t}\n+\n+\t/** @return collection of formatting errors, if any. */\n+\tpublic List<FormatError> getErrors() {\n+\t\treturn errors;\n+\t}\n+\n+\t/**\n \t * Parse a patch received from an InputStream.\n \t * <p>\n \t * Multiple parse calls on the same instance will concatenate the patch\n@@ -149,8 +168,7 @@ private int parseFile(final byte[] buf, int c) {\n \t\t\t\t// have missed a file header previously. The hunk\n \t\t\t\t// isn't valid without knowing where it comes from.\n \t\t\t\t//\n-\n-\t\t\t\t// TODO handle a disconnected hunk fragment\n+\t\t\t\terror(buf, c, \"Hunk disconnected from file\");\n \t\t\t\tc = nextLF(buf, c);\n \t\t\t\tcontinue;\n \t\t\t}\n@@ -218,6 +236,7 @@ private int parseDiffCC(final byte[] buf, final int startOffset) {\n \n \t\t// TODO Support parsing diff --cc headers\n \t\t// TODO parse diff --cc hunks\n+\t\twarn(buf, startOffset, \"diff --cc format not supported\");\n \t\tfh.endOffset = ptr;\n \t\taddFile(fh);\n \t\treturn ptr;\n@@ -259,12 +278,12 @@ private int parseHunks(final FileHeader fh, int c) {\n \t\t\tif (match(buf, c, HUNK_HDR) >= 0) {\n \t\t\t\tfinal HunkHeader h = new HunkHeader(fh, c);\n \t\t\t\th.parseHeader();\n-\t\t\t\tc = h.parseBody();\n+\t\t\t\tc = h.parseBody(this);\n \t\t\t\th.endOffset = c;\n \t\t\t\tfh.addHunk(h);\n \t\t\t\tif (c < sz && buf[c] != '@' && buf[c] != 'd'\n \t\t\t\t\t\t&& match(buf, c, SIG_FOOTER) < 0) {\n-\t\t\t\t\t// TODO report on noise between hunks, might be an error\n+\t\t\t\t\twarn(buf, c, \"Unexpected hunk trailer\");\n \t\t\t\t}\n \t\t\t\tcontinue;\n \t\t\t}\n@@ -308,8 +327,7 @@ private int parseGitBinary(final FileHeader fh, int c) {\n \t\tif (nEnd < 0) {\n \t\t\t// Not a binary hunk.\n \t\t\t//\n-\n-\t\t\t// TODO handle invalid binary hunks\n+\t\t\terror(fh.buf, c, \"Missing forward-image in GIT binary patch\");\n \t\t\treturn c;\n \t\t}\n \t\tc = nEnd;\n@@ -327,6 +345,14 @@ private int parseGitBinary(final FileHeader fh, int c) {\n \t\treturn c;\n \t}\n \n+\tvoid warn(final byte[] buf, final int ptr, final String msg) {\n+\t\taddError(new FormatError(buf, ptr, FormatError.Severity.WARNING, msg));\n+\t}\n+\n+\tvoid error(final byte[] buf, final int ptr, final String msg) {\n+\t\taddError(new FormatError(buf, ptr, FormatError.Severity.ERROR, msg));\n+\t}\n+\n \tprivate static boolean matchAny(final byte[] buf, final int c,\n \t\t\tfinal byte[][] srcs) {\n \t\tfor (final byte[] s : srcs) {\n-- \n1.6.1.rc2.306.ge5d5e\n"},{"id":"97671","messageId":"1229049981-14152-12-git-send-email-spearce@spearce.org","threadId":"16690","inReplyTo":"1229049981-14152-11-git-send-email-spearce@spearce.org","subject":"[JGIT PATCH 11/15] Fix Patch.parse to honor the end point passed in","fromName":"Shawn O. Pearce","fromEmail":"spearce@spearce.org","sentAt":"2008-12-12T02:46:17Z","receivedAt":"2008-12-12T02:46:17Z","isPatch":true,"sender":{"key":"spearce@spearce.org","avatar":"https://avatars.githubusercontent.com/u/34844?v=4"},"body":"Otherwise we may over-read the patch script and identify trailer\ndata as part of a patch when it was requested that we ignore it.\n\nSigned-off-by: Shawn O. Pearce <spearce@spearce.org>\n---\n .../tst/org/spearce/jgit/patch/FileHeaderTest.java |   46 +++++++-----\n .../src/org/spearce/jgit/patch/BinaryHunk.java     |    5 +-\n .../src/org/spearce/jgit/patch/FileHeader.java     |   16 ++--\n .../src/org/spearce/jgit/patch/HunkHeader.java     |    9 +--\n .../src/org/spearce/jgit/patch/Patch.java          |   73 ++++++++++----------\n 5 files changed, 77 insertions(+), 72 deletions(-)\n\ndiff --git a/org.spearce.jgit.test/tst/org/spearce/jgit/patch/FileHeaderTest.java b/org.spearce.jgit.test/tst/org/spearce/jgit/patch/FileHeaderTest.java\nindex 4c2140a..36c528e 100644\n--- a/org.spearce.jgit.test/tst/org/spearce/jgit/patch/FileHeaderTest.java\n+++ b/org.spearce.jgit.test/tst/org/spearce/jgit/patch/FileHeaderTest.java\n@@ -45,28 +45,32 @@\n public class FileHeaderTest extends TestCase {\n \tpublic void testParseGitFileName_Empty() {\n \t\tfinal FileHeader fh = data(\"\");\n-\t\tassertEquals(-1, fh.parseGitFileName(0));\n+\t\tassertEquals(-1, fh.parseGitFileName(0, fh.buf.length));\n \t\tassertNotNull(fh.getHunks());\n \t\tassertTrue(fh.getHunks().isEmpty());\n \t\tassertFalse(fh.hasMetaDataChanges());\n \t}\n \n \tpublic void testParseGitFileName_NoLF() {\n-\t\tassertEquals(-1, data(\"a/ b/\").parseGitFileName(0));\n+\t\tfinal FileHeader fh = data(\"a/ b/\");\n+\t\tassertEquals(-1, fh.parseGitFileName(0, fh.buf.length));\n \t}\n \n \tpublic void testParseGitFileName_NoSecondLine() {\n-\t\tassertEquals(-1, data(\"\\n\").parseGitFileName(0));\n+\t\tfinal FileHeader fh = data(\"\\n\");\n+\t\tassertEquals(-1, fh.parseGitFileName(0, fh.buf.length));\n \t}\n \n \tpublic void testParseGitFileName_EmptyHeader() {\n-\t\tassertEquals(1, data(\"\\n\\n\").parseGitFileName(0));\n+\t\tfinal FileHeader fh = data(\"\\n\\n\");\n+\t\tassertEquals(1, fh.parseGitFileName(0, fh.buf.length));\n \t}\n \n \tpublic void testParseGitFileName_Foo() {\n \t\tfinal String name = \"foo\";\n \t\tfinal FileHeader fh = header(name);\n-\t\tassertEquals(gitLine(name).length(), fh.parseGitFileName(0));\n+\t\tassertEquals(gitLine(name).length(), fh.parseGitFileName(0,\n+\t\t\t\tfh.buf.length));\n \t\tassertEquals(name, fh.getOldName());\n \t\tassertSame(fh.getOldName(), fh.getNewName());\n \t\tassertFalse(fh.hasMetaDataChanges());\n@@ -74,7 +78,7 @@ public void testParseGitFileName_Foo() {\n \n \tpublic void testParseGitFileName_FailFooBar() {\n \t\tfinal FileHeader fh = data(\"a/foo b/bar\\n-\");\n-\t\tassertTrue(fh.parseGitFileName(0) > 0);\n+\t\tassertTrue(fh.parseGitFileName(0, fh.buf.length) > 0);\n \t\tassertNull(fh.getOldName());\n \t\tassertNull(fh.getNewName());\n \t\tassertFalse(fh.hasMetaDataChanges());\n@@ -83,7 +87,8 @@ public void testParseGitFileName_FailFooBar() {\n \tpublic void testParseGitFileName_FooSpBar() {\n \t\tfinal String name = \"foo bar\";\n \t\tfinal FileHeader fh = header(name);\n-\t\tassertEquals(gitLine(name).length(), fh.parseGitFileName(0));\n+\t\tassertEquals(gitLine(name).length(), fh.parseGitFileName(0,\n+\t\t\t\tfh.buf.length));\n \t\tassertEquals(name, fh.getOldName());\n \t\tassertSame(fh.getOldName(), fh.getNewName());\n \t\tassertFalse(fh.hasMetaDataChanges());\n@@ -93,7 +98,8 @@ public void testParseGitFileName_DqFooTabBar() {\n \t\tfinal String name = \"foo\\tbar\";\n \t\tfinal String dqName = \"foo\\\\tbar\";\n \t\tfinal FileHeader fh = dqHeader(dqName);\n-\t\tassertEquals(dqGitLine(dqName).length(), fh.parseGitFileName(0));\n+\t\tassertEquals(dqGitLine(dqName).length(), fh.parseGitFileName(0,\n+\t\t\t\tfh.buf.length));\n \t\tassertEquals(name, fh.getOldName());\n \t\tassertSame(fh.getOldName(), fh.getNewName());\n \t\tassertFalse(fh.hasMetaDataChanges());\n@@ -103,7 +109,8 @@ public void testParseGitFileName_DqFooSpLfNulBar() {\n \t\tfinal String name = \"foo \\n\\0bar\";\n \t\tfinal String dqName = \"foo \\\\n\\\\0bar\";\n \t\tfinal FileHeader fh = dqHeader(dqName);\n-\t\tassertEquals(dqGitLine(dqName).length(), fh.parseGitFileName(0));\n+\t\tassertEquals(dqGitLine(dqName).length(), fh.parseGitFileName(0,\n+\t\t\t\tfh.buf.length));\n \t\tassertEquals(name, fh.getOldName());\n \t\tassertSame(fh.getOldName(), fh.getNewName());\n \t\tassertFalse(fh.hasMetaDataChanges());\n@@ -112,7 +119,8 @@ public void testParseGitFileName_DqFooSpLfNulBar() {\n \tpublic void testParseGitFileName_SrcFooC() {\n \t\tfinal String name = \"src/foo/bar/argh/code.c\";\n \t\tfinal FileHeader fh = header(name);\n-\t\tassertEquals(gitLine(name).length(), fh.parseGitFileName(0));\n+\t\tassertEquals(gitLine(name).length(), fh.parseGitFileName(0,\n+\t\t\t\tfh.buf.length));\n \t\tassertEquals(name, fh.getOldName());\n \t\tassertSame(fh.getOldName(), fh.getNewName());\n \t\tassertFalse(fh.hasMetaDataChanges());\n@@ -122,7 +130,7 @@ public void testParseGitFileName_SrcFooCNonStandardPrefix() {\n \t\tfinal String name = \"src/foo/bar/argh/code.c\";\n \t\tfinal String header = \"project-v-1.0/\" + name + \" mydev/\" + name + \"\\n\";\n \t\tfinal FileHeader fh = data(header + \"-\");\n-\t\tassertEquals(header.length(), fh.parseGitFileName(0));\n+\t\tassertEquals(header.length(), fh.parseGitFileName(0, fh.buf.length));\n \t\tassertEquals(name, fh.getOldName());\n \t\tassertSame(fh.getOldName(), fh.getNewName());\n \t\tassertFalse(fh.hasMetaDataChanges());\n@@ -202,12 +210,12 @@ public void testParseRename100_NewStyle() {\n \t\t\t\t+ \"similarity index 100%\\n\"\n \t\t\t\t+ \"rename from a\\n\"\n \t\t\t\t+ \"rename to \\\" c/\\\\303\\\\205ngstr\\\\303\\\\266m\\\"\\n\");\n-\t\tint ptr = fh.parseGitFileName(0);\n+\t\tint ptr = fh.parseGitFileName(0, fh.buf.length);\n \t\tassertTrue(ptr > 0);\n \t\tassertNull(fh.getOldName()); // can't parse names on a rename\n \t\tassertNull(fh.getNewName());\n \n-\t\tptr = fh.parseGitHeaders(ptr);\n+\t\tptr = fh.parseGitHeaders(ptr, fh.buf.length);\n \t\tassertTrue(ptr > 0);\n \n \t\tassertEquals(\"a\", fh.getOldName());\n@@ -231,12 +239,12 @@ public void testParseRename100_OldStyle() {\n \t\t\t\t+ \"similarity index 100%\\n\"\n \t\t\t\t+ \"rename old a\\n\"\n \t\t\t\t+ \"rename new \\\" c/\\\\303\\\\205ngstr\\\\303\\\\266m\\\"\\n\");\n-\t\tint ptr = fh.parseGitFileName(0);\n+\t\tint ptr = fh.parseGitFileName(0, fh.buf.length);\n \t\tassertTrue(ptr > 0);\n \t\tassertNull(fh.getOldName()); // can't parse names on a rename\n \t\tassertNull(fh.getNewName());\n \n-\t\tptr = fh.parseGitHeaders(ptr);\n+\t\tptr = fh.parseGitHeaders(ptr, fh.buf.length);\n \t\tassertTrue(ptr > 0);\n \n \t\tassertEquals(\"a\", fh.getOldName());\n@@ -260,12 +268,12 @@ public void testParseCopy100() {\n \t\t\t\t+ \"similarity index 100%\\n\"\n \t\t\t\t+ \"copy from a\\n\"\n \t\t\t\t+ \"copy to \\\" c/\\\\303\\\\205ngstr\\\\303\\\\266m\\\"\\n\");\n-\t\tint ptr = fh.parseGitFileName(0);\n+\t\tint ptr = fh.parseGitFileName(0, fh.buf.length);\n \t\tassertTrue(ptr > 0);\n \t\tassertNull(fh.getOldName()); // can't parse names on a copy\n \t\tassertNull(fh.getNewName());\n \n-\t\tptr = fh.parseGitHeaders(ptr);\n+\t\tptr = fh.parseGitHeaders(ptr, fh.buf.length);\n \t\tassertTrue(ptr > 0);\n \n \t\tassertEquals(\"a\", fh.getOldName());\n@@ -391,9 +399,9 @@ public void testParseAbbrIndexLine_NoMode() {\n \t}\n \n \tprivate static void assertParse(final FileHeader fh) {\n-\t\tint ptr = fh.parseGitFileName(0);\n+\t\tint ptr = fh.parseGitFileName(0, fh.buf.length);\n \t\tassertTrue(ptr > 0);\n-\t\tptr = fh.parseGitHeaders(ptr);\n+\t\tptr = fh.parseGitHeaders(ptr, fh.buf.length);\n \t\tassertTrue(ptr > 0);\n \t}\n \ndiff --git a/org.spearce.jgit/src/org/spearce/jgit/patch/BinaryHunk.java b/org.spearce.jgit/src/org/spearce/jgit/patch/BinaryHunk.java\nindex ce35286..3e07ec4 100644\n--- a/org.spearce.jgit/src/org/spearce/jgit/patch/BinaryHunk.java\n+++ b/org.spearce.jgit/src/org/spearce/jgit/patch/BinaryHunk.java\n@@ -91,7 +91,7 @@ public int getSize() {\n \t\treturn length;\n \t}\n \n-\tint parseHunk(int ptr) {\n+\tint parseHunk(int ptr, final int end) {\n \t\tfinal byte[] buf = file.buf;\n \n \t\tif (match(buf, ptr, LITERAL) >= 0) {\n@@ -115,8 +115,7 @@ int parseHunk(int ptr) {\n \t\t// encoded information in this hunk. To save time we don't do a\n \t\t// validation of the binary data at this point.\n \t\t//\n-\t\tfinal int sz = buf.length;\n-\t\twhile (ptr < sz) {\n+\t\twhile (ptr < end) {\n \t\t\tfinal boolean empty = buf[ptr] == '\\n';\n \t\t\tptr = nextLF(buf, ptr);\n \t\t\tif (empty)\ndiff --git a/org.spearce.jgit/src/org/spearce/jgit/patch/FileHeader.java b/org.spearce.jgit/src/org/spearce/jgit/patch/FileHeader.java\nindex a6ff4a6..a58e978 100644\n--- a/org.spearce.jgit/src/org/spearce/jgit/patch/FileHeader.java\n+++ b/org.spearce.jgit/src/org/spearce/jgit/patch/FileHeader.java\n@@ -291,12 +291,14 @@ public BinaryHunk getReverseBinaryHunk() {\n \t * \n \t * @param ptr\n \t *            first character after the \"diff --git \" or \"diff --cc \" part.\n+\t * @param end\n+\t *            one past the last position to parse.\n \t * @return first character after the LF at the end of the line; -1 on error.\n \t */\n-\tint parseGitFileName(int ptr) {\n+\tint parseGitFileName(int ptr, final int end) {\n \t\tfinal int eol = nextLF(buf, ptr);\n \t\tfinal int bol = ptr;\n-\t\tif (eol >= buf.length) {\n+\t\tif (eol >= end) {\n \t\t\treturn -1;\n \t\t}\n \n@@ -353,9 +355,8 @@ int parseGitFileName(int ptr) {\n \t\treturn eol;\n \t}\n \n-\tint parseGitHeaders(int ptr) {\n-\t\tfinal int sz = buf.length;\n-\t\twhile (ptr < sz) {\n+\tint parseGitHeaders(int ptr, final int end) {\n+\t\twhile (ptr < end) {\n \t\t\tfinal int eol = nextLF(buf, ptr);\n \t\t\tif (match(buf, ptr, HUNK_HDR) >= 0) {\n \t\t\t\t// First hunk header; break out and parse them later.\n@@ -428,9 +429,8 @@ int parseGitHeaders(int ptr) {\n \t\treturn ptr;\n \t}\n \n-\tint parseTraditionalHeaders(int ptr) {\n-\t\tfinal int sz = buf.length;\n-\t\twhile (ptr < sz) {\n+\tint parseTraditionalHeaders(int ptr, final int end) {\n+\t\twhile (ptr < end) {\n \t\t\tfinal int eol = nextLF(buf, ptr);\n \t\t\tif (match(buf, ptr, HUNK_HDR) >= 0) {\n \t\t\t\t// First hunk header; break out and parse them later.\ndiff --git a/org.spearce.jgit/src/org/spearce/jgit/patch/HunkHeader.java b/org.spearce.jgit/src/org/spearce/jgit/patch/HunkHeader.java\nindex 20dd6e2..4fd9bae 100644\n--- a/org.spearce.jgit/src/org/spearce/jgit/patch/HunkHeader.java\n+++ b/org.spearce.jgit/src/org/spearce/jgit/patch/HunkHeader.java\n@@ -119,7 +119,7 @@ public int getLinesAdded() {\n \t\treturn nAdded;\n \t}\n \n-\tvoid parseHeader() {\n+\tvoid parseHeader(final int end) {\n \t\t// Parse \"@@ -236,9 +236,9 @@ protected boolean\"\n \t\t//\n \t\tfinal byte[] buf = file.buf;\n@@ -132,15 +132,14 @@ void parseHeader() {\n \t\tnewLineCount = parseBase10(buf, ptr.value + 1, ptr);\n \t}\n \n-\tint parseBody(final Patch script) {\n+\tint parseBody(final Patch script, final int end) {\n \t\tfinal byte[] buf = file.buf;\n-\t\tfinal int sz = buf.length;\n \t\tint c = nextLF(buf, startOffset), last = c;\n \n \t\tnDeleted = 0;\n \t\tnAdded = 0;\n \n-\t\tSCAN: for (; c < sz; last = c, c = nextLF(buf, c)) {\n+\t\tSCAN: for (; c < end; last = c, c = nextLF(buf, c)) {\n \t\t\tswitch (buf[c]) {\n \t\t\tcase ' ':\n \t\t\tcase '\\n':\n@@ -163,7 +162,7 @@ int parseBody(final Patch script) {\n \t\t\t}\n \t\t}\n \n-\t\tif (last < sz && nContext + nDeleted - 1 == oldLineCount\n+\t\tif (last < end && nContext + nDeleted - 1 == oldLineCount\n \t\t\t\t&& nContext + nAdded == newLineCount\n \t\t\t\t&& match(buf, last, Patch.SIG_FOOTER) >= 0) {\n \t\t\t// This is an extremely common occurrence of \"corruption\".\ndiff --git a/org.spearce.jgit/src/org/spearce/jgit/patch/Patch.java b/org.spearce.jgit/src/org/spearce/jgit/patch/Patch.java\nindex 5cc208c..9aca22d 100644\n--- a/org.spearce.jgit/src/org/spearce/jgit/patch/Patch.java\n+++ b/org.spearce.jgit/src/org/spearce/jgit/patch/Patch.java\n@@ -157,12 +157,11 @@ public void parse(final InputStream is) throws IOException {\n \t */\n \tpublic void parse(final byte[] buf, int ptr, final int end) {\n \t\twhile (ptr < end)\n-\t\t\tptr = parseFile(buf, ptr);\n+\t\t\tptr = parseFile(buf, ptr, end);\n \t}\n \n-\tprivate int parseFile(final byte[] buf, int c) {\n-\t\tfinal int sz = buf.length;\n-\t\twhile (c < sz) {\n+\tprivate int parseFile(final byte[] buf, int c, final int end) {\n+\t\twhile (c < end) {\n \t\t\tif (match(buf, c, HUNK_HDR) >= 0) {\n \t\t\t\t// If we find a disconnected hunk header we might\n \t\t\t\t// have missed a file header previously. The hunk\n@@ -176,19 +175,19 @@ private int parseFile(final byte[] buf, int c) {\n \t\t\t// Valid git style patch?\n \t\t\t//\n \t\t\tif (match(buf, c, DIFF_GIT) >= 0)\n-\t\t\t\treturn parseDiffGit(buf, c);\n+\t\t\t\treturn parseDiffGit(buf, c, end);\n \t\t\tif (match(buf, c, DIFF_CC) >= 0)\n-\t\t\t\treturn parseDiffCC(buf, c);\n+\t\t\t\treturn parseDiffCC(buf, c, end);\n \n \t\t\t// Junk between files? Leading junk? Traditional\n \t\t\t// (non-git generated) patch?\n \t\t\t//\n \t\t\tfinal int n = nextLF(buf, c);\n-\t\t\tif (n >= sz) {\n+\t\t\tif (n >= end) {\n \t\t\t\t// Patches cannot be only one line long. This must be\n \t\t\t\t// trailing junk that we should ignore.\n \t\t\t\t//\n-\t\t\t\treturn sz;\n+\t\t\t\treturn end;\n \t\t\t}\n \n \t\t\tif (n - c < 6) {\n@@ -204,10 +203,10 @@ private int parseFile(final byte[] buf, int c) {\n \t\t\t\t// a \"@@ -0,0\" smelling line next. We only check the \"@@ -\".\n \t\t\t\t//\n \t\t\t\tfinal int f = nextLF(buf, n);\n-\t\t\t\tif (f >= sz)\n-\t\t\t\t\treturn sz;\n+\t\t\t\tif (f >= end)\n+\t\t\t\t\treturn end;\n \t\t\t\tif (match(buf, f, HUNK_HDR) >= 0)\n-\t\t\t\t\treturn parseTraditionalPatch(buf, c);\n+\t\t\t\t\treturn parseTraditionalPatch(buf, c, end);\n \t\t\t}\n \n \t\t\tc = n;\n@@ -215,53 +214,53 @@ private int parseFile(final byte[] buf, int c) {\n \t\treturn c;\n \t}\n \n-\tprivate int parseDiffGit(final byte[] buf, final int startOffset) {\n-\t\tfinal FileHeader fh = new FileHeader(buf, startOffset);\n-\t\tint ptr = fh.parseGitFileName(startOffset + DIFF_GIT.length);\n+\tprivate int parseDiffGit(final byte[] buf, final int start, final int end) {\n+\t\tfinal FileHeader fh = new FileHeader(buf, start);\n+\t\tint ptr = fh.parseGitFileName(start + DIFF_GIT.length, end);\n \t\tif (ptr < 0)\n-\t\t\treturn skipFile(buf, startOffset);\n+\t\t\treturn skipFile(buf, start, end);\n \n-\t\tptr = fh.parseGitHeaders(ptr);\n-\t\tptr = parseHunks(fh, ptr);\n+\t\tptr = fh.parseGitHeaders(ptr, end);\n+\t\tptr = parseHunks(fh, ptr, end);\n \t\tfh.endOffset = ptr;\n \t\taddFile(fh);\n \t\treturn ptr;\n \t}\n \n-\tprivate int parseDiffCC(final byte[] buf, final int startOffset) {\n-\t\tfinal FileHeader fh = new FileHeader(buf, startOffset);\n-\t\tint ptr = fh.parseGitFileName(startOffset + DIFF_CC.length);\n+\tprivate int parseDiffCC(final byte[] buf, final int start, final int end) {\n+\t\tfinal FileHeader fh = new FileHeader(buf, start);\n+\t\tint ptr = fh.parseGitFileName(start + DIFF_CC.length, end);\n \t\tif (ptr < 0)\n-\t\t\treturn skipFile(buf, startOffset);\n+\t\t\treturn skipFile(buf, start, end);\n \n \t\t// TODO Support parsing diff --cc headers\n \t\t// TODO parse diff --cc hunks\n-\t\twarn(buf, startOffset, \"diff --cc format not supported\");\n+\t\twarn(buf, start, \"diff --cc format not supported\");\n \t\tfh.endOffset = ptr;\n \t\taddFile(fh);\n \t\treturn ptr;\n \t}\n \n-\tprivate int parseTraditionalPatch(final byte[] buf, final int startOffset) {\n-\t\tfinal FileHeader fh = new FileHeader(buf, startOffset);\n-\t\tint ptr = fh.parseTraditionalHeaders(startOffset);\n-\t\tptr = parseHunks(fh, ptr);\n+\tprivate int parseTraditionalPatch(final byte[] buf, final int start,\n+\t\t\tfinal int end) {\n+\t\tfinal FileHeader fh = new FileHeader(buf, start);\n+\t\tint ptr = fh.parseTraditionalHeaders(start, end);\n+\t\tptr = parseHunks(fh, ptr, end);\n \t\tfh.endOffset = ptr;\n \t\taddFile(fh);\n \t\treturn ptr;\n \t}\n \n-\tprivate static int skipFile(final byte[] buf, int ptr) {\n+\tprivate static int skipFile(final byte[] buf, int ptr, final int end) {\n \t\tptr = nextLF(buf, ptr);\n \t\tif (match(buf, ptr, OLD_NAME) >= 0)\n \t\t\tptr = nextLF(buf, ptr);\n \t\treturn ptr;\n \t}\n \n-\tprivate int parseHunks(final FileHeader fh, int c) {\n+\tprivate int parseHunks(final FileHeader fh, int c, final int end) {\n \t\tfinal byte[] buf = fh.buf;\n-\t\tfinal int sz = buf.length;\n-\t\twhile (c < sz) {\n+\t\twhile (c < end) {\n \t\t\t// If we see a file header at this point, we have all of the\n \t\t\t// hunks for our current file. We should stop and report back\n \t\t\t// with this position so it can be parsed again later.\n@@ -277,11 +276,11 @@ private int parseHunks(final FileHeader fh, int c) {\n \n \t\t\tif (match(buf, c, HUNK_HDR) >= 0) {\n \t\t\t\tfinal HunkHeader h = new HunkHeader(fh, c);\n-\t\t\t\th.parseHeader();\n-\t\t\t\tc = h.parseBody(this);\n+\t\t\t\th.parseHeader(end);\n+\t\t\t\tc = h.parseBody(this, end);\n \t\t\t\th.endOffset = c;\n \t\t\t\tfh.addHunk(h);\n-\t\t\t\tif (c < sz && buf[c] != '@' && buf[c] != 'd'\n+\t\t\t\tif (c < end && buf[c] != '@' && buf[c] != 'd'\n \t\t\t\t\t\t&& match(buf, c, SIG_FOOTER) < 0) {\n \t\t\t\t\twarn(buf, c, \"Unexpected hunk trailer\");\n \t\t\t\t}\n@@ -291,7 +290,7 @@ private int parseHunks(final FileHeader fh, int c) {\n \t\t\tfinal int eol = nextLF(buf, c);\n \t\t\tif (fh.getHunks().isEmpty() && match(buf, c, GIT_BINARY) >= 0) {\n \t\t\t\tfh.patchType = FileHeader.PatchType.GIT_BINARY;\n-\t\t\t\treturn parseGitBinary(fh, eol);\n+\t\t\t\treturn parseGitBinary(fh, eol, end);\n \t\t\t}\n \n \t\t\tif (fh.getHunks().isEmpty() && BIN_TRAILER.length < eol - c\n@@ -321,9 +320,9 @@ private int parseHunks(final FileHeader fh, int c) {\n \t\treturn c;\n \t}\n \n-\tprivate int parseGitBinary(final FileHeader fh, int c) {\n+\tprivate int parseGitBinary(final FileHeader fh, int c, final int end) {\n \t\tfinal BinaryHunk postImage = new BinaryHunk(fh, c);\n-\t\tfinal int nEnd = postImage.parseHunk(c);\n+\t\tfinal int nEnd = postImage.parseHunk(c, end);\n \t\tif (nEnd < 0) {\n \t\t\t// Not a binary hunk.\n \t\t\t//\n@@ -335,7 +334,7 @@ private int parseGitBinary(final FileHeader fh, int c) {\n \t\tfh.forwardBinaryHunk = postImage;\n \n \t\tfinal BinaryHunk preImage = new BinaryHunk(fh, c);\n-\t\tfinal int oEnd = preImage.parseHunk(c);\n+\t\tfinal int oEnd = preImage.parseHunk(c, end);\n \t\tif (oEnd >= 0) {\n \t\t\tc = oEnd;\n \t\t\tpreImage.endOffset = c;\n-- \n1.6.1.rc2.306.ge5d5e\n"},{"id":"97669","messageId":"1229049981-14152-13-git-send-email-spearce@spearce.org","threadId":"16690","inReplyTo":"1229049981-14152-12-git-send-email-spearce@spearce.org","subject":"[JGIT PATCH 12/15] Correctly handle hunk headers such as \"@@ -0,0 +1 @@\"","fromName":"Shawn O. Pearce","fromEmail":"spearce@spearce.org","sentAt":"2008-12-12T02:46:18Z","receivedAt":"2008-12-12T02:46:18Z","isPatch":true,"sender":{"key":"spearce@spearce.org","avatar":"https://avatars.githubusercontent.com/u/34844?v=4"},"body":"Sometimes these are created for single line file creations.\n\nSigned-off-by: Shawn O. Pearce <spearce@spearce.org>\n---\n .../src/org/spearce/jgit/patch/HunkHeader.java     |   14 ++++++++++++--\n 1 files changed, 12 insertions(+), 2 deletions(-)\n\ndiff --git a/org.spearce.jgit/src/org/spearce/jgit/patch/HunkHeader.java b/org.spearce.jgit/src/org/spearce/jgit/patch/HunkHeader.java\nindex 4fd9bae..c3bd642 100644\n--- a/org.spearce.jgit/src/org/spearce/jgit/patch/HunkHeader.java\n+++ b/org.spearce.jgit/src/org/spearce/jgit/patch/HunkHeader.java\n@@ -126,10 +126,20 @@ void parseHeader(final int end) {\n \t\tfinal MutableInteger ptr = new MutableInteger();\n \t\tptr.value = nextLF(buf, startOffset, ' ');\n \t\toldStartLine = -parseBase10(buf, ptr.value, ptr);\n-\t\toldLineCount = parseBase10(buf, ptr.value + 1, ptr);\n+\t\tif (buf[ptr.value] == ',')\n+\t\t\toldLineCount = parseBase10(buf, ptr.value + 1, ptr);\n+\t\telse {\n+\t\t\toldLineCount = oldStartLine;\n+\t\t\toldStartLine = 0;\n+\t\t}\n \n \t\tnewStartLine = parseBase10(buf, ptr.value + 1, ptr);\n-\t\tnewLineCount = parseBase10(buf, ptr.value + 1, ptr);\n+\t\tif (buf[ptr.value] == ',')\n+\t\t\tnewLineCount = parseBase10(buf, ptr.value + 1, ptr);\n+\t\telse {\n+\t\t\tnewLineCount = newStartLine;\n+\t\t\tnewStartLine = 0;\n+\t\t}\n \t}\n \n \tint parseBody(final Patch script, final int end) {\n-- \n1.6.1.rc2.306.ge5d5e\n"},{"id":"97667","messageId":"1229049981-14152-14-git-send-email-spearce@spearce.org","threadId":"16690","inReplyTo":"1229049981-14152-13-git-send-email-spearce@spearce.org","subject":"[JGIT PATCH 13/15] Patch parse test comparing \"git log -p\" output to \"git log --numstat\"","fromName":"Shawn O. Pearce","fromEmail":"spearce@spearce.org","sentAt":"2008-12-12T02:46:19Z","receivedAt":"2008-12-12T02:46:19Z","isPatch":true,"sender":{"key":"spearce@spearce.org","avatar":"https://avatars.githubusercontent.com/u/34844?v=4"},"body":"By comparing the output of \"git log -p\", once parsed by our patch\nparser class, to the output of \"git log --numstat\" we can be quite\ncertain we are reading the patches from Git with a high degree of\naccuracy, at least for typical add/remove sorts of changes (no\nrename detection).\n\nUnfortunately two commits in our history produce an off-by-one bug\nin git log --numstat.  The bug appears to be in log --numstat and\nnot in JGit as git apply --numstat matches JGit's result, and is\nthus also differing from log --numstat.  Since this occurs on only\n2 commits out of 1,211 processed during the test I'm not worrying\nabout the difference on these two items.  Besides the numbers from\nJGit and git apply --numstat look to be more correct.\n\nSigned-off-by: Shawn O. Pearce <spearce@spearce.org>\n---\n .../spearce/jgit/patch/EGitPatchHistoryTest.java   |  221 ++++++++++++++++++++\n 1 files changed, 221 insertions(+), 0 deletions(-)\n create mode 100644 org.spearce.jgit.test/exttst/org/spearce/jgit/patch/EGitPatchHistoryTest.java\n\ndiff --git a/org.spearce.jgit.test/exttst/org/spearce/jgit/patch/EGitPatchHistoryTest.java b/org.spearce.jgit.test/exttst/org/spearce/jgit/patch/EGitPatchHistoryTest.java\nnew file mode 100644\nindex 0000000..d0c2632\n--- /dev/null\n+++ b/org.spearce.jgit.test/exttst/org/spearce/jgit/patch/EGitPatchHistoryTest.java\n@@ -0,0 +1,221 @@\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.patch;\n+\n+import java.io.BufferedReader;\n+import java.io.IOException;\n+import java.io.InputStreamReader;\n+import java.io.UnsupportedEncodingException;\n+import java.util.HashMap;\n+import java.util.HashSet;\n+\n+import junit.framework.TestCase;\n+\n+import org.spearce.jgit.lib.Constants;\n+import org.spearce.jgit.util.MutableInteger;\n+import org.spearce.jgit.util.RawParseUtils;\n+import org.spearce.jgit.util.TemporaryBuffer;\n+\n+public class EGitPatchHistoryTest extends TestCase {\n+\tpublic void testParseHistory() throws Exception {\n+\t\tfinal NumStatReader numstat = new NumStatReader();\n+\t\tnumstat.read();\n+\n+\t\tfinal HashMap<String, HashMap<String, StatInfo>> stats = numstat.stats;\n+\t\tassertEquals(1211, stats.size());\n+\n+\t\tnew PatchReader(stats).read();\n+\t}\n+\n+\tstatic class StatInfo {\n+\t\tint added, deleted;\n+\t}\n+\n+\tstatic class PatchReader extends CommitReader {\n+\t\tfinal HashSet<String> offBy1;\n+\n+\t\tfinal HashMap<String, HashMap<String, StatInfo>> stats;\n+\n+\t\tint errors;\n+\n+\t\tPatchReader(final HashMap<String, HashMap<String, StatInfo>> s)\n+\t\t\t\tthrows IOException {\n+\t\t\tsuper(new String[] { \"-p\" });\n+\t\t\tstats = s;\n+\n+\t\t\toffBy1 = new HashSet<String>();\n+\t\t\toffBy1.add(\"9bda5ece6806cd797416eaa47c7b927cc6e9c3b2\");\n+\t\t}\n+\n+\t\t@Override\n+\t\tvoid onCommit(String cid, byte[] buf) {\n+\t\t\tfinal HashMap<String, StatInfo> files = stats.remove(cid);\n+\t\t\tassertNotNull(\"No files for \" + cid, files);\n+\n+\t\t\tfinal Patch p = new Patch();\n+\t\t\tp.parse(buf, 0, buf.length - 1);\n+\t\t\tassertEquals(\"File count \" + cid, files.size(), p.getFiles().size());\n+\t\t\tif (!p.getErrors().isEmpty()) {\n+\t\t\t\tfor (final FormatError e : p.getErrors()) {\n+\t\t\t\t\tSystem.out.println(\"error \" + e.getMessage());\n+\t\t\t\t\tSystem.out.println(\"  at \" + e.getLineText());\n+\t\t\t\t}\n+\t\t\t\tdump(buf);\n+\t\t\t\tfail(\"Unexpected error in \" + cid);\n+\t\t\t}\n+\n+\t\t\tfor (final FileHeader fh : p.getFiles()) {\n+\t\t\t\tfinal String fileName;\n+\t\t\t\tif (fh.getChangeType() != FileHeader.ChangeType.DELETE)\n+\t\t\t\t\tfileName = fh.getNewName();\n+\t\t\t\telse\n+\t\t\t\t\tfileName = fh.getOldName();\n+\t\t\t\tfinal StatInfo s = files.remove(fileName);\n+\t\t\t\tfinal String nid = fileName + \" in \" + cid;\n+\t\t\t\tassertNotNull(\"No \" + nid, s);\n+\t\t\t\tint added = 0, deleted = 0;\n+\t\t\t\tfor (final HunkHeader h : fh.getHunks()) {\n+\t\t\t\t\tadded += h.getLinesAdded();\n+\t\t\t\t\tdeleted += h.getLinesDeleted();\n+\t\t\t\t}\n+\n+\t\t\t\tif (s.added == added) {\n+\t\t\t\t\t//\n+\t\t\t\t} else if (s.added == added + 1 && offBy1.contains(cid)) {\n+\t\t\t\t\t//\n+\t\t\t\t} else {\n+\t\t\t\t\tdump(buf);\n+\t\t\t\t\tassertEquals(\"Added diff in \" + nid, s.added, added);\n+\t\t\t\t}\n+\n+\t\t\t\tif (s.deleted == deleted) {\n+\t\t\t\t\t//\n+\t\t\t\t} else if (s.deleted == deleted + 1 && offBy1.contains(cid)) {\n+\t\t\t\t\t//\n+\t\t\t\t} else {\n+\t\t\t\t\tdump(buf);\n+\t\t\t\t\tassertEquals(\"Deleted diff in \" + nid, s.deleted, deleted);\n+\t\t\t\t}\n+\t\t\t}\n+\t\t\tassertTrue(\"Missed files in \" + cid, files.isEmpty());\n+\t\t}\n+\n+\t\tprivate static void dump(final byte[] buf) {\n+\t\t\tString str;\n+\t\t\ttry {\n+\t\t\t\tstr = new String(buf, 0, buf.length - 1, \"ISO-8859-1\");\n+\t\t\t} catch (UnsupportedEncodingException e) {\n+\t\t\t\tthrow new RuntimeException(e);\n+\t\t\t}\n+\t\t\tSystem.out.println(\"<<\" + str + \">>\");\n+\t\t}\n+\t}\n+\n+\tstatic class NumStatReader extends CommitReader {\n+\t\tfinal HashMap<String, HashMap<String, StatInfo>> stats = new HashMap<String, HashMap<String, StatInfo>>();\n+\n+\t\tNumStatReader() throws IOException {\n+\t\t\tsuper(new String[] { \"--numstat\" });\n+\t\t}\n+\n+\t\t@Override\n+\t\tvoid onCommit(String commitId, byte[] buf) {\n+\t\t\tfinal HashMap<String, StatInfo> files = new HashMap<String, StatInfo>();\n+\t\t\tfinal MutableInteger ptr = new MutableInteger();\n+\t\t\twhile (ptr.value < buf.length) {\n+\t\t\t\tif (buf[ptr.value] == '\\n')\n+\t\t\t\t\tbreak;\n+\t\t\t\tfinal StatInfo i = new StatInfo();\n+\t\t\t\ti.added = RawParseUtils.parseBase10(buf, ptr.value, ptr);\n+\t\t\t\ti.deleted = RawParseUtils.parseBase10(buf, ptr.value + 1, ptr);\n+\t\t\t\tfinal int eol = RawParseUtils.nextLF(buf, ptr.value);\n+\t\t\t\tfinal String name = RawParseUtils.decode(Constants.CHARSET,\n+\t\t\t\t\t\tbuf, ptr.value + 1, eol - 1);\n+\t\t\t\tfiles.put(name, i);\n+\t\t\t\tptr.value = eol;\n+\t\t\t}\n+\t\t\tstats.put(commitId, files);\n+\t\t}\n+\t}\n+\n+\tstatic abstract class CommitReader {\n+\t\tprivate Process proc;\n+\n+\t\tCommitReader(final String[] args) throws IOException {\n+\t\t\tfinal String[] realArgs = new String[3 + args.length + 1];\n+\t\t\trealArgs[0] = \"git\";\n+\t\t\trealArgs[1] = \"log\";\n+\t\t\trealArgs[2] = \"--pretty=format:commit %H\";\n+\t\t\tSystem.arraycopy(args, 0, realArgs, 3, args.length);\n+\t\t\trealArgs[3 + args.length] = \"a4b98ed15ea5f165a7aa0f2fd2ea6fcce6710925\";\n+\n+\t\t\tproc = Runtime.getRuntime().exec(realArgs);\n+\t\t\tproc.getOutputStream().close();\n+\t\t\tproc.getErrorStream().close();\n+\t\t}\n+\n+\t\tvoid read() throws IOException, InterruptedException {\n+\t\t\tfinal BufferedReader in = new BufferedReader(new InputStreamReader(\n+\t\t\t\t\tproc.getInputStream(), \"ISO-8859-1\"));\n+\t\t\tString commitId = null;\n+\t\t\tTemporaryBuffer buf = null;\n+\t\t\tfor (;;) {\n+\t\t\t\tString line = in.readLine();\n+\t\t\t\tif (line == null)\n+\t\t\t\t\tbreak;\n+\t\t\t\tif (line.startsWith(\"commit \")) {\n+\t\t\t\t\tif (buf != null) {\n+\t\t\t\t\t\tbuf.close();\n+\t\t\t\t\t\tonCommit(commitId, buf.toByteArray());\n+\t\t\t\t\t\tbuf.destroy();\n+\t\t\t\t\t}\n+\t\t\t\t\tcommitId = line.substring(\"commit \".length());\n+\t\t\t\t\tbuf = new TemporaryBuffer();\n+\t\t\t\t} else if (buf != null) {\n+\t\t\t\t\tbuf.write(line.getBytes(\"ISO-8859-1\"));\n+\t\t\t\t\tbuf.write('\\n');\n+\t\t\t\t}\n+\t\t\t}\n+\t\t\tin.close();\n+\t\t\tassertEquals(0, proc.waitFor());\n+\t\t\tproc = null;\n+\t\t}\n+\n+\t\tabstract void onCommit(String commitId, byte[] buf);\n+\t}\n+}\n-- \n1.6.1.rc2.306.ge5d5e\n"},{"id":"97676","messageId":"1229049981-14152-15-git-send-email-spearce@spearce.org","threadId":"16690","inReplyTo":"1229049981-14152-14-git-send-email-spearce@spearce.org","subject":"[JGIT PATCH 14/15] Abstract the hunk header testing into a method","fromName":"Shawn O. Pearce","fromEmail":"spearce@spearce.org","sentAt":"2008-12-12T02:46:20Z","receivedAt":"2008-12-12T02:46:20Z","isPatch":true,"sender":{"key":"spearce@spearce.org","avatar":"https://avatars.githubusercontent.com/u/34844?v=4"},"body":"This way we can test for \"@@@ -\" and \"@@@@@@@ -\" for combined\ndiffs in octopus merges.  We use the same scan test for the\nbasic two-way case, as the format is identical but has less\nmarker characters.\n\nSigned-off-by: Shawn O. Pearce <spearce@spearce.org>\n---\n .../src/org/spearce/jgit/patch/FileHeader.java     |   36 +++++++++++++++++--\n .../src/org/spearce/jgit/patch/Patch.java          |    8 ++--\n 2 files changed, 36 insertions(+), 8 deletions(-)\n\ndiff --git a/org.spearce.jgit/src/org/spearce/jgit/patch/FileHeader.java b/org.spearce.jgit/src/org/spearce/jgit/patch/FileHeader.java\nindex a58e978..5fe2acf 100644\n--- a/org.spearce.jgit/src/org/spearce/jgit/patch/FileHeader.java\n+++ b/org.spearce.jgit/src/org/spearce/jgit/patch/FileHeader.java\n@@ -87,8 +87,6 @@\n \n \tstatic final byte[] NEW_NAME = encodeASCII(\"+++ \");\n \n-\tstatic final byte[] HUNK_HDR = encodeASCII(\"@@ -\");\n-\n \t/** General type of change a single file-level patch describes. */\n \tpublic static enum ChangeType {\n \t\t/** Add a new file to the project */\n@@ -358,7 +356,7 @@ int parseGitFileName(int ptr, final int end) {\n \tint parseGitHeaders(int ptr, final int end) {\n \t\twhile (ptr < end) {\n \t\t\tfinal int eol = nextLF(buf, ptr);\n-\t\t\tif (match(buf, ptr, HUNK_HDR) >= 0) {\n+\t\t\tif (isHunkHdr(buf, ptr, eol) >= 1) {\n \t\t\t\t// First hunk header; break out and parse them later.\n \t\t\t\tbreak;\n \n@@ -432,7 +430,7 @@ int parseGitHeaders(int ptr, final int end) {\n \tint parseTraditionalHeaders(int ptr, final int end) {\n \t\twhile (ptr < end) {\n \t\t\tfinal int eol = nextLF(buf, ptr);\n-\t\t\tif (match(buf, ptr, HUNK_HDR) >= 0) {\n+\t\t\tif (isHunkHdr(buf, ptr, eol) >= 1) {\n \t\t\t\t// First hunk header; break out and parse them later.\n \t\t\t\tbreak;\n \n@@ -519,4 +517,34 @@ private boolean eq(int aPtr, int aEnd, int bPtr, int bEnd) {\n \t\t}\n \t\treturn true;\n \t}\n+\n+\t/**\n+\t * Determine if this is a patch hunk header.\n+\t * \n+\t * @param buf\n+\t *            the buffer to scan\n+\t * @param start\n+\t *            first position in the buffer to evaluate\n+\t * @param end\n+\t *            last position to consider; usually the end of the buffer (\n+\t *            <code>buf.length</code>) or the first position on the next\n+\t *            line. This is only used to avoid very long runs of '@' from\n+\t *            killing the scan loop.\n+\t * @return the number of \"ancestor revisions\" in the hunk header. A\n+\t *         traditional two-way diff (\"@@ -...\") returns 1; a combined diff\n+\t *         for a 3 way-merge returns 3. If this is not a hunk header, 0 is\n+\t *         returned instead.\n+\t */\n+\tstatic int isHunkHdr(final byte[] buf, final int start, final int end) {\n+\t\tint ptr = start;\n+\t\twhile (ptr < end && buf[ptr] == '@')\n+\t\t\tptr++;\n+\t\tif (ptr - start < 2)\n+\t\t\treturn 0;\n+\t\tif (ptr == end || buf[ptr++] != ' ')\n+\t\t\treturn 0;\n+\t\tif (ptr == end || buf[ptr++] != '-')\n+\t\t\treturn 0;\n+\t\treturn (ptr - 3) - start;\n+\t}\n }\ndiff --git a/org.spearce.jgit/src/org/spearce/jgit/patch/Patch.java b/org.spearce.jgit/src/org/spearce/jgit/patch/Patch.java\nindex 9aca22d..e1e79b7 100644\n--- a/org.spearce.jgit/src/org/spearce/jgit/patch/Patch.java\n+++ b/org.spearce.jgit/src/org/spearce/jgit/patch/Patch.java\n@@ -38,7 +38,7 @@\n package org.spearce.jgit.patch;\n \n import static org.spearce.jgit.lib.Constants.encodeASCII;\n-import static org.spearce.jgit.patch.FileHeader.HUNK_HDR;\n+import static org.spearce.jgit.patch.FileHeader.isHunkHdr;\n import static org.spearce.jgit.patch.FileHeader.NEW_NAME;\n import static org.spearce.jgit.patch.FileHeader.OLD_NAME;\n import static org.spearce.jgit.util.RawParseUtils.match;\n@@ -162,7 +162,7 @@ public void parse(final byte[] buf, int ptr, final int end) {\n \n \tprivate int parseFile(final byte[] buf, int c, final int end) {\n \t\twhile (c < end) {\n-\t\t\tif (match(buf, c, HUNK_HDR) >= 0) {\n+\t\t\tif (isHunkHdr(buf, c, end) >= 1) {\n \t\t\t\t// If we find a disconnected hunk header we might\n \t\t\t\t// have missed a file header previously. The hunk\n \t\t\t\t// isn't valid without knowing where it comes from.\n@@ -205,7 +205,7 @@ private int parseFile(final byte[] buf, int c, final int end) {\n \t\t\t\tfinal int f = nextLF(buf, n);\n \t\t\t\tif (f >= end)\n \t\t\t\t\treturn end;\n-\t\t\t\tif (match(buf, f, HUNK_HDR) >= 0)\n+\t\t\t\tif (isHunkHdr(buf, f, end) == 1)\n \t\t\t\t\treturn parseTraditionalPatch(buf, c, end);\n \t\t\t}\n \n@@ -274,7 +274,7 @@ private int parseHunks(final FileHeader fh, int c, final int end) {\n \t\t\tif (match(buf, c, NEW_NAME) >= 0)\n \t\t\t\tbreak;\n \n-\t\t\tif (match(buf, c, HUNK_HDR) >= 0) {\n+\t\t\tif (isHunkHdr(buf, c, end) == 1) {\n \t\t\t\tfinal HunkHeader h = new HunkHeader(fh, c);\n \t\t\t\th.parseHeader(end);\n \t\t\t\tc = h.parseBody(this, end);\n-- \n1.6.1.rc2.306.ge5d5e\n"},{"id":"97674","messageId":"1229049981-14152-16-git-send-email-spearce@spearce.org","threadId":"16690","inReplyTo":"1229049981-14152-15-git-send-email-spearce@spearce.org","subject":"[JGIT PATCH 15/15] Treat \"diff --combined\" the same as \"diff --cc\"","fromName":"Shawn O. Pearce","fromEmail":"spearce@spearce.org","sentAt":"2008-12-12T02:46:21Z","receivedAt":"2008-12-12T02:46:21Z","isPatch":true,"sender":{"key":"spearce@spearce.org","avatar":"https://avatars.githubusercontent.com/u/34844?v=4"},"body":"According to the git diff manual page these two formats\nshare the same file structure, so we can parse them with\nthe same function.\n\nSigned-off-by: Shawn O. Pearce <spearce@spearce.org>\n---\n .../src/org/spearce/jgit/patch/Patch.java          |   15 +++++++++++----\n 1 files changed, 11 insertions(+), 4 deletions(-)\n\ndiff --git a/org.spearce.jgit/src/org/spearce/jgit/patch/Patch.java b/org.spearce.jgit/src/org/spearce/jgit/patch/Patch.java\nindex e1e79b7..77ae02f 100644\n--- a/org.spearce.jgit/src/org/spearce/jgit/patch/Patch.java\n+++ b/org.spearce.jgit/src/org/spearce/jgit/patch/Patch.java\n@@ -57,6 +57,8 @@\n \n \tprivate static final byte[] DIFF_CC = encodeASCII(\"diff --cc \");\n \n+\tprivate static final byte[] DIFF_COMBINED = encodeASCII(\"diff --combined \");\n+\n \tprivate static final byte[][] BIN_HEADERS = new byte[][] {\n \t\t\tencodeASCII(\"Binary files \"), encodeASCII(\"Files \"), };\n \n@@ -177,7 +179,9 @@ private int parseFile(final byte[] buf, int c, final int end) {\n \t\t\tif (match(buf, c, DIFF_GIT) >= 0)\n \t\t\t\treturn parseDiffGit(buf, c, end);\n \t\t\tif (match(buf, c, DIFF_CC) >= 0)\n-\t\t\t\treturn parseDiffCC(buf, c, end);\n+\t\t\t\treturn parseDiffCombined(DIFF_CC, buf, c, end);\n+\t\t\tif (match(buf, c, DIFF_COMBINED) >= 0)\n+\t\t\t\treturn parseDiffCombined(DIFF_COMBINED, buf, c, end);\n \n \t\t\t// Junk between files? Leading junk? Traditional\n \t\t\t// (non-git generated) patch?\n@@ -227,9 +231,10 @@ private int parseDiffGit(final byte[] buf, final int start, final int end) {\n \t\treturn ptr;\n \t}\n \n-\tprivate int parseDiffCC(final byte[] buf, final int start, final int end) {\n-\t\tfinal FileHeader fh = new FileHeader(buf, start);\n-\t\tint ptr = fh.parseGitFileName(start + DIFF_CC.length, end);\n+\tprivate int parseDiffCombined(final byte[] hdr, final byte[] buf,\n+\t\t\tfinal int start, final int end) {\n+\t\tfinal CombinedFileHeader fh = new CombinedFileHeader(buf, start);\n+\t\tint ptr = fh.parseGitFileName(start + hdr.length, end);\n \t\tif (ptr < 0)\n \t\t\treturn skipFile(buf, start, end);\n \n@@ -269,6 +274,8 @@ private int parseHunks(final FileHeader fh, int c, final int end) {\n \t\t\t\tbreak;\n \t\t\tif (match(buf, c, DIFF_CC) >= 0)\n \t\t\t\tbreak;\n+\t\t\tif (match(buf, c, DIFF_COMBINED) >= 0)\n+\t\t\t\tbreak;\n \t\t\tif (match(buf, c, OLD_NAME) >= 0)\n \t\t\t\tbreak;\n \t\t\tif (match(buf, c, NEW_NAME) >= 0)\n-- \n1.6.1.rc2.306.ge5d5e\n"},{"id":"97698","messageId":"20081212151533.GM32487@spearce.org","threadId":"16690","inReplyTo":"bd6139dc0812120243y2b1a3dddu4975162114280e17@mail.gmail.com","subject":"Re: [JGIT PATCH 03/15] Add IntList as a more efficient representation of List<Integer>","fromName":"Shawn O. Pearce","fromEmail":"spearce@spearce.org","sentAt":"2008-12-12T15:15:33Z","receivedAt":"2008-12-12T15:15:33Z","isPatch":true,"sender":{"key":"spearce@spearce.org","avatar":"https://avatars.githubusercontent.com/u/34844?v=4"},"body":"Sverre Rabbelier <alturin@gmail.com> wrote:\n> On Fri, Dec 12, 2008 at 03:46, Shawn O. Pearce <spearce@spearce.org> wrote:\n> > +       public String toString() {\n> > +               final StringBuilder r = new StringBuilder();\n> > +               r.append('[');\n> > +               for (int i = 0; i < count; i++) {\n> > +                       if (i > 0)\n> > +                               r.append(\", \");\n> > +                       r.append(entries[i]);\n> > +               }\n> > +               r.append(']');\n> > +               return r.toString();\n> > +       }\n> > +}\n> \n> If you care about speed in your toString at all, pull the if statement\n> out of there. A friend of mine did a small benchmark once, and it was\n> _a lot_ slower to do the if in the for loop. I reckon you don't\n> though, but just in case ;).\n\nHmm, yea, good point.  But I don't care too much about the toString()\nin this case, its meant as a debugging aid and not something one\nwould rely upon.  Hence I didn't think it was worth testing for the\nempty list, writing the first entry, then doing a loop for [1,count).\n\n-- \nShawn.\n"},{"id":"97700","messageId":"bd6139dc0812120733o7c828532qbcd78c46a321fe6b@mail.gmail.com","threadId":"16690","inReplyTo":"20081212151533.GM32487@spearce.org","subject":"Re: [JGIT PATCH 03/15] Add IntList as a more efficient representation of List<Integer>","fromName":"Sverre Rabbelier","fromEmail":"srabbelier@gmail.com","sentAt":"2008-12-12T15:33:16Z","receivedAt":"2008-12-12T15:33:16Z","isPatch":true,"sender":{"key":"srabbelier@gmail.com","avatar":"https://avatars.githubusercontent.com/u/3098?v=4"},"body":"On Fri, Dec 12, 2008 at 16:15, Shawn O. Pearce <spearce@spearce.org> wrote:\n> Hmm, yea, good point.  But I don't care too much about the toString()\n> in this case, its meant as a debugging aid and not something one\n> would rely upon.  Hence I didn't think it was worth testing for the\n> empty list, writing the first entry, then doing a loop for [1,count).\n\nFair enough :).\n\n-- \nCheers,\n\nSverre Rabbelier\n"},{"id":"97701","messageId":"20081212154115.GO32487@spearce.org","threadId":"16690","inReplyTo":"bd6139dc0812120733o7c828532qbcd78c46a321fe6b@mail.gmail.com","subject":"Re: [JGIT PATCH 03/15] Add IntList as a more efficient representation of List<Integer>","fromName":"Shawn O. Pearce","fromEmail":"spearce@spearce.org","sentAt":"2008-12-12T15:41:15Z","receivedAt":"2008-12-12T15:41:15Z","isPatch":true,"sender":{"key":"spearce@spearce.org","avatar":"https://avatars.githubusercontent.com/u/34844?v=4"},"body":"Sverre Rabbelier <srabbelier@gmail.com> wrote:\n> On Fri, Dec 12, 2008 at 16:15, Shawn O. Pearce <spearce@spearce.org> wrote:\n> > Hmm, yea, good point.  But I don't care too much about the toString()\n> > in this case, its meant as a debugging aid and not something one\n> > would rely upon.  Hence I didn't think it was worth testing for the\n> > empty list, writing the first entry, then doing a loop for [1,count).\n> \n> Fair enough :).\n\nIf you'd like to send a patch to change it, I'll apply it.  But I\ndon't think its worth my time to make this toString() more efficient.\n\nOther areas of JGit I do try to micro-optimize, because they are\nright smack in the middle of the critical paths.\n\nE.g. look at ObjectId.equals(byte[],int,byte[],int). I hand-unrolled\nthe memcmp loop because the JIT on x86 does *soooo* much better\nwhen the code is spelled out:\n\n\tpublic static boolean equals(final byte[] firstBuffer, final int fi,\n\t\t\tfinal byte[] secondBuffer, final int si) {\n\t\treturn firstBuffer[fi] == secondBuffer[si]\n\t\t\t\t&& firstBuffer[fi + 1] == secondBuffer[si + 1]\n\t\t\t\t&& firstBuffer[fi + 2] == secondBuffer[si + 2]\n\t\t\t\t&& firstBuffer[fi + 3] == secondBuffer[si + 3]\n\t\t\t\t&& firstBuffer[fi + 4] == secondBuffer[si + 4]\n\t\t\t\t&& firstBuffer[fi + 5] == secondBuffer[si + 5]\n\t\t\t\t&& firstBuffer[fi + 6] == secondBuffer[si + 6]\n\t\t\t\t&& firstBuffer[fi + 7] == secondBuffer[si + 7]\n\t\t\t\t&& firstBuffer[fi + 8] == secondBuffer[si + 8]\n\t\t\t\t&& firstBuffer[fi + 9] == secondBuffer[si + 9]\n\t\t\t\t&& firstBuffer[fi + 10] == secondBuffer[si + 10]\n\t\t\t\t&& firstBuffer[fi + 11] == secondBuffer[si + 11]\n\t\t\t\t&& firstBuffer[fi + 12] == secondBuffer[si + 12]\n\t\t\t\t&& firstBuffer[fi + 13] == secondBuffer[si + 13]\n\t\t\t\t&& firstBuffer[fi + 14] == secondBuffer[si + 14]\n\t\t\t\t&& firstBuffer[fi + 15] == secondBuffer[si + 15]\n\t\t\t\t&& firstBuffer[fi + 16] == secondBuffer[si + 16]\n\t\t\t\t&& firstBuffer[fi + 17] == secondBuffer[si + 17]\n\t\t\t\t&& firstBuffer[fi + 18] == secondBuffer[si + 18]\n\t\t\t\t&& firstBuffer[fi + 19] == secondBuffer[si + 19];\n\t}\n\nThis block is in the critical path for any tree diff code, in\nparticular for a \"git log -- a/\" sort of operation.  Its used\nto compare the SHA-1s from two different tree records to see if\nthey differ.  Not unrolling this was a huge penalty.\n\n-- \nShawn.\n"},{"id":"97704","messageId":"bd6139dc0812120750j19b4d56an24b9c52a3ca60f20@mail.gmail.com","threadId":"16690","inReplyTo":"20081212154115.GO32487@spearce.org","subject":"Re: [JGIT PATCH 03/15] Add IntList as a more efficient representation of List<Integer>","fromName":"Sverre Rabbelier","fromEmail":"srabbelier@gmail.com","sentAt":"2008-12-12T15:50:32Z","receivedAt":"2008-12-12T15:50:32Z","isPatch":true,"sender":{"key":"srabbelier@gmail.com","avatar":"https://avatars.githubusercontent.com/u/3098?v=4"},"body":"On Fri, Dec 12, 2008 at 16:41, Shawn O. Pearce <spearce@spearce.org> wrote:\n> If you'd like to send a patch to change it, I'll apply it.  But I\n> don't think its worth my time to make this toString() more efficient.\n\nI mainly mentioned it because it's in a Class meant to be more optimal\nthan what Java ships with, but I agree with your reasoning that this\ntoString is not part of what needs to be optimized.\n\n> Other areas of JGit I do try to micro-optimize, because they are\n> right smack in the middle of the critical paths.\n\nHehe, I very much agree with not optimizing prematurely, and if you do\noptimize to go for it all the way.\n\n> E.g. look at ObjectId.equals(byte[],int,byte[],int). I hand-unrolled\n> the memcmp loop because the JIT on x86 does *soooo* much better\n> when the code is spelled out:\n\n<code snipped>\n\nKind of sad that you have to write this kind of code if you want good\nperformance, ah well, perhaps someday... (import java.lang.optimized\n;) ).\n\n> This block is in the critical path for any tree diff code, in\n> particular for a \"git log -- a/\" sort of operation.  Its used\n> to compare the SHA-1s from two different tree records to see if\n> they differ.  Not unrolling this was a huge penalty.\n\nI reckon that is done a lot :). Ashame the JRE can't do that kind of\noptimization for you. e.g., if you do:\nfor(int i = 0; i < constant; i++) {\n  some_code;\n}\n\n-- \nCheers,\n\nSverre Rabbelier\n"},{"id":"97759","messageId":"200812130011.37854.robin.rosenberg.lists@dewire.com","threadId":"16690","inReplyTo":"1229049981-14152-16-git-send-email-spearce@spearce.org","subject":"Re: [JGIT PATCH 15/15] Treat \"diff --combined\" the same as \"diff --cc\"","fromName":"Robin Rosenberg","fromEmail":"robin.rosenberg.lists@dewire.com","sentAt":"2008-12-12T23:11:37Z","receivedAt":"2008-12-12T23:11:37Z","isPatch":true,"sender":{"key":"robin.rosenberg@dewire.com","avatar":"https://avatars.githubusercontent.com/u/46357?v=4"},"body":"\nThe source for CombinedFileHeader would be nice. \n\nBtw, I moved the patches to the test-rsrc directory to make it possible\nto run the unit tests throuh maven.\n\n-- robin\n"},{"id":"97761","messageId":"20081212231815.GT32487@spearce.org","threadId":"16690","inReplyTo":"200812130011.37854.robin.rosenberg.lists@dewire.com","subject":"[JGIT PATCH 15/15 v2] Treat \"diff --combined\" the same as \"diff --cc\"","fromName":"Shawn O. Pearce","fromEmail":"spearce@spearce.org","sentAt":"2008-12-12T23:18:15Z","receivedAt":"2008-12-12T23:18:15Z","isPatch":true,"sender":{"key":"spearce@spearce.org","avatar":"https://avatars.githubusercontent.com/u/34844?v=4"},"body":"According to the git diff manual page these two formats\nshare the same file structure, so we can parse them with\nthe same function.\n\nSigned-off-by: Shawn O. Pearce <spearce@spearce.org>\n---\n  Robin Rosenberg <robin.rosenberg.lists@dewire.com> wrote:\n  > \n  > The source for CombinedFileHeader would be nice. \n\n  Dammit, that wasn't added until 12/12 of the final series.\n  A new 12/12 v2 will be sent in a minute.\n   \n  > Btw, I moved the patches to the test-rsrc directory to make it possible\n  > to run the unit tests throuh maven.\n  \n  Oh.\n\n .../src/org/spearce/jgit/patch/Patch.java          |   13 ++++++++++---\n 1 files changed, 10 insertions(+), 3 deletions(-)\n\ndiff --git a/org.spearce.jgit/src/org/spearce/jgit/patch/Patch.java b/org.spearce.jgit/src/org/spearce/jgit/patch/Patch.java\nindex e1e79b7..9ae2635 100644\n--- a/org.spearce.jgit/src/org/spearce/jgit/patch/Patch.java\n+++ b/org.spearce.jgit/src/org/spearce/jgit/patch/Patch.java\n@@ -57,6 +57,8 @@\n \n \tprivate static final byte[] DIFF_CC = encodeASCII(\"diff --cc \");\n \n+\tprivate static final byte[] DIFF_COMBINED = encodeASCII(\"diff --combined \");\n+\n \tprivate static final byte[][] BIN_HEADERS = new byte[][] {\n \t\t\tencodeASCII(\"Binary files \"), encodeASCII(\"Files \"), };\n \n@@ -177,7 +179,9 @@ private int parseFile(final byte[] buf, int c, final int end) {\n \t\t\tif (match(buf, c, DIFF_GIT) >= 0)\n \t\t\t\treturn parseDiffGit(buf, c, end);\n \t\t\tif (match(buf, c, DIFF_CC) >= 0)\n-\t\t\t\treturn parseDiffCC(buf, c, end);\n+\t\t\t\treturn parseDiffCombined(DIFF_CC, buf, c, end);\n+\t\t\tif (match(buf, c, DIFF_COMBINED) >= 0)\n+\t\t\t\treturn parseDiffCombined(DIFF_COMBINED, buf, c, end);\n \n \t\t\t// Junk between files? Leading junk? Traditional\n \t\t\t// (non-git generated) patch?\n@@ -227,9 +231,10 @@ private int parseDiffGit(final byte[] buf, final int start, final int end) {\n \t\treturn ptr;\n \t}\n \n-\tprivate int parseDiffCC(final byte[] buf, final int start, final int end) {\n+\tprivate int parseDiffCombined(final byte[] hdr, final byte[] buf,\n+\t\t\tfinal int start, final int end) {\n \t\tfinal FileHeader fh = new FileHeader(buf, start);\n-\t\tint ptr = fh.parseGitFileName(start + DIFF_CC.length, end);\n+\t\tint ptr = fh.parseGitFileName(start + hdr.length, end);\n \t\tif (ptr < 0)\n \t\t\treturn skipFile(buf, start, end);\n \n@@ -269,6 +274,8 @@ private int parseHunks(final FileHeader fh, int c, final int end) {\n \t\t\t\tbreak;\n \t\t\tif (match(buf, c, DIFF_CC) >= 0)\n \t\t\t\tbreak;\n+\t\t\tif (match(buf, c, DIFF_COMBINED) >= 0)\n+\t\t\t\tbreak;\n \t\t\tif (match(buf, c, OLD_NAME) >= 0)\n \t\t\t\tbreak;\n \t\t\tif (match(buf, c, NEW_NAME) >= 0)\n-- \n1.6.1.rc2.306.ge5d5e\n\n\n-- \nShawn.\n"}]}