git/list[1] front-page[2] threads[3] people[4] search[5] about
 

[PATCH 00/17] Peace with CRLF

From
Junio C Hamano <gitster@pobox.com>
Date
Oct 28, 2015, 22:25 UTC
Message-ID
<1446071161-15610-1-git-send-email-gitster@pobox.com>
In-Reply-To
<xmqqtws5o4lp.fsf@gitster.dls.corp.google.com>

We have too many topics titled "War on something"; let's try to make peace for a change.

This is a continuation to $gmane/275735, which is filed in the archive under another mailing list:

  http://thread.gmane.org/gmane.comp.version-control.msysgit/21773

We read "text" files from filesystem (or from the standard input) in various places in the code. Some of them are written by us, but others are often prepared by an editor driven by human user. Even though we write (and expect) lines in these text files to be terminated with LF (not CRLF), the end-user's editor can be told to use CRLF termination, and on some platforms that may be the default.

Because many codepaths (e.g. commit log message) first pass the contents of such a file through stripspace(), and as a side effect of discarding the whitespace at the end of each line, lines terminated with CRLF are normalized to LF terminated lines (but we do not lose a lone CR in the middle of a non-blank string), this is not a problem in many codepaths. But not all of the codepaths are safe.

Typically, we use strbuf_getline() to read these "text" files, which reads a single line up to the first LF into a buffer, discard that LF at the end, and returns (an incomplete last line gets returned as-is). In theory, these places that expect to read "text", we should be able to update the lotic to read a line up to the first LF, discard that LF, together with a CR if one appears immediately before that LF, without breaking anything.

I inspected all the callsites of this function to see if it is safe to use such an updated logic at these callsites, and did not find anything problematic. I could update strbuf_getline() in place, but just to be extra careful, this series instead introduces another helper, strbuf_gets(), that is aware of this CRLF business, and convert the ones that are safe to update as we verify.

At the end of this message, you will find my notes while inspecting the current codebase as of 37023ba3 (Seventh batch for 2.7, 2015-10-26).

 * This series converts only the callers of strbuf_getline() in the
   category [A], i.e. the current code would misbehave when fed a
   file with CRLF-terminated lines and use the data with an unwanted
   CR appended at the end, and with the update the code should work
   as intended with such a file, without breaking the expected
   behaviour when working on a file with LF-terminated lines.
 * Callers that expect to read from our own output do not have to
   accomodate CRLF-terminated lines, but they can be updated to do
   so safely if we do not rely on the ability to express a payload
   that has CR at the end.  For example, the insn sheet "rebase -i"
   uses typically end with commit log summary that we ourselves do
   not read or use, so even if the end-user on a platform with LF
   lines deliberately does insert \r at the end of the line and
   strbuf_gets() removed that \r from the payload, no unexpected
   behaviour should happen.  They are categorized as [B] in the
   attached notes (and this series does not touch them).
 * Some callers just strbuf_trim() or otherwise have logic that
   tolerates whitespaces at the end of the line.  For them, it does
   not make any difference whether strbuf_getline() or strbuf_gets()
   is used to read the lines to be processed.  They are caregorized
   as [C] in the attached notes (and this series does not touch
   them).
 * I haven't found a caller that wants to only see LF-terminated
   lines (in other words, "ABC\r\n" must be treated as a line with 4
   bytes payload on it, A B C and CR), but the survey reserves
   category name [X] for this empty set ;-).

Double-checking my classification for callers in [B] and [C] and making them all use strbuf_gets() would be a good microproject for GSoC in coming years ;-) If all callers with strbuf_getline() that uses '\n' as the terminator disappears at the end, that would be ideal.

Junio C Hamano (17):
  strbuf: add strbuf_gets()
  check-attr, check-ignore, checkout-index: read paths with strbuf_gets()
  update-index: read --index-info with strbuf_gets()
  update-index: read list of paths with strbuf_gets() under --stdin
  mktree: read textual tree representation with strbuf_gets()
  hash-object: read --stdin-paths with strbuf_gets()
  revision: read --stdin with strbuf_gets()
  rev-parse: read parseopt spec with strbuf_gets()
  ident.c: read /etc/mailname with strbuf_gets()
  remote.c: read $GIT_DIR/remotes/* with strbuf_gets()
  clone/sha1_file: read info/alternates with strbuf_gets()
  transport-helper: read helper response with strbuf_gets()
  cat-file: read batch stream with strbuf_gets()
  column: read lines with strbuf_gets()
  send-pack: read list of refs with strbuf_gets()
  grep: read -f file with strbuf_gets()
  test-sha1-array: read command stream with strbuf_gets()
 builtin/am.c             | 23 ++++-------------------
 builtin/cat-file.c       |  2 +-
 builtin/check-attr.c     |  4 +++-
 builtin/check-ignore.c   |  5 ++++-
 builtin/checkout-index.c |  4 +++-
 builtin/clone.c          |  2 +-
 builtin/column.c         |  2 +-
 builtin/grep.c           |  2 +-
 builtin/hash-object.c    |  2 +-
 builtin/mktree.c         |  4 +++-
 builtin/rev-parse.c      |  4 ++--
 builtin/send-pack.c      |  2 +-
 builtin/update-index.c   |  9 +++++++--
 ident.c                  |  2 +-
 remote.c                 |  2 +-
 revision.c               |  9 ++-------
 sha1_file.c              |  2 +-
 strbuf.c                 | 16 ++++++++++++++--
 strbuf.h                 |  7 +++++++
 test-sha1-array.c        |  2 +-
 transport-helper.c       |  2 +-
 21 files changed, 60 insertions(+), 47 deletions(-)
-- >8 -- survey of current strbuf_getline() callers -- >8 --
updating == "teaching strbuf_getline() to strip CR that comes
             immediately before the LF"
[A] may be problematic on CRLF systems, "updating" will help fixing.
[B] CRLF line ending may or may not appear, but "updating" does not hurt.
[C] CRLF line ending may appear, but the code is already safe.
[X] The caller cares about distinction between CRLF and LF, and "updating"
    introduce breakage.

bisect.c uses it to read from BISECT_NAMES, which is a text file written by redirecting "rev-parse --sq-quote" into it. [B]

bisect.c uses it to read from BISECT_EXPECTED_REV, which is a "ref-like" file written by update_ref(). [B]

*** This should be cleaned up to use read_ref() or something.

ident.c reads from /etc/mailname, which is a text file. [A] *** mailnamebuf may want to be strbuf_strip()'ed, and doing so is another way to fix this.

credential-cache--daemon.c uses it to read requests, which is a text channel. [B]

credential-store.c uses it to read from the credential file, which is a text file. However, it is parsed by credential_from_url() that is not affected by an extra CR at the end. [C]

credential-store.c parrots the original in CRLF as CRLF with "store", while writing a new one with LF. This codepath will start normalizing the resulting file to LF, which may not be a bad thing. [C]

credential.c uses it to read from the helper. [B]

daemon.c uses it to read from a child process's output in order to save it to the log. The current code may have sent string with CR at the end to logerror(). [A]

fast-import.c uses it to read the command stream, which is text. [B]

remote-curl.c uses it as a transport helper to read commadn stream, which is text [B]

remote-testsvn.c uses it as a transport helper to read commadn stream, which is text [B]

remote.c uses it to read $GIT_DIR/remotes/* files, which is text. [A]

sequencer.c uses it to read $GIT_DIR/sequencer/head that it itself writes. [B]

builtin/clone.c and sha1_file.c uses it to read from an alternates file, which is text. [A]

shell.c does end-user interactions ;-) [B]
test-sha1-array.c reads command stream, which is text [A].
transport-helper.c reads helper output, which is text [A].

walker.c reads from the standard input a <target> HT <ref> per line, which is text [A].

wt-status.c reads from rebase-i insn, which is text, but it uses strbuf_trim() on it [C].

wt-status.c also reads from various "ref-like" things, all of which are internally generated by us [B].

builtin/am.c does a lot to process mail input. [A]
builtin/cat-file.c uses it to read --batch input, which is text [A].

builtin/checkout-index.c, builtin/check-attr.c and builtin/check-ignore.c use it to read --stdin, which is text [A].

builtin/check-mailmap.c uses it to read --stdin, but the tail end of the input line is not used [C].

builtin/clean.c does end-user interactions; it trims so it is safe [C].
builtin/column.c reads from standard input text. [A]

builtin/commit.c reads from MERGE_HEAD which is internally generated [B].

builtin/fetch-pack.c reads list of refs with --stdin, which is internally generated [B].

builtin/grep.c uses it to read patterns from a file with -f [A].
builtin/hash-object.c uses it with --stdin-paths [A].
mailinfo uses it to read from its input [B].
builtin/mktree.c reads etxt-formatted tree representation [A].

builtin/notes.c reads from the standard input upon "git notes copy"; the input is trimmed [C].

builtin/pull.c reads FETCH_HEAD which is internally generated [B].

builtin/repack.c reads the packname output by pack-objects which is internally generated [B].

builtin/rev-parse.c uses it for its parseopt mode [A].
builtin/send-pack.c uses it to read the refs from --stdin [A].
builtin/update-index.c reads "--index-info" [A].
builtin/update-index.c reads paths from "--stdin" [A].
Previous: Johannes SixtNext: Junio C Hamano
Message 5 of 114 in “revisions --stdin: accept CRLF line terminators”
  1. revisions --stdin: accept CRLF line terminatorsJohannes Sixt, Aug 11, 2015
  2. Junio C HamanoAug 11, 2015
  3. Junio C HamanoAug 11, 2015
  4. Johannes SixtAug 12, 2015
  5. 00/17 Peace with CRLFJunio C Hamano, Oct 28, 2015
  6. 01/17 strbuf: add strbuf_gets()Junio C Hamano, Oct 28, 2015
  7. 02/17 check-attr, check-ignore, checkout-index: read paths with strbuf_gets()Junio C Hamano, Oct 28, 2015
  8. 03/17 update-index: read --index-info with strbuf_gets()Junio C Hamano, Oct 28, 2015
  9. 04/17 update-index: read list of paths with strbuf_gets() under --stdinJunio C Hamano, Oct 28, 2015
  10. 05/17 mktree: read textual tree representation with strbuf_gets()Junio C Hamano, Oct 28, 2015
  11. 06/17 hash-object: read --stdin-paths with strbuf_gets()Junio C Hamano, Oct 28, 2015
  12. 07/17 revision: read --stdin with strbuf_gets()Junio C Hamano, Oct 28, 2015
  13. 08/17 rev-parse: read parseopt spec with strbuf_gets()Junio C Hamano, Oct 28, 2015
  14. 09/17 ident.c: read /etc/mailname with strbuf_gets()Junio C Hamano, Oct 28, 2015
  15. 10/17 remote.c: read $GIT_DIR/remotes/* with strbuf_gets()Junio C Hamano, Oct 28, 2015
  16. 11/17 clone/sha1_file: read info/alternates with strbuf_gets()Junio C Hamano, Oct 28, 2015
  17. 12/17 transport-helper: read helper response with strbuf_gets()Junio C Hamano, Oct 28, 2015
  18. 13/17 cat-file: read batch stream with strbuf_gets()Junio C Hamano, Oct 28, 2015
  19. 14/17 column: read lines with strbuf_gets()Junio C Hamano, Oct 28, 2015
  20. 15/17 send-pack: read list of refs with strbuf_gets()Junio C Hamano, Oct 28, 2015
  21. 16/17 grep: read -f file with strbuf_gets()Junio C Hamano, Oct 28, 2015
  22. 17/17 test-sha1-array: read command stream with strbuf_gets()Junio C Hamano, Oct 28, 2015
  23. 00/17 Peace with CRLFJunio C Hamano, Dec 16, 2015
  24. 01/17 strbuf: make strbuf_getline_crlf() globalJunio C Hamano, Dec 16, 2015
  25. Johannes SchindelinJan 4, 2016
  26. Junio C HamanoJan 4, 2016
  27. 02/17 check-attr, check-ignore, checkout-index: read paths with strbuf_getline_crlf()Junio C Hamano, Dec 16, 2015
  28. Johannes SchindelinJan 4, 2016
  29. Johannes SchindelinJan 4, 2016
  30. Junio C HamanoJan 4, 2016
  31. 03/17 update-index: read --index-info with strbuf_getline_crlf()Junio C Hamano, Dec 16, 2015
  32. Johannes SchindelinJan 4, 2016
  33. Junio C HamanoJan 4, 2016
  34. 04/17 update-index: read list of paths with strbuf_getline_crlf() under --stdinJunio C Hamano, Dec 16, 2015
  35. Johannes SchindelinJan 4, 2016
  36. 05/17 mktree: read textual tree representation with strbuf_getline_crlf()Junio C Hamano, Dec 16, 2015
  37. Johannes SchindelinJan 4, 2016
  38. 06/17 hash-object: read --stdin-paths with strbuf_getline_crlf()Junio C Hamano, Dec 16, 2015
  39. 07/17 revision: read --stdin with strbuf_getline_crlf()Junio C Hamano, Dec 16, 2015
  40. 08/17 rev-parse: read parseopt spec with strbuf_getline_crlf()Junio C Hamano, Dec 16, 2015
  41. 09/17 ident.c: read /etc/mailname with strbuf_getline_crlf()Junio C Hamano, Dec 16, 2015
  42. 10/17 remote.c: read $GIT_DIR/remotes/* with strbuf_getline_crlf()Junio C Hamano, Dec 16, 2015
  43. 11/17 clone/sha1_file: read info/alternates with strbuf_getline_crlf()Junio C Hamano, Dec 16, 2015
  44. 12/17 transport-helper: read helper response with strbuf_getline_crlf()Junio C Hamano, Dec 16, 2015
  45. 13/17 cat-file: read batch stream with strbuf_getline_crlf()Junio C Hamano, Dec 16, 2015
  46. 14/17 column: read lines with strbuf_getline_crlf()Junio C Hamano, Dec 16, 2015
  47. 15/17 send-pack: read list of refs with strbuf_getline_crlf()Junio C Hamano, Dec 16, 2015
  48. 16/17 grep: read -f file with strbuf_getline_crlf()Junio C Hamano, Dec 16, 2015
  49. Johannes SchindelinJan 4, 2016
  50. Junio C HamanoJan 4, 2016
  51. 17/17 test-sha1-array: read command stream with strbuf_getline_crlf()Junio C Hamano, Dec 16, 2015
  52. Johannes SchindelinJan 4, 2016
  53. Johannes SchindelinJan 4, 2016
  54. 0/9 Preview of "Peace with CRLF" rerolledJunio C Hamano, Jan 14, 2016
  55. 1/9 strbuf: miniscule style fixJunio C Hamano, Jan 14, 2016
  56. 2/9 strbuf: make strbuf_getline_crlf() globalJunio C Hamano, Jan 14, 2016
  57. 3/9 strbuf: introduce strbuf_getline_{lf,nul}()Junio C Hamano, Jan 14, 2016
  58. 4/9 mktree: there are only two line terminatorsJunio C Hamano, Jan 14, 2016
  59. Jeff KingJan 14, 2016
  60. 5/9 check-attr: there are only two line terminatorsJunio C Hamano, Jan 14, 2016
  61. 6/9 check-ignore: there are only two line terminatorsJunio C Hamano, Jan 14, 2016
  62. 7/9 update-index: there are only two line terminatorsJunio C Hamano, Jan 14, 2016
  63. Jeff KingJan 14, 2016
  64. 8/9 checkout-index: there are only two line terminatorsJunio C Hamano, Jan 14, 2016
  65. Jeff KingJan 14, 2016
  66. Junio C HamanoJan 14, 2016
  67. Jeff KingJan 14, 2016
  68. 9/9 strbuf: give strbuf_getline() to the "most text friendly" variantJunio C Hamano, Jan 14, 2016
  69. 9/9 strbuf: retire strbuf_getline() for nowJunio C Hamano, Jan 14, 2016
  70. Junio C HamanoJan 14, 2016
  71. Jeff KingJan 14, 2016
  72. 00/21 Peace with CRLFJunio C Hamano, Jan 14, 2016
  73. 01/21 strbuf: miniscule style fixJunio C Hamano, Jan 14, 2016
  74. 02/21 strbuf: make strbuf_getline_crlf() globalJunio C Hamano, Jan 14, 2016
  75. 03/21 strbuf: introduce strbuf_getline_{lf,nul}()Junio C Hamano, Jan 14, 2016
  76. 04/21 mktree: there are only two possible line terminationsJunio C Hamano, Jan 14, 2016
  77. 05/21 check-attr: there are only two possible line terminationsJunio C Hamano, Jan 14, 2016
  78. Jeff KingJan 15, 2016
  79. Jeff KingJan 15, 2016
  80. Junio C HamanoJan 15, 2016
  81. Jeff KingJan 15, 2016
  82. Junio C HamanoJan 15, 2016
  83. 06/21 check-ignore: there are only two possible line terminationsJunio C Hamano, Jan 14, 2016
  84. 07/21 update-index: there are only two possible line terminationsJunio C Hamano, Jan 14, 2016
  85. 08/21 checkout-index: there are only two possible line terminationsJunio C Hamano, Jan 14, 2016
  86. Jeff KingJan 15, 2016
  87. Jeff KingJan 15, 2016
  88. Junio C HamanoJan 15, 2016
  89. Junio C HamanoJan 15, 2016
  90. 09/21 strbuf: give strbuf_getline() to the "most text friendly" variantJunio C Hamano, Jan 14, 2016
  91. Eric SunshineJan 15, 2016
  92. Junio C HamanoJan 15, 2016
  93. 10/21 hash-object: read --stdin-paths with strbuf_getline()Junio C Hamano, Jan 14, 2016
  94. Jeff KingJan 15, 2016
  95. 11/21 revision: read --stdin with strbuf_getline()Junio C Hamano, Jan 14, 2016
  96. Eric SunshineJan 15, 2016
  97. Junio C HamanoJan 15, 2016
  98. Jeff KingJan 15, 2016
  99. 12/21 rev-parse: read parseopt spec with strbuf_getline()Junio C Hamano, Jan 14, 2016
  100. 13/21 ident.c: read /etc/mailname with strbuf_getline()Junio C Hamano, Jan 14, 2016
  101. 14/21 remote.c: read $GIT_DIR/remotes/* with strbuf_getline()Junio C Hamano, Jan 14, 2016
  102. 15/21 clone/sha1_file: read info/alternates with strbuf_getline()Junio C Hamano, Jan 14, 2016
  103. Eric SunshineJan 15, 2016
  104. 16/21 transport-helper: read helper response with strbuf_getline()Junio C Hamano, Jan 14, 2016
  105. Jeff KingJan 15, 2016
  106. 17/21 cat-file: read batch stream with strbuf_getline()Junio C Hamano, Jan 14, 2016
  107. Jeff KingJan 15, 2016
  108. 18/21 column: read lines with strbuf_getline()Junio C Hamano, Jan 14, 2016
  109. Jeff KingJan 15, 2016
  110. 19/21 send-pack: read list of refs with strbuf_getline()Junio C Hamano, Jan 14, 2016
  111. 20/21 grep: read -f file with strbuf_getline()Junio C Hamano, Jan 14, 2016
  112. 21/21 test-sha1-array: read command stream with strbuf_getline()Junio C Hamano, Jan 14, 2016
  113. Jeff KingJan 15, 2016
  114. Jeff KingJan 15, 2016

Read the whole thread, see it on lore, or plain text.

$ cat FOOTERMessages come from the public archive at lore.kernel.org/git, fetched every hour. The front page is chosen and written each morning by an AI editor and can be wrong; the threads themselves are the record. About and API. For agents: an MCP server at https://gitlist.dev/mcp, and any thread, story or person page as Markdown by adding .md to its URL (or sending Accept: text/markdown). Details in /llms.txt.