{"thread":{"id":"16709","subject":"[JGIT PATCH 1/2] Add raw buffer fetch methods to FileHeader, HunkHeader","startedAt":"2008-12-13T02:42:25Z","lastAt":"2008-12-17T20:13:26Z","messageCount":5,"participants":["Shawn O. Pearce","Robin Rosenberg"],"isPatch":true,"patchVersion":1,"patchTotal":2},"messages":[{"id":"97775","messageId":"1229136146-15359-1-git-send-email-spearce@spearce.org","threadId":"16709","inReplyTo":null,"subject":"[JGIT PATCH 1/2] Add raw buffer fetch methods to FileHeader, HunkHeader","fromName":"Shawn O. Pearce","fromEmail":"spearce@spearce.org","sentAt":"2008-12-13T02:42:25Z","receivedAt":"2008-12-13T02:42:25Z","isPatch":true,"sender":{"key":"spearce@spearce.org","avatar":"https://avatars.githubusercontent.com/u/34844?v=4"},"body":"These permit application level code to read back the patch\nscript, for example to slice it up and output parts into a\nUI on a per-file or per-hunk basis.\n\nSigned-off-by: Shawn O. Pearce <spearce@spearce.org>\n---\n\n Two last-minute patches.  While using this code in Gerrit 2 I\n realized I forgot to add a way to get the script back after its\n been parsed by the library.  :-)\n\n .../src/org/spearce/jgit/patch/BinaryHunk.java     |   15 +++++++++++++++\n .../src/org/spearce/jgit/patch/FileHeader.java     |   15 +++++++++++++++\n .../src/org/spearce/jgit/patch/HunkHeader.java     |   15 +++++++++++++++\n 3 files changed, 45 insertions(+), 0 deletions(-)\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 92eab86..f43a1b9 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@@ -81,6 +81,21 @@ public FileHeader getFileHeader() {\n \t\treturn file;\n \t}\n \n+\t/** @return the byte array holding this hunk's patch script. */\n+\tpublic byte[] getBuffer() {\n+\t\treturn file.buf;\n+\t}\n+\n+\t/** @return offset the start of this hunk in {@link #getBuffer()}. */\n+\tpublic int getStartOffset() {\n+\t\treturn startOffset;\n+\t}\n+\n+\t/** @return offset one past the end of the hunk in {@link #getBuffer()}. */\n+\tpublic int getEndOffset() {\n+\t\treturn endOffset;\n+\t}\n+\n \t/** @return type of this binary hunk */\n \tpublic Type getType() {\n \t\treturn type;\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 79e4b0a..7c3a45a 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@@ -173,6 +173,21 @@ int getParentCount() {\n \t\treturn 1;\n \t}\n \n+\t/** @return the byte array holding this file's patch script. */\n+\tpublic byte[] getBuffer() {\n+\t\treturn buf;\n+\t}\n+\n+\t/** @return offset the start of this file's script in {@link #getBuffer()}. */\n+\tpublic int getStartOffset() {\n+\t\treturn startOffset;\n+\t}\n+\n+\t/** @return offset one past the end of the file script. */\n+\tpublic int getEndOffset() {\n+\t\treturn endOffset;\n+\t}\n+\n \t/**\n \t * Get the old name associated with this file.\n \t * <p>\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 fc149ac..12c670d 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@@ -123,6 +123,21 @@ public FileHeader getFileHeader() {\n \t\treturn file;\n \t}\n \n+\t/** @return the byte array holding this hunk's patch script. */\n+\tpublic byte[] getBuffer() {\n+\t\treturn file.buf;\n+\t}\n+\n+\t/** @return offset the start of this hunk in {@link #getBuffer()}. */\n+\tpublic int getStartOffset() {\n+\t\treturn startOffset;\n+\t}\n+\n+\t/** @return offset one past the end of the hunk in {@link #getBuffer()}. */\n+\tpublic int getEndOffset() {\n+\t\treturn endOffset;\n+\t}\n+\n \t/** @return information about the old image mentioned in this hunk. */\n \tpublic OldImage getOldImage() {\n \t\treturn old;\n-- \n1.6.1.rc2.306.ge5d5e\n"},{"id":"97776","messageId":"1229136146-15359-2-git-send-email-spearce@spearce.org","threadId":"16709","inReplyTo":"1229136146-15359-1-git-send-email-spearce@spearce.org","subject":"[JGIT PATCH 2/2] Add getPatchText functions to obtain the plain-text version of a patch","fromName":"Shawn O. Pearce","fromEmail":"spearce@spearce.org","sentAt":"2008-12-13T02:42:26Z","receivedAt":"2008-12-13T02:42:26Z","isPatch":true,"sender":{"key":"spearce@spearce.org","avatar":"https://avatars.githubusercontent.com/u/34844?v=4"},"body":"The conversion from byte[] to String is performed one line at a time,\nin case the patch is a character encoding conversion patch for the\nfile.  For simplicity we currently assume UTF-8 still as the default\nencoding for any content, but eventually we should support using the\n.gitattributes encoding property when performing this conversion.\n\nSigned-off-by: Shawn O. Pearce <spearce@spearce.org>\n---\n .../src/org/spearce/jgit/patch/BinaryHunk.java     |    8 ++\n .../src/org/spearce/jgit/patch/FileHeader.java     |    6 ++\n .../src/org/spearce/jgit/patch/HunkHeader.java     |    7 ++\n .../src/org/spearce/jgit/patch/PatchUtil.java      |   79 ++++++++++++++++++++\n 4 files changed, 100 insertions(+), 0 deletions(-)\n create mode 100644 org.spearce.jgit/src/org/spearce/jgit/patch/PatchUtil.java\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 f43a1b9..f4e2ee3 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@@ -42,6 +42,8 @@\n import static org.spearce.jgit.util.RawParseUtils.nextLF;\n import static org.spearce.jgit.util.RawParseUtils.parseBase10;\n \n+import org.spearce.jgit.lib.Constants;\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@@ -96,6 +98,12 @@ public int getEndOffset() {\n \t\treturn endOffset;\n \t}\n \n+\t/** @return text of this patch file's script; best-effort decoded */\n+\tpublic String getHunkText() {\n+\t\treturn PatchUtil.decode(Constants.CHARSET, getBuffer(),\n+\t\t\t\tgetStartOffset(), getEndOffset());\n+\t}\n+\n \t/** @return type of this binary hunk */\n \tpublic Type getType() {\n \t\treturn type;\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 7c3a45a..0110f4a 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@@ -188,6 +188,12 @@ public int getEndOffset() {\n \t\treturn endOffset;\n \t}\n \n+\t/** @return text of this patch file's script; best-effort decoded */\n+\tpublic String getScriptText() {\n+\t\treturn PatchUtil.decode(Constants.CHARSET, getBuffer(),\n+\t\t\t\tgetStartOffset(), getEndOffset());\n+\t}\n+\n \t/**\n \t * Get the old name associated with this file.\n \t * <p>\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 12c670d..5a3b590 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@@ -42,6 +42,7 @@\n import static org.spearce.jgit.util.RawParseUtils.parseBase10;\n \n import org.spearce.jgit.lib.AbbreviatedObjectId;\n+import org.spearce.jgit.lib.Constants;\n import org.spearce.jgit.util.MutableInteger;\n \n /** Hunk header describing the layout of a single block of lines */\n@@ -138,6 +139,12 @@ public int getEndOffset() {\n \t\treturn endOffset;\n \t}\n \n+\t/** @return text of this patch file's script; best-effort decoded */\n+\tpublic String getHunkText() {\n+\t\treturn PatchUtil.decode(Constants.CHARSET, getBuffer(),\n+\t\t\t\tgetStartOffset(), getEndOffset());\n+\t}\n+\n \t/** @return information about the old image mentioned in this hunk. */\n \tpublic OldImage getOldImage() {\n \t\treturn old;\ndiff --git a/org.spearce.jgit/src/org/spearce/jgit/patch/PatchUtil.java b/org.spearce.jgit/src/org/spearce/jgit/patch/PatchUtil.java\nnew file mode 100644\nindex 0000000..89136c0\n--- /dev/null\n+++ b/org.spearce.jgit/src/org/spearce/jgit/patch/PatchUtil.java\n@@ -0,0 +1,79 @@\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.nio.charset.Charset;\n+\n+import org.spearce.jgit.util.RawParseUtils;\n+\n+/** Patch related utility functions. */\n+public class PatchUtil {\n+\t/**\n+\t * Decode a region of a buffer one line at a time.\n+\t * <p>\n+\t * Unlike {@link RawParseUtils#decode(Charset, byte[], int, int)} this\n+\t * method reads the input one line at a time and decodes each line\n+\t * individually. This permits a decoding of a file converting from\n+\t * ISO-8859-1 to UTF-8 encoding (for example), as each line in the patch\n+\t * script will be in one encoding or the other.\n+\t * \n+\t * @param cs\n+\t *            preferred character set to use when decoding the buffer.\n+\t * @param buf\n+\t *            buffer to pull the raw bytes from.\n+\t * @param ptr\n+\t *            first position to read.\n+\t * @param end\n+\t *            one position past the last position to read.\n+\t * @return a string representation of the region, decoded per-line.\n+\t */\n+\tpublic static String decode(final Charset cs, final byte[] buf, int ptr,\n+\t\t\tfinal int end) {\n+\t\tfinal StringBuilder r = new StringBuilder(end - ptr);\n+\t\twhile (ptr < end) {\n+\t\t\tfinal int eol = Math.min(end, RawParseUtils.nextLF(buf, ptr));\n+\t\t\tr.append(RawParseUtils.decode(cs, buf, ptr, eol));\n+\t\t\tptr = eol;\n+\t\t}\n+\t\treturn r.toString();\n+\t}\n+\n+\tprivate PatchUtil() {\n+\t\t// No instances\n+\t}\n+}\n-- \n1.6.1.rc2.306.ge5d5e\n"},{"id":"97790","messageId":"200812131202.07717.robin.rosenberg.lists@dewire.com","threadId":"16709","inReplyTo":"1229136146-15359-2-git-send-email-spearce@spearce.org","subject":"Re: [JGIT PATCH 2/2] Add getPatchText functions to obtain the plain-text version of a patch","fromName":"Robin Rosenberg","fromEmail":"robin.rosenberg.lists@dewire.com","sentAt":"2008-12-13T11:02:07Z","receivedAt":"2008-12-13T11:02:07Z","isPatch":true,"sender":{"key":"robin.rosenberg@dewire.com","avatar":"https://avatars.githubusercontent.com/u/46357?v=4"},"body":"lördag 13 december 2008 03:42:26 skrev Shawn O. Pearce:\n> The conversion from byte[] to String is performed one line at a time,\n> in case the patch is a character encoding conversion patch for the\n> file.  For simplicity we currently assume UTF-8 still as the default\n> encoding for any content, but eventually we should support using the\n> .gitattributes encoding property when performing this conversion.\n\nFor usefulness we must be able to pass the encoding from outside, \ne.g. the encoding Eclipse uses, which often is not UTF-8-\n\n-- robin\n"},{"id":"97816","messageId":"200812132226.47815.robin.rosenberg.lists@dewire.com","threadId":"16709","inReplyTo":"200812131202.07717.robin.rosenberg.lists@dewire.com","subject":"Re: [JGIT PATCH 2/2] Add getPatchText functions to obtain the plain-text version of a patch","fromName":"Robin Rosenberg","fromEmail":"robin.rosenberg.lists@dewire.com","sentAt":"2008-12-13T21:26:47Z","receivedAt":"2008-12-13T21:26:47Z","isPatch":true,"sender":{"key":"robin.rosenberg@dewire.com","avatar":"https://avatars.githubusercontent.com/u/46357?v=4"},"body":"lördag 13 december 2008 12:02:07 skrev Robin Rosenberg:\n> lördag 13 december 2008 03:42:26 skrev Shawn O. Pearce:\n> > The conversion from byte[] to String is performed one line at a time,\n> > in case the patch is a character encoding conversion patch for the\n> > file.  For simplicity we currently assume UTF-8 still as the default\n> > encoding for any content, but eventually we should support using the\n> > .gitattributes encoding property when performing this conversion.\n> \n> For usefulness we must be able to pass the encoding from outside, \n> e.g. the encoding Eclipse uses, which often is not UTF-8-\n\nIt's even worse. You should probably do the encoding guess on the whole\npatch, or per file and not per line so make success possible at all. Reading\nand writing as ISO-8859-1 will always work as that is just padding every\nbyte with NUL on reading and dropping it on writing. I.e. if your convert\nto char at all...\n\n-- robin\n"},{"id":"98173","messageId":"20081217201326.GI32487@spearce.org","threadId":"16709","inReplyTo":"200812132226.47815.robin.rosenberg.lists@dewire.com","subject":"[JGIT PATCH 2/2 v2] Add getScriptText functions to obtain the plain-text version of a patch","fromName":"Shawn O. Pearce","fromEmail":"spearce@spearce.org","sentAt":"2008-12-17T20:13:26Z","receivedAt":"2008-12-17T20:13:26Z","isPatch":true,"sender":{"key":"spearce@spearce.org","avatar":"https://avatars.githubusercontent.com/u/34844?v=4"},"body":"The conversion from byte[] to String is performed one file at a time,\nin case the patch is a character encoding conversion patch for the\nfile.  For simplicity we currently assume UTF-8 still as the default\nencoding for any content, but eventually we should support using the\n.gitattributes encoding property when performing this conversion.\n\nSigned-off-by: Shawn O. Pearce <spearce@spearce.org>\n---\n  Robin Rosenberg <robin.rosenberg.lists@dewire.com> wrote:\n  > > For usefulness we must be able to pass the encoding from outside, \n  > > e.g. the encoding Eclipse uses, which often is not UTF-8-\n  > \n  > It's even worse. You should probably do the encoding guess on the whole\n  > patch, or per file and not per line so make success possible at all. Reading\n  > and writing as ISO-8859-1 will always work as that is just padding every\n  > byte with NUL on reading and dropping it on writing. I.e. if your convert\n  > to char at all...\n\n  So this patch does the \"whole file\" thing.  But there is a\n  fast-path in getScriptText to try and bypass the multiple copies\n  we have to make in order to shovel the entire file into the\n  CharsetDecoder just to read the patch.  It isn't common to see\n  a character set conversion patch, so the fast case of decoding\n  the whole patch text at once should happen most of the time.\n\n .../jgit/patch/testGetText_BothISO88591.patch      |   21 +++\n .../spearce/jgit/patch/testGetText_Convert.patch   |   21 +++\n .../spearce/jgit/patch/testGetText_DiffCc.patch    |   13 ++\n .../spearce/jgit/patch/testGetText_NoBinary.patch  |    4 +\n .../tst/org/spearce/jgit/patch/GetTextTest.java    |  142 ++++++++++++++++++++\n .../org/spearce/jgit/patch/CombinedFileHeader.java |   27 ++++\n .../org/spearce/jgit/patch/CombinedHunkHeader.java |  127 +++++++++++++++++\n .../src/org/spearce/jgit/patch/FileHeader.java     |  116 ++++++++++++++++\n .../src/org/spearce/jgit/patch/HunkHeader.java     |   86 ++++++++++++\n .../src/org/spearce/jgit/util/RawParseUtils.java   |   57 ++++++++-\n 10 files changed, 611 insertions(+), 3 deletions(-)\n create mode 100644 org.spearce.jgit.test/tst-rsrc/org/spearce/jgit/patch/testGetText_BothISO88591.patch\n create mode 100644 org.spearce.jgit.test/tst-rsrc/org/spearce/jgit/patch/testGetText_Convert.patch\n create mode 100644 org.spearce.jgit.test/tst-rsrc/org/spearce/jgit/patch/testGetText_DiffCc.patch\n create mode 100644 org.spearce.jgit.test/tst-rsrc/org/spearce/jgit/patch/testGetText_NoBinary.patch\n create mode 100644 org.spearce.jgit.test/tst/org/spearce/jgit/patch/GetTextTest.java\n\ndiff --git a/org.spearce.jgit.test/tst-rsrc/org/spearce/jgit/patch/testGetText_BothISO88591.patch b/org.spearce.jgit.test/tst-rsrc/org/spearce/jgit/patch/testGetText_BothISO88591.patch\nnew file mode 100644\nindex 0000000..8224fcc\n--- /dev/null\n+++ b/org.spearce.jgit.test/tst-rsrc/org/spearce/jgit/patch/testGetText_BothISO88591.patch\n@@ -0,0 +1,21 @@\n+diff --git a/X b/X\n+index 014ef30..8c80a36 100644\n+--- a/X\n++++ b/X\n+@@ -1,7 +1,7 @@\n+ a\n+ b\n+ c\n+-�ngstr�m\n++line 4 �ngstr�m\n+ d\n+ e\n+ f\n+@@ -13,6 +13,6 @@ k\n+ l\n+ m\n+ n\n+-�ngstr�m\n++�ngstr�m; line 16\n+ o\n+ p\ndiff --git a/org.spearce.jgit.test/tst-rsrc/org/spearce/jgit/patch/testGetText_Convert.patch b/org.spearce.jgit.test/tst-rsrc/org/spearce/jgit/patch/testGetText_Convert.patch\nnew file mode 100644\nindex 0000000..a43fef5\n--- /dev/null\n+++ b/org.spearce.jgit.test/tst-rsrc/org/spearce/jgit/patch/testGetText_Convert.patch\n@@ -0,0 +1,21 @@\n+diff --git a/X b/X\n+index 014ef30..209db0d 100644\n+--- a/X\n++++ b/X\n+@@ -1,7 +1,7 @@\n+ a\n+ b\n+ c\n+-�ngstr�m\n++Ångström\n+ d\n+ e\n+ f\n+@@ -13,6 +13,6 @@ k\n+ l\n+ m\n+ n\n+-�ngstr�m\n++Ångström\n+ o\n+ p\ndiff --git a/org.spearce.jgit.test/tst-rsrc/org/spearce/jgit/patch/testGetText_DiffCc.patch b/org.spearce.jgit.test/tst-rsrc/org/spearce/jgit/patch/testGetText_DiffCc.patch\nnew file mode 100644\nindex 0000000..3f74a52\n--- /dev/null\n+++ b/org.spearce.jgit.test/tst-rsrc/org/spearce/jgit/patch/testGetText_DiffCc.patch\n@@ -0,0 +1,13 @@\n+diff --cc X\n+index bdfc9f4,209db0d..474bd69\n+--- a/X\n++++ b/X\n+@@@ -1,7 -1,7 +1,7 @@@\n+  a\n+--b\n+  c\n+ +test �ngstr�m\n++ Ångström\n+  d\n+  e\n+  f\ndiff --git a/org.spearce.jgit.test/tst-rsrc/org/spearce/jgit/patch/testGetText_NoBinary.patch b/org.spearce.jgit.test/tst-rsrc/org/spearce/jgit/patch/testGetText_NoBinary.patch\nnew file mode 100644\nindex 0000000..e4968dc\n--- /dev/null\n+++ b/org.spearce.jgit.test/tst-rsrc/org/spearce/jgit/patch/testGetText_NoBinary.patch\n@@ -0,0 +1,4 @@\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\ndiff --git a/org.spearce.jgit.test/tst/org/spearce/jgit/patch/GetTextTest.java b/org.spearce.jgit.test/tst/org/spearce/jgit/patch/GetTextTest.java\nnew file mode 100644\nindex 0000000..04810be\n--- /dev/null\n+++ b/org.spearce.jgit.test/tst/org/spearce/jgit/patch/GetTextTest.java\n@@ -0,0 +1,142 @@\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+import java.io.InputStreamReader;\n+import java.nio.charset.Charset;\n+\n+import junit.framework.TestCase;\n+\n+public class GetTextTest extends TestCase {\n+\tpublic void testGetText_BothISO88591() throws IOException {\n+\t\tfinal Charset cs = Charset.forName(\"ISO-8859-1\");\n+\t\tfinal Patch p = parseTestPatchFile();\n+\t\tassertTrue(p.getErrors().isEmpty());\n+\t\tassertEquals(1, p.getFiles().size());\n+\t\tfinal FileHeader fh = p.getFiles().get(0);\n+\t\tassertEquals(2, fh.getHunks().size());\n+\t\tassertEquals(readTestPatchFile(cs), fh.getScriptText(cs, cs));\n+\t}\n+\n+\tpublic void testGetText_NoBinary() throws IOException {\n+\t\tfinal Charset cs = Charset.forName(\"ISO-8859-1\");\n+\t\tfinal Patch p = parseTestPatchFile();\n+\t\tassertTrue(p.getErrors().isEmpty());\n+\t\tassertEquals(1, p.getFiles().size());\n+\t\tfinal FileHeader fh = p.getFiles().get(0);\n+\t\tassertEquals(0, fh.getHunks().size());\n+\t\tassertEquals(readTestPatchFile(cs), fh.getScriptText(cs, cs));\n+\t}\n+\n+\tpublic void testGetText_Convert() throws IOException {\n+\t\tfinal Charset csOld = Charset.forName(\"ISO-8859-1\");\n+\t\tfinal Charset csNew = Charset.forName(\"UTF-8\");\n+\t\tfinal Patch p = parseTestPatchFile();\n+\t\tassertTrue(p.getErrors().isEmpty());\n+\t\tassertEquals(1, p.getFiles().size());\n+\t\tfinal FileHeader fh = p.getFiles().get(0);\n+\t\tassertEquals(2, fh.getHunks().size());\n+\n+\t\t// Read the original file as ISO-8859-1 and fix up the one place\n+\t\t// where we changed the character encoding. That makes the exp\n+\t\t// string match what we really expect to get back.\n+\t\t//\n+\t\tString exp = readTestPatchFile(csOld);\n+\t\texp = exp.replace(\"\\303\\205ngstr\\303\\266m\", \"\\u00c5ngstr\\u00f6m\");\n+\n+\t\tassertEquals(exp, fh.getScriptText(csOld, csNew));\n+\t}\n+\n+\tpublic void testGetText_DiffCc() throws IOException {\n+\t\tfinal Charset csOld = Charset.forName(\"ISO-8859-1\");\n+\t\tfinal Charset csNew = Charset.forName(\"UTF-8\");\n+\t\tfinal Patch p = parseTestPatchFile();\n+\t\tassertTrue(p.getErrors().isEmpty());\n+\t\tassertEquals(1, p.getFiles().size());\n+\t\tfinal CombinedFileHeader fh = (CombinedFileHeader) p.getFiles().get(0);\n+\t\tassertEquals(1, fh.getHunks().size());\n+\n+\t\t// Read the original file as ISO-8859-1 and fix up the one place\n+\t\t// where we changed the character encoding. That makes the exp\n+\t\t// string match what we really expect to get back.\n+\t\t//\n+\t\tString exp = readTestPatchFile(csOld);\n+\t\texp = exp.replace(\"\\303\\205ngstr\\303\\266m\", \"\\u00c5ngstr\\u00f6m\");\n+\n+\t\tassertEquals(exp, fh\n+\t\t\t\t.getScriptText(new Charset[] { csNew, csOld, csNew }));\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+\n+\tprivate String readTestPatchFile(final Charset cs) 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 InputStreamReader r = new InputStreamReader(in, cs);\n+\t\t\tchar[] tmp = new char[2048];\n+\t\t\tfinal StringBuilder s = new StringBuilder();\n+\t\t\tint n;\n+\t\t\twhile ((n = r.read(tmp)) > 0)\n+\t\t\t\ts.append(tmp, 0, n);\n+\t\t\treturn s.toString();\n+\t\t} finally {\n+\t\t\tin.close();\n+\t\t}\n+\t}\n+}\ndiff --git a/org.spearce.jgit/src/org/spearce/jgit/patch/CombinedFileHeader.java b/org.spearce.jgit/src/org/spearce/jgit/patch/CombinedFileHeader.java\nindex 3ccc418..a27e0f8 100644\n--- a/org.spearce.jgit/src/org/spearce/jgit/patch/CombinedFileHeader.java\n+++ b/org.spearce.jgit/src/org/spearce/jgit/patch/CombinedFileHeader.java\n@@ -41,7 +41,9 @@\n import static org.spearce.jgit.util.RawParseUtils.match;\n import static org.spearce.jgit.util.RawParseUtils.nextLF;\n \n+import java.nio.charset.Charset;\n import java.util.ArrayList;\n+import java.util.Arrays;\n import java.util.List;\n \n import org.spearce.jgit.lib.AbbreviatedObjectId;\n@@ -111,6 +113,31 @@ public AbbreviatedObjectId getOldId(final int nthParent) {\n \t\treturn oldIds[nthParent];\n \t}\n \n+\t@Override\n+\tpublic String getScriptText(final Charset ocs, final Charset ncs) {\n+\t\tfinal Charset[] cs = new Charset[getParentCount() + 1];\n+\t\tArrays.fill(cs, ocs);\n+\t\tcs[getParentCount()] = ncs;\n+\t\treturn getScriptText(cs);\n+\t}\n+\n+\t/**\n+\t * Convert the patch script for this file into a string.\n+\t * \n+\t * @param charsetGuess\n+\t *            optional array to suggest the character set to use when\n+\t *            decoding each file's line. If supplied the array must have a\n+\t *            length of <code>{@link #getParentCount()} + 1</code>\n+\t *            representing the old revision character sets and the new\n+\t *            revision character set.\n+\t * @return the patch script, as a Unicode string.\n+\t */\n+\t@Override\n+\tpublic String getScriptText(final Charset[] charsetGuess) {\n+\t\treturn super.getScriptText(charsetGuess);\n+\t}\n+\n+\t@Override\n \tint parseGitHeaders(int ptr, final int end) {\n \t\twhile (ptr < end) {\n \t\t\tfinal int eol = nextLF(buf, ptr);\ndiff --git a/org.spearce.jgit/src/org/spearce/jgit/patch/CombinedHunkHeader.java b/org.spearce.jgit/src/org/spearce/jgit/patch/CombinedHunkHeader.java\nindex 3e5c465..83ea681 100644\n--- a/org.spearce.jgit/src/org/spearce/jgit/patch/CombinedHunkHeader.java\n+++ b/org.spearce.jgit/src/org/spearce/jgit/patch/CombinedHunkHeader.java\n@@ -40,6 +40,9 @@\n import static org.spearce.jgit.util.RawParseUtils.nextLF;\n import static org.spearce.jgit.util.RawParseUtils.parseBase10;\n \n+import java.io.IOException;\n+import java.io.OutputStream;\n+\n import org.spearce.jgit.lib.AbbreviatedObjectId;\n import org.spearce.jgit.util.MutableInteger;\n \n@@ -188,4 +191,128 @@ int parseBody(final Patch script, final int end) {\n \n \t\treturn c;\n \t}\n+\n+\t@Override\n+\tvoid extractFileLines(final OutputStream[] out) throws IOException {\n+\t\tfinal byte[] buf = file.buf;\n+\t\tint ptr = startOffset;\n+\t\tint eol = nextLF(buf, ptr);\n+\t\tif (endOffset <= eol)\n+\t\t\treturn;\n+\n+\t\t// Treat the hunk header as though it were from the ancestor,\n+\t\t// as it may have a function header appearing after it which\n+\t\t// was copied out of the ancestor file.\n+\t\t//\n+\t\tout[0].write(buf, ptr, eol - ptr);\n+\n+\t\tSCAN: for (ptr = eol; ptr < endOffset; ptr = eol) {\n+\t\t\teol = nextLF(buf, ptr);\n+\n+\t\t\tif (eol - ptr < old.length + 1) {\n+\t\t\t\t// Line isn't long enough to mention the state of each\n+\t\t\t\t// ancestor. It must be the end of the hunk.\n+\t\t\t\tbreak SCAN;\n+\t\t\t}\n+\n+\t\t\tswitch (buf[ptr]) {\n+\t\t\tcase ' ':\n+\t\t\tcase '-':\n+\t\t\tcase '+':\n+\t\t\t\tbreak;\n+\n+\t\t\tdefault:\n+\t\t\t\t// Line can't possibly be part of this hunk; the first\n+\t\t\t\t// ancestor information isn't recognizable.\n+\t\t\t\t//\n+\t\t\t\tbreak SCAN;\n+\t\t\t}\n+\n+\t\t\tint delcnt = 0;\n+\t\t\tfor (int ancestor = 0; ancestor < old.length; ancestor++) {\n+\t\t\t\tswitch (buf[ptr + ancestor]) {\n+\t\t\t\tcase '-':\n+\t\t\t\t\tdelcnt++;\n+\t\t\t\t\tout[ancestor].write(buf, ptr, eol - ptr);\n+\t\t\t\t\tcontinue;\n+\n+\t\t\t\tcase ' ':\n+\t\t\t\t\tout[ancestor].write(buf, ptr, eol - ptr);\n+\t\t\t\t\tcontinue;\n+\n+\t\t\t\tcase '+':\n+\t\t\t\t\tcontinue;\n+\n+\t\t\t\tdefault:\n+\t\t\t\t\tbreak SCAN;\n+\t\t\t\t}\n+\t\t\t}\n+\t\t\tif (delcnt < old.length) {\n+\t\t\t\t// This line appears in the new file if it wasn't deleted\n+\t\t\t\t// relative to all ancestors.\n+\t\t\t\t//\n+\t\t\t\tout[old.length].write(buf, ptr, eol - ptr);\n+\t\t\t}\n+\t\t}\n+\t}\n+\n+\tvoid extractFileLines(final StringBuilder sb, final String[] text,\n+\t\t\tfinal int[] offsets) {\n+\t\tfinal byte[] buf = file.buf;\n+\t\tint ptr = startOffset;\n+\t\tint eol = nextLF(buf, ptr);\n+\t\tif (endOffset <= eol)\n+\t\t\treturn;\n+\t\tcopyLine(sb, text, offsets, 0);\n+\t\tSCAN: for (ptr = eol; ptr < endOffset; ptr = eol) {\n+\t\t\teol = nextLF(buf, ptr);\n+\n+\t\t\tif (eol - ptr < old.length + 1) {\n+\t\t\t\t// Line isn't long enough to mention the state of each\n+\t\t\t\t// ancestor. It must be the end of the hunk.\n+\t\t\t\tbreak SCAN;\n+\t\t\t}\n+\n+\t\t\tswitch (buf[ptr]) {\n+\t\t\tcase ' ':\n+\t\t\tcase '-':\n+\t\t\tcase '+':\n+\t\t\t\tbreak;\n+\n+\t\t\tdefault:\n+\t\t\t\t// Line can't possibly be part of this hunk; the first\n+\t\t\t\t// ancestor information isn't recognizable.\n+\t\t\t\t//\n+\t\t\t\tbreak SCAN;\n+\t\t\t}\n+\n+\t\t\tboolean copied = false;\n+\t\t\tfor (int ancestor = 0; ancestor < old.length; ancestor++) {\n+\t\t\t\tswitch (buf[ptr + ancestor]) {\n+\t\t\t\tcase ' ':\n+\t\t\t\tcase '-':\n+\t\t\t\t\tif (copied)\n+\t\t\t\t\t\tskipLine(text, offsets, ancestor);\n+\t\t\t\t\telse {\n+\t\t\t\t\t\tcopyLine(sb, text, offsets, ancestor);\n+\t\t\t\t\t\tcopied = true;\n+\t\t\t\t\t}\n+\t\t\t\t\tcontinue;\n+\n+\t\t\t\tcase '+':\n+\t\t\t\t\tcontinue;\n+\n+\t\t\t\tdefault:\n+\t\t\t\t\tbreak SCAN;\n+\t\t\t\t}\n+\t\t\t}\n+\t\t\tif (!copied) {\n+\t\t\t\t// If none of the ancestors caused the copy then this line\n+\t\t\t\t// must be new across the board, so it only appears in the\n+\t\t\t\t// text of the new file.\n+\t\t\t\t//\n+\t\t\t\tcopyLine(sb, text, offsets, old.length);\n+\t\t\t}\n+\t\t}\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 c91f80e..66c785f 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@@ -39,10 +39,15 @@\n \n import static org.spearce.jgit.lib.Constants.encodeASCII;\n import static org.spearce.jgit.util.RawParseUtils.decode;\n+import static org.spearce.jgit.util.RawParseUtils.decodeNoFallback;\n+import static org.spearce.jgit.util.RawParseUtils.extractBinaryString;\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+import java.io.IOException;\n+import java.nio.charset.CharacterCodingException;\n+import java.nio.charset.Charset;\n import java.util.ArrayList;\n import java.util.Collections;\n import java.util.List;\n@@ -51,6 +56,8 @@\n import org.spearce.jgit.lib.Constants;\n import org.spearce.jgit.lib.FileMode;\n import org.spearce.jgit.util.QuotedString;\n+import org.spearce.jgit.util.RawParseUtils;\n+import org.spearce.jgit.util.TemporaryBuffer;\n \n /** Patch header describing an action for a single file path. */\n public class FileHeader {\n@@ -189,6 +196,115 @@ public int getEndOffset() {\n \t}\n \n \t/**\n+\t * Convert the patch script for this file into a string.\n+\t * <p>\n+\t * The default character encoding ({@link Constants#CHARSET}) is assumed for\n+\t * both the old and new files.\n+\t * \n+\t * @return the patch script, as a Unicode string.\n+\t */\n+\tpublic String getScriptText() {\n+\t\treturn getScriptText(null, null);\n+\t}\n+\n+\t/**\n+\t * Convert the patch script for this file into a string.\n+\t * \n+\t * @param oldCharset\n+\t *            hint character set to decode the old lines with.\n+\t * @param newCharset\n+\t *            hint character set to decode the new lines with.\n+\t * @return the patch script, as a Unicode string.\n+\t */\n+\tpublic String getScriptText(Charset oldCharset, Charset newCharset) {\n+\t\treturn getScriptText(new Charset[] { oldCharset, newCharset });\n+\t}\n+\n+\tprotected String getScriptText(Charset[] charsetGuess) {\n+\t\tif (getHunks().isEmpty()) {\n+\t\t\t// If we have no hunks then we can safely assume the entire\n+\t\t\t// patch is a binary style patch, or a meta-data only style\n+\t\t\t// patch. Either way the encoding of the headers should be\n+\t\t\t// strictly 7-bit US-ASCII and the body is either 7-bit ASCII\n+\t\t\t// (due to the base 85 encoding used for a BinaryHunk) or is\n+\t\t\t// arbitrary noise we have chosen to ignore and not understand\n+\t\t\t// (e.g. the message \"Binary files ... differ\").\n+\t\t\t//\n+\t\t\treturn extractBinaryString(buf, startOffset, endOffset);\n+\t\t}\n+\n+\t\tif (charsetGuess != null && charsetGuess.length != getParentCount() + 1)\n+\t\t\tthrow new IllegalArgumentException(\"Expected \"\n+\t\t\t\t\t+ (getParentCount() + 1) + \" character encoding guesses\");\n+\n+\t\tif (trySimpleConversion(charsetGuess)) {\n+\t\t\tCharset cs = charsetGuess != null ? charsetGuess[0] : null;\n+\t\t\tif (cs == null)\n+\t\t\t\tcs = Constants.CHARSET;\n+\t\t\ttry {\n+\t\t\t\treturn decodeNoFallback(cs, buf, startOffset, endOffset);\n+\t\t\t} catch (CharacterCodingException cee) {\n+\t\t\t\t// Try the much slower, more-memory intensive version which\n+\t\t\t\t// can handle a character set conversion patch.\n+\t\t\t}\n+\t\t}\n+\n+\t\tfinal StringBuilder r = new StringBuilder(endOffset - startOffset);\n+\n+\t\t// Always treat the headers as US-ASCII; Git file names are encoded\n+\t\t// in a C style escape if any character has the high-bit set.\n+\t\t//\n+\t\tfinal int hdrEnd = getHunks().get(0).getStartOffset();\n+\t\tfor (int ptr = startOffset; ptr < hdrEnd;) {\n+\t\t\tfinal int eol = Math.min(hdrEnd, nextLF(buf, ptr));\n+\t\t\tr.append(extractBinaryString(buf, ptr, eol));\n+\t\t\tptr = eol;\n+\t\t}\n+\n+\t\tfinal String[] files = extractFileLines(charsetGuess);\n+\t\tfinal int[] offsets = new int[files.length];\n+\t\tfor (final HunkHeader h : getHunks())\n+\t\t\th.extractFileLines(r, files, offsets);\n+\t\treturn r.toString();\n+\t}\n+\n+\tprivate static boolean trySimpleConversion(final Charset[] charsetGuess) {\n+\t\tif (charsetGuess == null)\n+\t\t\treturn true;\n+\t\tfor (int i = 1; i < charsetGuess.length; i++) {\n+\t\t\tif (charsetGuess[i] != charsetGuess[0])\n+\t\t\t\treturn false;\n+\t\t}\n+\t\treturn true;\n+\t}\n+\n+\tprivate String[] extractFileLines(final Charset[] csGuess) {\n+\t\tfinal TemporaryBuffer[] tmp = new TemporaryBuffer[getParentCount() + 1];\n+\t\ttry {\n+\t\t\tfor (int i = 0; i < tmp.length; i++)\n+\t\t\t\ttmp[i] = new TemporaryBuffer();\n+\t\t\tfor (final HunkHeader h : getHunks())\n+\t\t\t\th.extractFileLines(tmp);\n+\n+\t\t\tfinal String[] r = new String[tmp.length];\n+\t\t\tfor (int i = 0; i < tmp.length; i++) {\n+\t\t\t\tCharset cs = csGuess != null ? csGuess[i] : null;\n+\t\t\t\tif (cs == null)\n+\t\t\t\t\tcs = Constants.CHARSET;\n+\t\t\t\tr[i] = RawParseUtils.decode(cs, tmp[i].toByteArray());\n+\t\t\t}\n+\t\t\treturn r;\n+\t\t} catch (IOException ioe) {\n+\t\t\tthrow new RuntimeException(\"Cannot convert script to text\", ioe);\n+\t\t} finally {\n+\t\t\tfor (final TemporaryBuffer b : tmp) {\n+\t\t\t\tif (b != null)\n+\t\t\t\t\tb.destroy();\n+\t\t\t}\n+\t\t}\n+\t}\n+\n+\t/**\n \t * Get the old name associated with this file.\n \t * <p>\n \t * The meaning of the old name can differ depending on the semantic meaning\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 12c670d..fc30311 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@@ -41,6 +41,9 @@\n import static org.spearce.jgit.util.RawParseUtils.nextLF;\n import static org.spearce.jgit.util.RawParseUtils.parseBase10;\n \n+import java.io.IOException;\n+import java.io.OutputStream;\n+\n import org.spearce.jgit.lib.AbbreviatedObjectId;\n import org.spearce.jgit.util.MutableInteger;\n \n@@ -240,4 +243,87 @@ int parseBody(final Patch script, final int end) {\n \n \t\treturn c;\n \t}\n+\n+\tvoid extractFileLines(final OutputStream[] out) throws IOException {\n+\t\tfinal byte[] buf = file.buf;\n+\t\tint ptr = startOffset;\n+\t\tint eol = nextLF(buf, ptr);\n+\t\tif (endOffset <= eol)\n+\t\t\treturn;\n+\n+\t\t// Treat the hunk header as though it were from the ancestor,\n+\t\t// as it may have a function header appearing after it which\n+\t\t// was copied out of the ancestor file.\n+\t\t//\n+\t\tout[0].write(buf, ptr, eol - ptr);\n+\n+\t\tSCAN: for (ptr = eol; ptr < endOffset; ptr = eol) {\n+\t\t\teol = nextLF(buf, ptr);\n+\t\t\tswitch (buf[ptr]) {\n+\t\t\tcase ' ':\n+\t\t\tcase '\\n':\n+\t\t\tcase '\\\\':\n+\t\t\t\tout[0].write(buf, ptr, eol - ptr);\n+\t\t\t\tout[1].write(buf, ptr, eol - ptr);\n+\t\t\t\tbreak;\n+\t\t\tcase '-':\n+\t\t\t\tout[0].write(buf, ptr, eol - ptr);\n+\t\t\t\tbreak;\n+\t\t\tcase '+':\n+\t\t\t\tout[1].write(buf, ptr, eol - ptr);\n+\t\t\t\tbreak;\n+\t\t\tdefault:\n+\t\t\t\tbreak SCAN;\n+\t\t\t}\n+\t\t}\n+\t}\n+\n+\tvoid extractFileLines(final StringBuilder sb, final String[] text,\n+\t\t\tfinal int[] offsets) {\n+\t\tfinal byte[] buf = file.buf;\n+\t\tint ptr = startOffset;\n+\t\tint eol = nextLF(buf, ptr);\n+\t\tif (endOffset <= eol)\n+\t\t\treturn;\n+\t\tcopyLine(sb, text, offsets, 0);\n+\t\tSCAN: for (ptr = eol; ptr < endOffset; ptr = eol) {\n+\t\t\teol = nextLF(buf, ptr);\n+\t\t\tswitch (buf[ptr]) {\n+\t\t\tcase ' ':\n+\t\t\tcase '\\n':\n+\t\t\tcase '\\\\':\n+\t\t\t\tcopyLine(sb, text, offsets, 0);\n+\t\t\t\tskipLine(text, offsets, 1);\n+\t\t\t\tbreak;\n+\t\t\tcase '-':\n+\t\t\t\tcopyLine(sb, text, offsets, 0);\n+\t\t\t\tbreak;\n+\t\t\tcase '+':\n+\t\t\t\tcopyLine(sb, text, offsets, 1);\n+\t\t\t\tbreak;\n+\t\t\tdefault:\n+\t\t\t\tbreak SCAN;\n+\t\t\t}\n+\t\t}\n+\t}\n+\n+\tprotected void copyLine(final StringBuilder sb, final String[] text,\n+\t\t\tfinal int[] offsets, final int fileIdx) {\n+\t\tfinal String s = text[fileIdx];\n+\t\tfinal int start = offsets[fileIdx];\n+\t\tint end = s.indexOf('\\n', start);\n+\t\tif (end < 0)\n+\t\t\tend = s.length();\n+\t\telse\n+\t\t\tend++;\n+\t\tsb.append(s, start, end);\n+\t\toffsets[fileIdx] = end;\n+\t}\n+\n+\tprotected void skipLine(final String[] text, final int[] offsets,\n+\t\t\tfinal int fileIdx) {\n+\t\tfinal String s = text[fileIdx];\n+\t\tfinal int end = s.indexOf('\\n', offsets[fileIdx]);\n+\t\toffsets[fileIdx] = end < 0 ? s.length() : end + 1;\n+\t}\n }\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 55a3001..ff89e9e 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@@ -472,6 +472,40 @@ public static String decode(final Charset cs, final byte[] buffer) {\n \t */\n \tpublic static String decode(final Charset cs, final byte[] buffer,\n \t\t\tfinal int start, final int end) {\n+\t\ttry {\n+\t\t\treturn decodeNoFallback(cs, buffer, start, end);\n+\t\t} catch (CharacterCodingException e) {\n+\t\t\t// Fall back to an ISO-8859-1 style encoding. At least all of\n+\t\t\t// the bytes will be present in the output.\n+\t\t\t//\n+\t\t\treturn extractBinaryString(buffer, start, end);\n+\t\t}\n+\t}\n+\n+\t/**\n+\t * Decode a region of the buffer under the specified character set if\n+\t * possible.\n+\t * \n+\t * If the byte stream cannot be decoded that way, the platform default is\n+\t * tried and if that too fails, an exception is thrown.\n+\t * \n+\t * @param cs\n+\t *            character set to use when decoding the buffer.\n+\t * @param buffer\n+\t *            buffer to pull raw bytes from.\n+\t * @param start\n+\t *            first position within the buffer to take data from.\n+\t * @param end\n+\t *            one position past the last location within the buffer to take\n+\t *            data from.\n+\t * @return a string representation of the range <code>[start,end)</code>,\n+\t *         after decoding the region through the specified character set.\n+\t * @throws CharacterCodingException\n+\t *             the input is not in any of the tested character sets.\n+\t */\n+\tpublic static String decodeNoFallback(final Charset cs,\n+\t\t\tfinal byte[] buffer, final int start, final int end)\n+\t\t\tthrows CharacterCodingException {\n \t\tfinal ByteBuffer b = ByteBuffer.wrap(buffer, start, end - start);\n \t\tb.mark();\n \n@@ -508,9 +542,26 @@ public static String decode(final Charset cs, final byte[] buffer,\n \t\t\t}\n \t\t}\n \n-\t\t// Fall back to an ISO-8859-1 style encoding. At least all of\n-\t\t// the bytes will be present in the output.\n-\t\t//\n+\t\tthrow new CharacterCodingException();\n+\t}\n+\n+\t/**\n+\t * Decode a region of the buffer under the ISO-8859-1 encoding.\n+\t * \n+\t * Each byte is treated as a single character in the 8859-1 character\n+\t * encoding, performing a raw binary->char conversion.\n+\t * \n+\t * @param buffer\n+\t *            buffer to pull raw bytes from.\n+\t * @param start\n+\t *            first position within the buffer to take data from.\n+\t * @param end\n+\t *            one position past the last location within the buffer to take\n+\t *            data from.\n+\t * @return a string representation of the range <code>[start,end)</code>.\n+\t */\n+\tpublic static String extractBinaryString(final byte[] buffer,\n+\t\t\tfinal int start, final int end) {\n \t\tfinal StringBuilder r = new StringBuilder(end - start);\n \t\tfor (int i = start; i < end; i++)\n \t\t\tr.append((char) (buffer[i] & 0xff));\n-- \n1.6.1.rc3.302.gb14d9\n\n-- \nShawn.\n"}]}