{"thread":{"id":"55097","subject":"[PATCH] git-p4: handle non-unicode characters in p4 cl","startedAt":"2021-02-03T17:01:46Z","lastAt":"2021-02-04T19:18:53Z","messageCount":4,"participants":["Feiyang via GitGitGadget","Junio C Hamano","Luke Diamand","Andrew Oakley"],"isPatch":true,"patchVersion":1,"patchTotal":null},"messages":[{"id":"416063","messageId":"pull.864.git.1612371600332.gitgitgadget@gmail.com","threadId":"55097","inReplyTo":null,"subject":"[PATCH] git-p4: handle non-unicode characters in p4 cl","fromName":"Feiyang via GitGitGadget","fromEmail":"gitgitgadget@gmail.com","sentAt":"2021-02-03T16:59:59Z","receivedAt":"2021-02-03T17:01:46Z","isPatch":true,"sender":{"key":"name:Feiyang","avatar":null},"body":"From: Feiynag Xue <fxue@roku.com>\n\nP4 allows non-unicode characters in changelist description body,\nso git-p4 needs to be character encoding aware when reading p4 cl\n\nThis change adds 2 config options, one specifies encoding,\nthe other specifies erro handling upon unrecognized character.\nThose configs  apply when it reads p4 description text, mostly\nfrom commands \"p4 describe\" and \"p4 changes\".\n\nSigned-off-by: Feiynag Xue <fxue@roku.com>\n---\n    git-p4: handle non-unicode characters in p4 changelist description\n    \n    P4 allows non-unicode characters in changelist description body, so\n    git-p4 needs to be character encoding aware when reading p4 cl.\n    \n    This change adds 2 config options: one specifies encoding, the other\n    specifies erro handling upon unrecognized character. Those configs apply\n    when it reads p4 description text, mostly from commands \"p4 describe\"\n    and \"p4 changes\".\n    \n    ------------------------------------------------------------------------\n    \n    I have an open question in mind: what might be the best default config\n    to use?\n    \n    Currently the python's bytes.decode() is called with default utf-8 and\n    strict error handling, so git-p4 pukes on non-unicode characters. I\n    encountered it when git p4 sync attempts to ingest a certain CL.\n    \n    It seems to make sense to default to replace so that it gets rid of\n    non-unicode chars while trying to retain information. However, i am\n    uncertain on if we have use cases where it relies on the\n    stop-on-non-unicode behavior. (Hypothetically say an automation that's\n    expected to return error on non-unicode char in order to stop them from\n    propagating further?)\n    \n    ------------------------------------------------------------------------\n    \n    I tested it with git p4 sync to a P4 CL that somehow has non-unicode\n    control character in description. With\n    git-p4.cldescencodingerrhandling=ignore, it proceeded without error.\n\nPublished-As: https://github.com/gitgitgadget/git/releases/tag/pr-864%2Ffeiyeung%2Fdescription-text-encoding-handling-v1\nFetch-It-Via: git fetch https://github.com/gitgitgadget/git pr-864/feiyeung/description-text-encoding-handling-v1\nPull-Request: https://github.com/gitgitgadget/git/pull/864\n\n Documentation/git-p4.txt | 13 +++++++++++++\n git-p4.py                | 12 +++++++++++-\n 2 files changed, 24 insertions(+), 1 deletion(-)\n\ndiff --git a/Documentation/git-p4.txt b/Documentation/git-p4.txt\nindex f89e68b424c..01a0e0b1067 100644\n--- a/Documentation/git-p4.txt\n+++ b/Documentation/git-p4.txt\n@@ -638,6 +638,19 @@ git-p4.pathEncoding::\n \tto transcode the paths to UTF-8. As an example, Perforce on Windows\n \toften uses \"cp1252\" to encode path names.\n \n+git-p4.clDescEncoding::\n+\tPerforce allows non-unicode characters in changelist description. \n+\tUse this config to tell git-p4 what encoding Perforce had used for \n+\tdescription text. This encoding is used to transcode the text to\n+\tUTF-8. Defaults to \"utf_8\".\n+\n+git-p4.clDescNonUnicodeHandling::\n+\tPerforce allows non-unicode characters in changelist description. \n+\tUse this config to tell git-p4 what to do when it does not recognize \n+\tthe character encoding in description body. Defaults to \"strict\" for \n+\tstopping upon encounter. \"ignore\" for skipping unrecognized\n+\tcharacters; \"replace\" for attempting to convert into UTF-8. \n+\n git-p4.largeFileSystem::\n \tSpecify the system that is used for large (binary) files. Please note\n \tthat large file systems do not support the 'git p4 submit' command.\ndiff --git a/git-p4.py b/git-p4.py\nindex 09c9e93ac40..abbeb9156bd 100755\n--- a/git-p4.py\n+++ b/git-p4.py\n@@ -206,6 +206,13 @@ def decode_path(path):\n                 print('Path with non-ASCII characters detected. Used {} to decode: {}'.format(encoding, path))\n         return path\n \n+def decode_changlist_description(text):\n+    \"\"\"Decode bytes or bytearray using configured changelist description encoding options\n+    \"\"\"\n+    encoding = gitConfig('git-p4.clDescEncoding') or 'utf_8'\n+    err_handling = gitConfig('git-p4.clDescEncodingErrHandling') or 'strict'\n+    return text.decode(encoding, err_handling)\n+\n def run_git_hook(cmd, param=[]):\n     \"\"\"Execute a hook if the hook exists.\"\"\"\n     if verbose:\n@@ -771,7 +778,10 @@ def p4CmdList(cmd, stdin=None, stdin_mode='w+b', cb=None, skip_info=False,\n                 for key, value in entry.items():\n                     key = key.decode()\n                     if isinstance(value, bytes) and not (key in ('data', 'path', 'clientFile') or key.startswith('depotFile')):\n-                        value = value.decode()\n+                        if key == 'desc':\n+                            value = decode_changlist_description(value)\n+                        else:\n+                            value = value.decode()\n                     decoded_entry[key] = value\n                 # Parse out data if it's an error response\n                 if decoded_entry.get('code') == 'error' and 'data' in decoded_entry:\n\nbase-commit: e6362826a0409539642a5738db61827e5978e2e4\n-- \ngitgitgadget\n"},{"id":"416099","messageId":"xmqqpn1gbzdh.fsf@gitster.c.googlers.com","threadId":"55097","inReplyTo":"pull.864.git.1612371600332.gitgitgadget@gmail.com","subject":"Re: [PATCH] git-p4: handle non-unicode characters in p4 cl","fromName":"Junio C Hamano","fromEmail":"gitster@pobox.com","sentAt":"2021-02-03T21:44:10Z","receivedAt":"2021-02-03T21:45:09Z","isPatch":true,"sender":{"key":"gitster@pobox.com","avatar":"https://avatars.githubusercontent.com/u/54884?v=4"},"body":"\"Feiyang via GitGitGadget\" <gitgitgadget@gmail.com> writes:\n\n> From: Feiynag Xue <fxue@roku.com>\n>\n> P4 allows non-unicode characters in changelist description body,\n> so git-p4 needs to be character encoding aware when reading p4 cl\n>\n> This change adds 2 config options, one specifies encoding,\n> the other specifies erro handling upon unrecognized character.\n> Those configs  apply when it reads p4 description text, mostly\n> from commands \"p4 describe\" and \"p4 changes\".\n>\n> Signed-off-by: Feiynag Xue <fxue@roku.com>\n> ---\n\nAdding a few people who had meaningful (read: needs some Perforce\nknowledge) changes to this part of the codebase to Cc: to ask for\ntheir reviews.\n\n>     git-p4: handle non-unicode characters in p4 changelist description\n>     \n>     P4 allows non-unicode characters in changelist description body, so\n>     git-p4 needs to be character encoding aware when reading p4 cl.\n>     \n>     This change adds 2 config options: one specifies encoding, the other\n>     specifies erro handling upon unrecognized character. Those configs apply\n>     when it reads p4 description text, mostly from commands \"p4 describe\"\n>     and \"p4 changes\".\n>     \n>     ------------------------------------------------------------------------\n>     \n>     I have an open question in mind: what might be the best default config\n>     to use?\n>     \n>     Currently the python's bytes.decode() is called with default utf-8 and\n>     strict error handling, so git-p4 pukes on non-unicode characters. I\n>     encountered it when git p4 sync attempts to ingest a certain CL.\n>     \n>     It seems to make sense to default to replace so that it gets rid of\n>     non-unicode chars while trying to retain information. However, i am\n>     uncertain on if we have use cases where it relies on the\n>     stop-on-non-unicode behavior. (Hypothetically say an automation that's\n>     expected to return error on non-unicode char in order to stop them from\n>     propagating further?)\n>     \n>     ------------------------------------------------------------------------\n>     \n>     I tested it with git p4 sync to a P4 CL that somehow has non-unicode\n>     control character in description. With\n>     git-p4.cldescencodingerrhandling=ignore, it proceeded without error.\n>\n> Published-As: https://github.com/gitgitgadget/git/releases/tag/pr-864%2Ffeiyeung%2Fdescription-text-encoding-handling-v1\n> Fetch-It-Via: git fetch https://github.com/gitgitgadget/git pr-864/feiyeung/description-text-encoding-handling-v1\n> Pull-Request: https://github.com/gitgitgadget/git/pull/864\n>\n>  Documentation/git-p4.txt | 13 +++++++++++++\n>  git-p4.py                | 12 +++++++++++-\n>  2 files changed, 24 insertions(+), 1 deletion(-)\n>\n> diff --git a/Documentation/git-p4.txt b/Documentation/git-p4.txt\n> index f89e68b424c..01a0e0b1067 100644\n> --- a/Documentation/git-p4.txt\n> +++ b/Documentation/git-p4.txt\n> @@ -638,6 +638,19 @@ git-p4.pathEncoding::\n>  \tto transcode the paths to UTF-8. As an example, Perforce on Windows\n>  \toften uses \"cp1252\" to encode path names.\n>  \n> +git-p4.clDescEncoding::\n> +\tPerforce allows non-unicode characters in changelist description. \n> +\tUse this config to tell git-p4 what encoding Perforce had used for \n> +\tdescription text. This encoding is used to transcode the text to\n> +\tUTF-8. Defaults to \"utf_8\".\n\nWould it still work if you replaced \"utf_8\" here with \"UTF-8\"?  If\nwe can use \"UTF-8\", this description (and the code that does so)\nwould read much less awkward, I would think.\n\n> +git-p4.clDescNonUnicodeHandling::\n> +\tPerforce allows non-unicode characters in changelist description. \n> +\tUse this config to tell git-p4 what to do when it does not recognize \n> +\tthe character encoding in description body. Defaults to \"strict\" for \n> +\tstopping upon encounter. \"ignore\" for skipping unrecognized\n> +\tcharacters; \"replace\" for attempting to convert into UTF-8. \n> +\n>  git-p4.largeFileSystem::\n>  \tSpecify the system that is used for large (binary) files. Please note\n>  \tthat large file systems do not support the 'git p4 submit' command.\n> diff --git a/git-p4.py b/git-p4.py\n> index 09c9e93ac40..abbeb9156bd 100755\n> --- a/git-p4.py\n> +++ b/git-p4.py\n> @@ -206,6 +206,13 @@ def decode_path(path):\n>                  print('Path with non-ASCII characters detected. Used {} to decode: {}'.format(encoding, path))\n>          return path\n>  \n> +def decode_changlist_description(text):\n> +    \"\"\"Decode bytes or bytearray using configured changelist description encoding options\n> +    \"\"\"\n> +    encoding = gitConfig('git-p4.clDescEncoding') or 'utf_8'\n> +    err_handling = gitConfig('git-p4.clDescEncodingErrHandling') or 'strict'\n> +    return text.decode(encoding, err_handling)\n> +\n>  def run_git_hook(cmd, param=[]):\n>      \"\"\"Execute a hook if the hook exists.\"\"\"\n>      if verbose:\n> @@ -771,7 +778,10 @@ def p4CmdList(cmd, stdin=None, stdin_mode='w+b', cb=None, skip_info=False,\n>                  for key, value in entry.items():\n>                      key = key.decode()\n>                      if isinstance(value, bytes) and not (key in ('data', 'path', 'clientFile') or key.startswith('depotFile')):\n> -                        value = value.decode()\n> +                        if key == 'desc':\n> +                            value = decode_changlist_description(value)\n> +                        else:\n> +                            value = value.decode()\n>                      decoded_entry[key] = value\n>                  # Parse out data if it's an error response\n>                  if decoded_entry.get('code') == 'error' and 'data' in decoded_entry:\n>\n> base-commit: e6362826a0409539642a5738db61827e5978e2e4\n"},{"id":"416118","messageId":"CAE5ih7-F9efsiV5AQmw3ocjiy+BT6ZAT5fA0Lx0OSkVTO8Kqjg@mail.gmail.com","threadId":"55097","inReplyTo":"BD039BE8-643F-4F61-A0DB-E3581C6B6B10@feiyangxue.com","subject":"Re: [PATCH] git-p4: handle non-unicode characters in p4 cl","fromName":"Luke Diamand","fromEmail":"luke@diamand.org","sentAt":"2021-02-04T00:11:20Z","receivedAt":"2021-02-04T00:12:31Z","isPatch":true,"sender":{"key":"luke@diamand.org","avatar":"https://avatars.githubusercontent.com/u/5330967?v=4"},"body":"We've started getting this quite a lot as we switched to a new P4\nserver and I suspect that the i18N options are incorrect. So I think\nthis would be welcome.\n\nA test case would be useful, as debugging these decoding problems is a\nbit of a nightmare.\n\nLuke\n\nOn Wed, 3 Feb 2021 at 22:42, Feiyang Xue <me@feiyangxue.com> wrote:\n>\n>\n>\n> On Feb 3, 2021, at 3:44 PM, Junio C Hamano <gitster@pobox.com> wrote:\n>\n> \"Feiyang via GitGitGadget\" <gitgitgadget@gmail.com> writes:\n>\n> From: Feiynag Xue <fxue@roku.com>\n>\n> P4 allows non-unicode characters in changelist description body,\n> so git-p4 needs to be character encoding aware when reading p4 cl\n>\n> This change adds 2 config options, one specifies encoding,\n> the other specifies erro handling upon unrecognized character.\n> Those configs  apply when it reads p4 description text, mostly\n> from commands \"p4 describe\" and \"p4 changes\".\n>\n> Signed-off-by: Feiynag Xue <fxue@roku.com>\n> ---\n>\n>\n> Adding a few people who had meaningful (read: needs some Perforce\n> knowledge) changes to this part of the codebase to Cc: to ask for\n> their reviews.\n>\n>\n> Adding Yang Zhao <yang.zhao@skyboxlabs.com>, who had made character\n> encodings related changes for paths.\n>\n> Adding Scott Lamb <slamb@slamb.org>, who had made changes to this\n> “p4CmdList()” method.\n>\n>\n>\n>    git-p4: handle non-unicode characters in p4 changelist description\n>\n>    P4 allows non-unicode characters in changelist description body, so\n>    git-p4 needs to be character encoding aware when reading p4 cl.\n>\n>    This change adds 2 config options: one specifies encoding, the other\n>    specifies erro handling upon unrecognized character. Those configs apply\n>    when it reads p4 description text, mostly from commands \"p4 describe\"\n>    and \"p4 changes\".\n>\n>    ------------------------------------------------------------------------\n>\n>    I have an open question in mind: what might be the best default config\n>    to use?\n>\n>    Currently the python's bytes.decode() is called with default utf-8 and\n>    strict error handling, so git-p4 pukes on non-unicode characters. I\n>    encountered it when git p4 sync attempts to ingest a certain CL.\n>\n>    It seems to make sense to default to replace so that it gets rid of\n>    non-unicode chars while trying to retain information. However, i am\n>    uncertain on if we have use cases where it relies on the\n>    stop-on-non-unicode behavior. (Hypothetically say an automation that's\n>    expected to return error on non-unicode char in order to stop them from\n>    propagating further?)\n>\n>    ------------------------------------------------------------------------\n>\n>    I tested it with git p4 sync to a P4 CL that somehow has non-unicode\n>    control character in description. With\n>    git-p4.cldescencodingerrhandling=ignore, it proceeded without error.\n>\n> Published-As: https://github.com/gitgitgadget/git/releases/tag/pr-864%2Ffeiyeung%2Fdescription-text-encoding-handling-v1\n> Fetch-It-Via: git fetch https://github.com/gitgitgadget/git pr-864/feiyeung/description-text-encoding-handling-v1\n> Pull-Request: https://github.com/gitgitgadget/git/pull/864\n>\n> Documentation/git-p4.txt | 13 +++++++++++++\n> git-p4.py                | 12 +++++++++++-\n> 2 files changed, 24 insertions(+), 1 deletion(-)\n>\n> diff --git a/Documentation/git-p4.txt b/Documentation/git-p4.txt\n> index f89e68b424c..01a0e0b1067 100644\n> --- a/Documentation/git-p4.txt\n> +++ b/Documentation/git-p4.txt\n> @@ -638,6 +638,19 @@ git-p4.pathEncoding::\n> to transcode the paths to UTF-8. As an example, Perforce on Windows\n> often uses \"cp1252\" to encode path names.\n>\n> +git-p4.clDescEncoding::\n> + Perforce allows non-unicode characters in changelist description.\n> + Use this config to tell git-p4 what encoding Perforce had used for\n> + description text. This encoding is used to transcode the text to\n> + UTF-8. Defaults to \"utf_8\".\n>\n>\n> Would it still work if you replaced \"utf_8\" here with \"UTF-8\"?  If\n> we can use \"UTF-8\", this description (and the code that does so)\n> would read much less awkward, I would think.\n>\n>\n> I doubt “UTF-8” would work; I do believe the lower case “utf-8” would.\n>\n> Looking at Python3 documentation on encodings, UTF-8 is specified as “utf_8”.\n> It allows aliases of using dash to replace underscore, as pointed out by the\n> samge page: https://docs.python.org/3/library/codecs.html#standard-encodings\n> > Notice that spelling alternatives that only differ in case or use a hyphen\n> > instead of an underscore are also valid aliases; therefore, e.g. 'utf-8’\n> > is a valid alias for the 'utf_8' codec.\n>\n> I used underscore one “utf_8” for consistency reason: this file already has\n> uses of “utf_8”.\n>\n>\n>\n> +git-p4.clDescNonUnicodeHandling::\n> + Perforce allows non-unicode characters in changelist description.\n> + Use this config to tell git-p4 what to do when it does not recognize\n> + the character encoding in description body. Defaults to \"strict\" for\n> + stopping upon encounter. \"ignore\" for skipping unrecognized\n> + characters; \"replace\" for attempting to convert into UTF-8.\n> +\n> git-p4.largeFileSystem::\n> Specify the system that is used for large (binary) files. Please note\n> that large file systems do not support the 'git p4 submit' command.\n> diff --git a/git-p4.py b/git-p4.py\n> index 09c9e93ac40..abbeb9156bd 100755\n> --- a/git-p4.py\n> +++ b/git-p4.py\n> @@ -206,6 +206,13 @@ def decode_path(path):\n>                 print('Path with non-ASCII characters detected. Used {} to decode: {}'.format(encoding, path))\n>         return path\n>\n> +def decode_changlist_description(text):\n> +    \"\"\"Decode bytes or bytearray using configured changelist description encoding options\n> +    \"\"\"\n> +    encoding = gitConfig('git-p4.clDescEncoding') or 'utf_8'\n> +    err_handling = gitConfig('git-p4.clDescEncodingErrHandling') or 'strict'\n> +    return text.decode(encoding, err_handling)\n> +\n> def run_git_hook(cmd, param=[]):\n>     \"\"\"Execute a hook if the hook exists.\"\"\"\n>     if verbose:\n> @@ -771,7 +778,10 @@ def p4CmdList(cmd, stdin=None, stdin_mode='w+b', cb=None, skip_info=False,\n>                 for key, value in entry.items():\n>                     key = key.decode()\n>                     if isinstance(value, bytes) and not (key in ('data', 'path', 'clientFile') or key.startswith('depotFile')):\n> -                        value = value.decode()\n> +                        if key == 'desc':\n> +                            value = decode_changlist_description(value)\n> +                        else:\n> +                            value = value.decode()\n>                     decoded_entry[key] = value\n>                 # Parse out data if it's an error response\n>                 if decoded_entry.get('code') == 'error' and 'data' in decoded_entry:\n>\n> base-commit: e6362826a0409539642a5738db61827e5978e2e4\n>\n>\n"},{"id":"416179","messageId":"20210204184534.30107dd7@ado-tr.home.arpa","threadId":"55097","inReplyTo":"pull.864.git.1612371600332.gitgitgadget@gmail.com","subject":"Re: [PATCH] git-p4: handle non-unicode characters in p4 cl","fromName":"Andrew Oakley","fromEmail":"andrew@adoakley.name","sentAt":"2021-02-04T18:45:34Z","receivedAt":"2021-02-04T19:18:53Z","isPatch":true,"sender":{"key":"andrew@adoakley.name","avatar":"https://avatars.githubusercontent.com/u/1107440?v=4"},"body":"On Wed, 03 Feb 2021 16:59:59 +0000\n\"Feiyang via GitGitGadget\" <gitgitgadget@gmail.com> wrote:\n> From: Feiynag Xue <fxue@roku.com>\n> \n> P4 allows non-unicode characters in changelist description body,\n> so git-p4 needs to be character encoding aware when reading p4 cl\n> \n> This change adds 2 config options, one specifies encoding,\n> the other specifies erro handling upon unrecognized character.\n> Those configs  apply when it reads p4 description text, mostly\n> from commands \"p4 describe\" and \"p4 changes\".\n\n...\n\n>     It seems to make sense to default to replace so that it gets rid\n> of non-unicode chars while trying to retain information. However, i am\n>     uncertain on if we have use cases where it relies on the\n>     stop-on-non-unicode behavior. (Hypothetically say an automation\n> that's expected to return error on non-unicode char in order to stop\n> them from propagating further?)\n\nI suspect these options will be insufficient for real repositories.\n\nThere are two ways a perforce server is configured:\n- unicode mode where the metadata is valid UTF-8, and you can request\n  conversion to different character sets\n- not in unicode mode where the metadata can be pretty much any random\n  bytes (but not '\\0'), and the encoding is not stored anywhere\n\nThere isn't any way to recover the encoding information from perforce,\nand it's likely that a server that's not in unicode mode will end up\nwith both UTF-8 commits, and commits that contain other things (which\nwe have no way to work out what they are).\n\nUntil recently git-p4 was written in python2 and it just moved the\nbytes from perforce into git without trying to interpret them in any\nway.  This has the advantage that the git repository will accurately\nreflect what was in perforce, even if it's complete garbage.\n\nThe other useful option I can think of would be to attempt to decode\nthe data as UTF-8, but fall back to some other encoding if the data\nisn't valid (probably Windows-1252, but a config option would make\nsense here).\n"}]}