{"thread":{"id":"15964","subject":"[JGIT PATCH] Encode/decode index and tree entries using UTF-8","startedAt":"2008-10-19T13:29:43Z","lastAt":"2008-10-19T18:24:24Z","messageCount":3,"participants":["Robin Rosenberg","Shawn O. Pearce"],"isPatch":true,"patchVersion":1,"patchTotal":null},"messages":[{"id":"93429","messageId":"200810191529.43439.robin.rosenberg.lists@dewire.com","threadId":"15964","inReplyTo":null,"subject":"[JGIT PATCH] Encode/decode index and tree entries using UTF-8","fromName":"Robin Rosenberg","fromEmail":"robin.rosenberg.lists@dewire.com","sentAt":"2008-10-19T13:29:43Z","receivedAt":"2008-10-19T13:29:43Z","isPatch":true,"sender":{"key":"robin.rosenberg@dewire.com","avatar":"https://avatars.githubusercontent.com/u/46357?v=4"},"body":"Decoding uses the same strategy as for commit messages and other string\nentities. Encoding is always done in UTF-8. This is incompatible with\nGit for non-unicode unices, but it leads to the expected behavior on\nWindows and cross-locale sharing of repositories.\n\nSigned-off-by: Robin Rosenberg <robin.rosnberg@dewire.com>\n---\n\nInpired by the recent thread on the gitml, I decideed to clean up jgit a little. I\nknow the GitIndex is soon to be obsoleted, but it it still the class that does\nthe dirty work when committing in Egit and the changes are fairly simple\nanyway.\n\n- Unicode paths will work on all platforms that support unicode, i.e. Windows\nand any unix using a UTF-8 locale, with one small exception. Accented characters\non OS-X probably do not work well.\n- Combined use of unicode on one platform is compatible with non-unicode locales\non other platforms as long as the characters in use are available in the local character\nset.\n\nA side note, invalid byte sequences in unix, e.g. ISO-latin-1 encoded file names cannot\nwork in Java. Such files are inaccessible. Jgit will allow you to rename them in the index,\nbut that is all.\n\n-- robin\n\n .../src/org/spearce/jgit/lib/GitIndex.java         |   27 +++++++++++---------\n .../src/org/spearce/jgit/lib/Tree.java             |   11 +++----\n .../src/org/spearce/jgit/lib/TreeEntry.java        |   13 +++------\n 3 files changed, 25 insertions(+), 26 deletions(-)\n\ndiff --git a/org.spearce.jgit/src/org/spearce/jgit/lib/GitIndex.java b/org.spearce.jgit/src/org/spearce/jgit/lib/GitIndex.java\nindex 22935ab..3d37033 100644\n--- a/org.spearce.jgit/src/org/spearce/jgit/lib/GitIndex.java\n+++ b/org.spearce.jgit/src/org/spearce/jgit/lib/GitIndex.java\n@@ -63,6 +63,7 @@\n import org.spearce.jgit.errors.CorruptObjectException;\n import org.spearce.jgit.errors.NotSupportedException;\n import org.spearce.jgit.util.FS;\n+import org.spearce.jgit.util.RawParseUtils;\n \n /**\n  * A representation of the Git index.\n@@ -178,8 +179,9 @@ public Entry add(File wd, File f) throws IOException {\n \t * @param f\n \t *            the file whose path shall be removed.\n \t * @return true if such a path was found (and thus removed)\n+\t * @throws IOException \n \t */\n-\tpublic boolean remove(File wd, File f) {\n+\tpublic boolean remove(File wd, File f) throws IOException {\n \t\tbyte[] key = makeKey(wd, f);\n \t\treturn entries.remove(key) != null;\n \t}\n@@ -300,11 +302,11 @@ static boolean File_hasExecute() {\n \t\treturn FS.INSTANCE.supportsExecute();\n \t}\n \n-\tstatic byte[] makeKey(File wd, File f) {\n+\tstatic byte[] makeKey(File wd, File f) throws IOException {\n \t\tif (!f.getPath().startsWith(wd.getPath()))\n \t\t\tthrow new Error(\"Path is not in working dir\");\n \t\tString relName = Repository.stripWorkDir(wd, f);\n-\t\treturn relName.getBytes();\n+\t\treturn relName.getBytes(Constants.CHARACTER_ENCODING);\n \t}\n \n \tBoolean filemode;\n@@ -376,7 +378,7 @@ Entry(TreeEntry f, int stage)\n \t\t\t\tsize = -1;\n \t\t\t}\n \t\t\tsha1 = f.getId();\n-\t\t\tname = f.getFullName().getBytes(\"UTF-8\");\n+\t\t\tname = f.getFullName().getBytes(Constants.CHARACTER_ENCODING);\n \t\t\tflags = (short) ((stage << 12) | name.length); // TODO: fix flags\n \t\t}\n \n@@ -580,7 +582,7 @@ private File getFile(File wd) {\n \t\t}\n \n \t\tpublic String toString() {\n-\t\t\treturn new String(name) + \"/SHA-1(\" + sha1.name() + \")/M:\"\n+\t\t\treturn getName() + \"/SHA-1(\" + sha1.name() + \")/M:\"\n \t\t\t\t\t+ new Date(ctime / 1000000L) + \"/C:\"\n \t\t\t\t\t+ new Date(mtime / 1000000L) + \"/d\" + dev + \"/i\" + ino\n \t\t\t\t\t+ \"/m\" + Integer.toString(mode, 8) + \"/u\" + uid + \"/g\"\n@@ -591,7 +593,7 @@ public String toString() {\n \t\t * @return path name for this entry\n \t\t */\n \t\tpublic String getName() {\n-\t\t\treturn new String(name);\n+\t\t\treturn RawParseUtils.decode(Constants.CHARSET, name, 0, name.length);\n \t\t}\n \n \t\t/**\n@@ -731,7 +733,7 @@ void readTree(String prefix, Tree t) throws IOException {\n \t\t\t\treadTree(name, (Tree) te);\n \t\t\t} else {\n \t\t\t\tEntry e = new Entry(te, 0);\n-\t\t\t\tentries.put(name.getBytes(\"UTF-8\"), e);\n+\t\t\t\tentries.put(name.getBytes(Constants.CHARACTER_ENCODING), e);\n \t\t\t}\n \t\t}\n \t}\n@@ -743,7 +745,7 @@ void readTree(String prefix, Tree t) throws IOException {\n \t * @throws IOException\n \t */\n \tpublic Entry addEntry(TreeEntry te) throws IOException {\n-\t\tbyte[] key = te.getFullName().getBytes(\"UTF-8\");\n+\t\tbyte[] key = te.getFullName().getBytes(Constants.CHARACTER_ENCODING);\n \t\tEntry e = new Entry(te, 0);\n \t\tentries.put(key, e);\n \t\treturn e;\n@@ -825,7 +827,7 @@ public ObjectId writeTree() throws IOException {\n \t\t\twhile (trees.size() < newName.length) {\n \t\t\t\tif (!current.existsTree(newName[trees.size() - 1])) {\n \t\t\t\t\tcurrent = new Tree(current, newName[trees.size() - 1]\n-\t\t\t\t\t\t\t.getBytes());\n+\t\t\t\t\t\t\t.getBytes(Constants.CHARACTER_ENCODING));\n \t\t\t\t\tcurrent.getParent().addEntry(current);\n \t\t\t\t\ttrees.push(current);\n \t\t\t\t} else {\n@@ -835,7 +837,7 @@ public ObjectId writeTree() throws IOException {\n \t\t\t\t}\n \t\t\t}\n \t\t\tFileTreeEntry ne = new FileTreeEntry(current, e.sha1,\n-\t\t\t\t\tnewName[newName.length - 1].getBytes(),\n+\t\t\t\t\tnewName[newName.length - 1].getBytes(Constants.CHARACTER_ENCODING),\n \t\t\t\t\t(e.mode & FileMode.EXECUTABLE_FILE.getBits()) == FileMode.EXECUTABLE_FILE.getBits());\n \t\t\tcurrent.addEntry(ne);\n \t\t}\n@@ -880,7 +882,7 @@ int longestCommonPath(String[] a, String[] b) {\n \t * Small beware: Unaccounted for are unmerged entries. You may want\n \t * to abort if members with stage != 0 are found if you are doing\n \t * any updating operations. All stages will be found after one another\n-\t * here later. Currently only one stage per name is returned.\n+\t * here later. Currently only one stage per name is returned.\t\n \t *\n \t * @return The index entries sorted\n \t */\n@@ -896,7 +898,8 @@ int longestCommonPath(String[] a, String[] b) {\n \t * @throws UnsupportedEncodingException\n \t */\n \tpublic Entry getEntry(String path) throws UnsupportedEncodingException {\n-\t\treturn (Entry) entries.get(Repository.gitInternalSlash(path.getBytes(\"ISO-8859-1\")));\n+\t\treturn (Entry) entries.get(Repository.gitInternalSlash(path\n+\t\t\t\t.getBytes(Constants.CHARACTER_ENCODING)));\n \t}\n \n \t/**\ndiff --git a/org.spearce.jgit/src/org/spearce/jgit/lib/Tree.java b/org.spearce.jgit/src/org/spearce/jgit/lib/Tree.java\nindex 25a9a71..3fd3d30 100644\n--- a/org.spearce.jgit/src/org/spearce/jgit/lib/Tree.java\n+++ b/org.spearce.jgit/src/org/spearce/jgit/lib/Tree.java\n@@ -44,6 +44,7 @@\n import org.spearce.jgit.errors.CorruptObjectException;\n import org.spearce.jgit.errors.EntryExistsException;\n import org.spearce.jgit.errors.MissingObjectException;\n+import org.spearce.jgit.util.RawParseUtils;\n \n /**\n  * A representation of a Git tree entry. A Tree is a directory in Git.\n@@ -281,8 +282,7 @@ public FileTreeEntry addFile(final byte[] s, final int offset)\n \n \t\tfinal byte[] newName = substring(s, offset, slash);\n \t\tif (p >= 0)\n-\t\t\tthrow new EntryExistsException(new String(newName,\n-\t\t\t\t\tConstants.CHARACTER_ENCODING));\n+\t\t\tthrow new EntryExistsException(RawParseUtils.decode(Constants.CHARSET, newName, 0, newName.length));\n \t\telse if (slash < s.length) {\n \t\t\tfinal Tree t = new Tree(this, newName);\n \t\t\tinsertEntry(p, t);\n@@ -332,8 +332,8 @@ public Tree addTree(final byte[] s, final int offset) throws IOException {\n \n \t\tfinal byte[] newName = substring(s, offset, slash);\n \t\tif (p >= 0)\n-\t\t\tthrow new EntryExistsException(new String(newName,\n-\t\t\t\t\tConstants.CHARACTER_ENCODING));\n+\t\t\tthrow new EntryExistsException(RawParseUtils.decode(\n+\t\t\t\t\tConstants.CHARSET, newName, 0, newName.length));\n \n \t\tfinal Tree t = new Tree(this, newName);\n \t\tinsertEntry(p, t);\n@@ -355,8 +355,7 @@ public void addEntry(final TreeEntry e) throws IOException {\n \t\t\te.attachParent(this);\n \t\t\tinsertEntry(p, e);\n \t\t} else {\n-\t\t\tthrow new EntryExistsException(new String(e.getNameUTF8(),\n-\t\t\t\t\tConstants.CHARACTER_ENCODING));\n+\t\t\tthrow new EntryExistsException(e.getName());\n \t\t}\n \t}\n \ndiff --git a/org.spearce.jgit/src/org/spearce/jgit/lib/TreeEntry.java b/org.spearce.jgit/src/org/spearce/jgit/lib/TreeEntry.java\nindex 85dda1d..7f58056 100644\n--- a/org.spearce.jgit/src/org/spearce/jgit/lib/TreeEntry.java\n+++ b/org.spearce.jgit/src/org/spearce/jgit/lib/TreeEntry.java\n@@ -39,9 +39,9 @@\n package org.spearce.jgit.lib;\n \n import java.io.IOException;\n-import java.io.UnsupportedEncodingException;\n \n import org.spearce.jgit.lib.GitIndex.Entry;\n+import org.spearce.jgit.util.RawParseUtils;\n \n /**\n  * This class represents an entry in a tree, like a blob or another tree.\n@@ -126,13 +126,10 @@ public Repository getRepository() {\n \t * @return the name of this entry.\n \t */\n \tpublic String getName() {\n-\t\ttry {\n-\t\t\treturn nameUTF8 != null ? new String(nameUTF8,\n-\t\t\t\t\tConstants.CHARACTER_ENCODING) : null;\n-\t\t} catch (UnsupportedEncodingException uee) {\n-\t\t\tthrow new RuntimeException(\"JVM doesn't support \"\n-\t\t\t\t\t+ Constants.CHARACTER_ENCODING, uee);\n-\t\t}\n+\t\tif (nameUTF8 != null)\n+\t\t\treturn RawParseUtils.decode(Constants.CHARSET, nameUTF8, 0,\n+\t\t\t\t\tnameUTF8.length);\n+\t\treturn null;\n \t}\n \n \t/**\n-- \n1.6.0.2.308.gef4a\n"},{"id":"93441","messageId":"20081019171456.GC14786@spearce.org","threadId":"15964","inReplyTo":"200810191529.43439.robin.rosenberg.lists@dewire.com","subject":"Re: [JGIT PATCH] Encode/decode index and tree entries using UTF-8","fromName":"Shawn O. Pearce","fromEmail":"spearce@spearce.org","sentAt":"2008-10-19T17:14:56Z","receivedAt":"2008-10-19T17:14:56Z","isPatch":true,"sender":{"key":"spearce@spearce.org","avatar":"https://avatars.githubusercontent.com/u/34844?v=4"},"body":"Robin Rosenberg <robin.rosenberg.lists@dewire.com> wrote:\n> Decoding uses the same strategy as for commit messages and other string\n> entities. Encoding is always done in UTF-8. This is incompatible with\n> Git for non-unicode unices, but it leads to the expected behavior on\n> Windows and cross-locale sharing of repositories.\n\nFWIW I think this is a good idea.\n \n> Inpired by the recent thread on the gitml, I decideed to clean up jgit a little. I\n> know the GitIndex is soon to be obsoleted, but it it still the class that does\n> the dirty work when committing in Egit and the changes are fairly simple\n> anyway.\n\nYup, I agree.\n\nI mostly agree with the patch, but we have a utility function you\nare missing using:\n \n> @@ -300,11 +302,11 @@ static boolean File_hasExecute() {\n>  \t\treturn FS.INSTANCE.supportsExecute();\n>  \t}\n>  \n> -\tstatic byte[] makeKey(File wd, File f) {\n> +\tstatic byte[] makeKey(File wd, File f) throws IOException {\n>  \t\tif (!f.getPath().startsWith(wd.getPath()))\n>  \t\t\tthrow new Error(\"Path is not in working dir\");\n>  \t\tString relName = Repository.stripWorkDir(wd, f);\n> -\t\treturn relName.getBytes();\n> +\t\treturn relName.getBytes(Constants.CHARACTER_ENCODING);\n>  \t}\n\nInstead of \"relName.getBytes(Constants.CHARACTER_ENCODING)\" use\n\"Constants.encode(relName)\".  Its shorter and faster.\n  \n> @@ -591,7 +593,7 @@ public String toString() {\n>  \t\t * @return path name for this entry\n>  \t\t */\n>  \t\tpublic String getName() {\n> -\t\t\treturn new String(name);\n> +\t\t\treturn RawParseUtils.decode(Constants.CHARSET, name, 0, name.length);\n\nHeh.  That's actually a common idiom.  We probably should add:\n\n\tString decode(final byte[] arr) {\n\t\treturn decode(Constants.CHARSET, arr, 0, arr.length);\n\t}\n\nto RawParseUtils to make these decode whole array calls easier\nto make.\n\nI think you should squash this into your patch, and fix up the\ngetBytes and decode calls as I noted above before we apply this.\n\ndiff --git a/org.spearce.jgit/src/org/spearce/jgit/dircache/DirCacheEntry.java b/org.spearce.jgit/src/org/spearce/jgit/dircache/DirCacheEntry.java\nindex cc683d7..913f3ae 100644\n--- a/org.spearce.jgit/src/org/spearce/jgit/dircache/DirCacheEntry.java\n+++ b/org.spearce.jgit/src/org/spearce/jgit/dircache/DirCacheEntry.java\n@@ -42,7 +42,6 @@\n import java.io.IOException;\n import java.io.InputStream;\n import java.io.OutputStream;\n-import java.nio.ByteBuffer;\n import java.util.Arrays;\n \n import org.spearce.jgit.lib.AnyObjectId;\n@@ -50,6 +49,7 @@\n import org.spearce.jgit.lib.FileMode;\n import org.spearce.jgit.lib.ObjectId;\n import org.spearce.jgit.util.NB;\n+import org.spearce.jgit.util.RawParseUtils;\n \n /**\n  * A single file (or stage of a file) in a {@link DirCache}.\n@@ -405,7 +405,7 @@ public void setObjectIdFromRaw(final byte[] bs, final int p) {\n \t *         returned string.\n \t */\n \tpublic String getPathString() {\n-\t\treturn Constants.CHARSET.decode(ByteBuffer.wrap(path)).toString();\n+\t\treturn RawParseUtils.decode(path);\n \t}\n \n \t/**\ndiff --git a/org.spearce.jgit/src/org/spearce/jgit/dircache/DirCacheTree.java b/org.spearce.jgit/src/org/spearce/jgit/dircache/DirCacheTree.java\nindex 26b6348..589894a 100644\n--- a/org.spearce.jgit/src/org/spearce/jgit/dircache/DirCacheTree.java\n+++ b/org.spearce.jgit/src/org/spearce/jgit/dircache/DirCacheTree.java\n@@ -39,7 +39,6 @@\n \n import java.io.IOException;\n import java.io.OutputStream;\n-import java.nio.ByteBuffer;\n import java.util.Arrays;\n import java.util.Comparator;\n \n@@ -251,8 +250,7 @@ ObjectId getObjectId() {\n \t * @return name of the tree. This does not contain any '/' characters.\n \t */\n \tpublic String getNameString() {\n-\t\tfinal ByteBuffer bb = ByteBuffer.wrap(encodedName);\n-\t\treturn Constants.CHARSET.decode(bb).toString();\n+\t\treturn RawParseUtils.decode(encodedName);\n \t}\n \n \t/**\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 6c0e339..2519f19 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@@ -379,6 +379,21 @@ public static PersonIdent parsePersonIdent(final byte[] raw, final int nameB) {\n \t}\n \n \t/**\n+\t * Decode a region of the buffer from the default character set (UTF-8).\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, the fail-safe ISO-8859-1 encoding is tried.\n+\t * \n+\t * @param buffer\n+\t *            buffer to pull raw bytes from.\n+\t * @return a string representation of the entire buffer, after decoding the\n+\t *         region through the specified character set.\n+\t */\n+\tpublic static String decode(final byte[] buffer) {\n+\t\treturn decode(Constants.CHARSET, buffer, 0, buffer.length);\n+\t}\n+\n+\t/**\n \t * Decode a region of the buffer under the specified character set if possible.\n \t *\n \t * If the byte stream cannot be decoded that way, the platform default is tried\n\n-- \nShawn.\n"},{"id":"93470","messageId":"200810192024.24466.robin.rosenberg.lists@dewire.com","threadId":"15964","inReplyTo":"20081019171456.GC14786@spearce.org","subject":"Re: [JGIT PATCH v2] Encode/decode index and tree entries using UTF-8","fromName":"Robin Rosenberg","fromEmail":"robin.rosenberg.lists@dewire.com","sentAt":"2008-10-19T18:24:24Z","receivedAt":"2008-10-19T18:24:24Z","isPatch":true,"sender":{"key":"robin.rosenberg@dewire.com","avatar":"https://avatars.githubusercontent.com/u/46357?v=4"},"body":"Decoding uses the same strategy as for commit messages and other string\nentities. Encoding is always done in UTF-8. This is incompatible with\nGit for non-unicode unices, but it leads to the expected behavior on\nWindows and cross-locale sharing of repositories.\n\nSigned-off-by: Robin Rosenberg <robin.rosnberg@dewire.com>\n---\n\nsöndagen den 19 oktober 2008 19.14.56 skrev Shawn O. Pearce:\n> Robin Rosenberg <robin.rosenberg.lists@dewire.com> wrote:\n> > Decoding uses the same strategy as for commit messages and other string\n> > entities. Encoding is always done in UTF-8. This is incompatible with\n> > Git for non-unicode unices, but it leads to the expected behavior on\n> > Windows and cross-locale sharing of repositories.\n> \n> FWIW I think this is a good idea.\n\nOk, so here's the update.  We might want to move the encode out of Constants\ntoo as it is no longer a utility for constants.\n\n-- robin\n\n .../src/org/spearce/jgit/lib/GitIndex.java         |   27 ++++++++--------\n .../src/org/spearce/jgit/lib/Tree.java             |   16 ++++-----\n .../src/org/spearce/jgit/lib/TreeEntry.java        |   14 +++-----\n .../src/org/spearce/jgit/util/RawParseUtils.java   |   32 ++++++++++++++++++++\n 4 files changed, 58 insertions(+), 31 deletions(-)\n\ndiff --git a/org.spearce.jgit/src/org/spearce/jgit/lib/GitIndex.java b/org.spearce.jgit/src/org/spearce/jgit/lib/GitIndex.java\nindex 22935ab..bafddef 100644\n--- a/org.spearce.jgit/src/org/spearce/jgit/lib/GitIndex.java\n+++ b/org.spearce.jgit/src/org/spearce/jgit/lib/GitIndex.java\n@@ -63,6 +63,7 @@\n import org.spearce.jgit.errors.CorruptObjectException;\n import org.spearce.jgit.errors.NotSupportedException;\n import org.spearce.jgit.util.FS;\n+import org.spearce.jgit.util.RawParseUtils;\n \n /**\n  * A representation of the Git index.\n@@ -178,8 +179,9 @@ public Entry add(File wd, File f) throws IOException {\n \t * @param f\n \t *            the file whose path shall be removed.\n \t * @return true if such a path was found (and thus removed)\n+\t * @throws IOException \n \t */\n-\tpublic boolean remove(File wd, File f) {\n+\tpublic boolean remove(File wd, File f) throws IOException {\n \t\tbyte[] key = makeKey(wd, f);\n \t\treturn entries.remove(key) != null;\n \t}\n@@ -300,11 +302,11 @@ static boolean File_hasExecute() {\n \t\treturn FS.INSTANCE.supportsExecute();\n \t}\n \n-\tstatic byte[] makeKey(File wd, File f) {\n+\tstatic byte[] makeKey(File wd, File f) throws IOException {\n \t\tif (!f.getPath().startsWith(wd.getPath()))\n \t\t\tthrow new Error(\"Path is not in working dir\");\n \t\tString relName = Repository.stripWorkDir(wd, f);\n-\t\treturn relName.getBytes();\n+\t\treturn Constants.encode(relName);\n \t}\n \n \tBoolean filemode;\n@@ -376,7 +378,7 @@ Entry(TreeEntry f, int stage)\n \t\t\t\tsize = -1;\n \t\t\t}\n \t\t\tsha1 = f.getId();\n-\t\t\tname = f.getFullName().getBytes(\"UTF-8\");\n+\t\t\tname = Constants.encode(f.getFullName());\n \t\t\tflags = (short) ((stage << 12) | name.length); // TODO: fix flags\n \t\t}\n \n@@ -580,7 +582,7 @@ private File getFile(File wd) {\n \t\t}\n \n \t\tpublic String toString() {\n-\t\t\treturn new String(name) + \"/SHA-1(\" + sha1.name() + \")/M:\"\n+\t\t\treturn getName() + \"/SHA-1(\" + sha1.name() + \")/M:\"\n \t\t\t\t\t+ new Date(ctime / 1000000L) + \"/C:\"\n \t\t\t\t\t+ new Date(mtime / 1000000L) + \"/d\" + dev + \"/i\" + ino\n \t\t\t\t\t+ \"/m\" + Integer.toString(mode, 8) + \"/u\" + uid + \"/g\"\n@@ -591,7 +593,7 @@ public String toString() {\n \t\t * @return path name for this entry\n \t\t */\n \t\tpublic String getName() {\n-\t\t\treturn new String(name);\n+\t\t\treturn RawParseUtils.decode(name);\n \t\t}\n \n \t\t/**\n@@ -731,7 +733,7 @@ void readTree(String prefix, Tree t) throws IOException {\n \t\t\t\treadTree(name, (Tree) te);\n \t\t\t} else {\n \t\t\t\tEntry e = new Entry(te, 0);\n-\t\t\t\tentries.put(name.getBytes(\"UTF-8\"), e);\n+\t\t\t\tentries.put(Constants.encode(name), e);\n \t\t\t}\n \t\t}\n \t}\n@@ -743,7 +745,7 @@ void readTree(String prefix, Tree t) throws IOException {\n \t * @throws IOException\n \t */\n \tpublic Entry addEntry(TreeEntry te) throws IOException {\n-\t\tbyte[] key = te.getFullName().getBytes(\"UTF-8\");\n+\t\tbyte[] key = Constants.encode(te.getFullName());\n \t\tEntry e = new Entry(te, 0);\n \t\tentries.put(key, e);\n \t\treturn e;\n@@ -824,8 +826,7 @@ public ObjectId writeTree() throws IOException {\n \t\t\t}\n \t\t\twhile (trees.size() < newName.length) {\n \t\t\t\tif (!current.existsTree(newName[trees.size() - 1])) {\n-\t\t\t\t\tcurrent = new Tree(current, newName[trees.size() - 1]\n-\t\t\t\t\t\t\t.getBytes());\n+\t\t\t\t\tcurrent = new Tree(current, Constants.encode(newName[trees.size() - 1]));\n \t\t\t\t\tcurrent.getParent().addEntry(current);\n \t\t\t\t\ttrees.push(current);\n \t\t\t\t} else {\n@@ -835,7 +836,7 @@ public ObjectId writeTree() throws IOException {\n \t\t\t\t}\n \t\t\t}\n \t\t\tFileTreeEntry ne = new FileTreeEntry(current, e.sha1,\n-\t\t\t\t\tnewName[newName.length - 1].getBytes(),\n+\t\t\t\t\tConstants.encode(newName[newName.length - 1]),\n \t\t\t\t\t(e.mode & FileMode.EXECUTABLE_FILE.getBits()) == FileMode.EXECUTABLE_FILE.getBits());\n \t\t\tcurrent.addEntry(ne);\n \t\t}\n@@ -880,7 +881,7 @@ int longestCommonPath(String[] a, String[] b) {\n \t * Small beware: Unaccounted for are unmerged entries. You may want\n \t * to abort if members with stage != 0 are found if you are doing\n \t * any updating operations. All stages will be found after one another\n-\t * here later. Currently only one stage per name is returned.\n+\t * here later. Currently only one stage per name is returned.\t\n \t *\n \t * @return The index entries sorted\n \t */\n@@ -896,7 +897,7 @@ int longestCommonPath(String[] a, String[] b) {\n \t * @throws UnsupportedEncodingException\n \t */\n \tpublic Entry getEntry(String path) throws UnsupportedEncodingException {\n-\t\treturn (Entry) entries.get(Repository.gitInternalSlash(path.getBytes(\"ISO-8859-1\")));\n+\t\treturn (Entry) entries.get(Repository.gitInternalSlash(Constants.encode(path)));\n \t}\n \n \t/**\ndiff --git a/org.spearce.jgit/src/org/spearce/jgit/lib/Tree.java b/org.spearce.jgit/src/org/spearce/jgit/lib/Tree.java\nindex 25a9a71..0ecd04d 100644\n--- a/org.spearce.jgit/src/org/spearce/jgit/lib/Tree.java\n+++ b/org.spearce.jgit/src/org/spearce/jgit/lib/Tree.java\n@@ -44,6 +44,7 @@\n import org.spearce.jgit.errors.CorruptObjectException;\n import org.spearce.jgit.errors.EntryExistsException;\n import org.spearce.jgit.errors.MissingObjectException;\n+import org.spearce.jgit.util.RawParseUtils;\n \n /**\n  * A representation of a Git tree entry. A Tree is a directory in Git.\n@@ -251,7 +252,7 @@ public void unload() {\n \t * @throws IOException\n \t */\n \tpublic FileTreeEntry addFile(final String name) throws IOException {\n-\t\treturn addFile(Repository.gitInternalSlash(name.getBytes(Constants.CHARACTER_ENCODING)), 0);\n+\t\treturn addFile(Repository.gitInternalSlash(Constants.encode(name)), 0);\n \t}\n \n \t/**\n@@ -281,8 +282,7 @@ public FileTreeEntry addFile(final byte[] s, final int offset)\n \n \t\tfinal byte[] newName = substring(s, offset, slash);\n \t\tif (p >= 0)\n-\t\t\tthrow new EntryExistsException(new String(newName,\n-\t\t\t\t\tConstants.CHARACTER_ENCODING));\n+\t\t\tthrow new EntryExistsException(RawParseUtils.decode(newName));\n \t\telse if (slash < s.length) {\n \t\t\tfinal Tree t = new Tree(this, newName);\n \t\t\tinsertEntry(p, t);\n@@ -304,7 +304,7 @@ else if (slash < s.length) {\n \t * @throws IOException\n \t */\n \tpublic Tree addTree(final String name) throws IOException {\n-\t\treturn addTree(Repository.gitInternalSlash(name.getBytes(Constants.CHARACTER_ENCODING)), 0);\n+\t\treturn addTree(Repository.gitInternalSlash(Constants.encode(name)), 0);\n \t}\n \n \t/**\n@@ -332,8 +332,7 @@ public Tree addTree(final byte[] s, final int offset) throws IOException {\n \n \t\tfinal byte[] newName = substring(s, offset, slash);\n \t\tif (p >= 0)\n-\t\t\tthrow new EntryExistsException(new String(newName,\n-\t\t\t\t\tConstants.CHARACTER_ENCODING));\n+\t\t\tthrow new EntryExistsException(RawParseUtils.decode(newName));\n \n \t\tfinal Tree t = new Tree(this, newName);\n \t\tinsertEntry(p, t);\n@@ -355,8 +354,7 @@ public void addEntry(final TreeEntry e) throws IOException {\n \t\t\te.attachParent(this);\n \t\t\tinsertEntry(p, e);\n \t\t} else {\n-\t\t\tthrow new EntryExistsException(new String(e.getNameUTF8(),\n-\t\t\t\t\tConstants.CHARACTER_ENCODING));\n+\t\t\tthrow new EntryExistsException(e.getName());\n \t\t}\n \t}\n \n@@ -450,7 +448,7 @@ public boolean existsBlob(String path) throws IOException {\n \t}\n \n \tprivate TreeEntry findMember(final String s, byte slast) throws IOException {\n-\t\treturn findMember(Repository.gitInternalSlash(s.getBytes(Constants.CHARACTER_ENCODING)), slast, 0);\n+\t\treturn findMember(Repository.gitInternalSlash(Constants.encode(s)), slast, 0);\n \t}\n \n \tprivate TreeEntry findMember(final byte[] s, final byte slast, final int offset)\ndiff --git a/org.spearce.jgit/src/org/spearce/jgit/lib/TreeEntry.java b/org.spearce.jgit/src/org/spearce/jgit/lib/TreeEntry.java\nindex 85dda1d..c95863c 100644\n--- a/org.spearce.jgit/src/org/spearce/jgit/lib/TreeEntry.java\n+++ b/org.spearce.jgit/src/org/spearce/jgit/lib/TreeEntry.java\n@@ -39,9 +39,9 @@\n package org.spearce.jgit.lib;\n \n import java.io.IOException;\n-import java.io.UnsupportedEncodingException;\n \n import org.spearce.jgit.lib.GitIndex.Entry;\n+import org.spearce.jgit.util.RawParseUtils;\n \n /**\n  * This class represents an entry in a tree, like a blob or another tree.\n@@ -126,13 +126,9 @@ public Repository getRepository() {\n \t * @return the name of this entry.\n \t */\n \tpublic String getName() {\n-\t\ttry {\n-\t\t\treturn nameUTF8 != null ? new String(nameUTF8,\n-\t\t\t\t\tConstants.CHARACTER_ENCODING) : null;\n-\t\t} catch (UnsupportedEncodingException uee) {\n-\t\t\tthrow new RuntimeException(\"JVM doesn't support \"\n-\t\t\t\t\t+ Constants.CHARACTER_ENCODING, uee);\n-\t\t}\n+\t\tif (nameUTF8 != null)\n+\t\t\treturn RawParseUtils.decode(nameUTF8);\n+\t\treturn null;\n \t}\n \n \t/**\n@@ -142,7 +138,7 @@ public String getName() {\n \t * @throws IOException\n \t */\n \tpublic void rename(final String n) throws IOException {\n-\t\trename(n.getBytes(Constants.CHARACTER_ENCODING));\n+\t\trename(Constants.encode(n));\n \t}\n \n \t/**\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 6c0e339..4b96439 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@@ -379,6 +379,38 @@ public static PersonIdent parsePersonIdent(final byte[] raw, final int nameB) {\n \t}\n \n \t/**\n+\t * Decode a buffer under UTF-8, if possible.\n+\t *\n+\t * If the byte stream cannot be decoded that way, the platform default is tried\n+\t * and if that too fails, the fail-safe ISO-8859-1 encoding is tried.\n+\t * \n+\t * @param buffer\n+\t *            buffer to pull raw bytes 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 */\n+\tpublic static String decode(final byte[] buffer) {\n+\t\treturn decode(Constants.CHARSET, buffer, 0, buffer.length);\n+\t}\n+\n+\t/**\n+\t * Decode a buffer under the specified character set if possible.\n+\t *\n+\t * If the byte stream cannot be decoded that way, the platform default is tried\n+\t * and if that too fails, the fail-safe ISO-8859-1 encoding is tried.\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 * @return a string representation of the range <code>[start,end)</code>,\n+\t *         after decoding the region through the specified character set.\n+\t */\n+\tpublic static String decode(final Charset cs, final byte[] buffer) {\n+\t\treturn decode(cs, buffer, 0, buffer.length);\n+\t}\n+\n+\t/**\n \t * Decode a region of the buffer under the specified character set if possible.\n \t *\n \t * If the byte stream cannot be decoded that way, the platform default is tried\n-- \n1.6.0.2.308.gef4a\n"}]}