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

[PATCH v4 00/21] Peace with CRLF

From
Junio C Hamano <gitster@pobox.com>
Date
Jan 14, 2016, 23:58 UTC
Message-ID
<1452815916-6447-1-git-send-email-gitster@pobox.com>
In-Reply-To
<1452740590-16827-1-git-send-email-gitster@pobox.com>

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

This is a reroll of
  http://thread.gmane.org/gmane.comp.version-control.git/282614

which 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

but with a larger clean-up whose early part was sent earlier, marked with the "PREVIEW v3" label.

  http://thread.gmane.org/gmane.comp.version-control.git/284001

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.

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). Lines from a file edited on Dossy systems hence may have a CR at the end that the end-user did not intend to be a part of it.

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), when we process what we read from these files, this is not a problem for them, but not all of the codepaths are safe.

In these places that expect to read "text", we should be able to update the logic 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. That includes cases where the program might want to treat a CR in the middle of a line as proper payload the user intended to give us. Such a variant of strbuf_getline() already exists as strbuf_getline_crlf() and used in the "git am" that was reimplemented in C recently.

The strbuf_getline() function takes a "line-termination" as its third argument. Its interface is overly broad in that it allows an arbitrary byte to be used to mark the end of a line, but in practice, no codepaths pass any byte other than LF or NUL. In theory, the callers that use LF as the line-termination are expecting "text" input, so we could blindly replace them with strbuf_getline_crlf() and not many things should break. To be conservative, however, this series converts only chosen ones after auditing that such a conversion is safe.

To make the tree-wide change manageable, the early part of this series first changes all callsites of strbuf_getline() to call one of the two new thin wrapper functions, strbuf_getline_lf() and strbuf_getline_nul(), depending on the value of "line-termination" used by the caller. This is done with Patches 1-9, which is essentially the same as "PREVIEW v3" series posted earlier, with small changes:

 - The update to mktree used "lf_lines" in the preview, but this
   version flips it to the same "nul_term_line" to match other two,
   even though I think the latter is a terrible variable name with
   bad taste.
 - The update to checkout-index kept "line_termination" in the
   preview, but this version no longer uses it and instead uses
   nul_term_line.
 - The definition of nul_term_line for the option parser uses
   OPT_BOOL(), as the possible values in it are true or false, not
   '\0' or '\n'.
 - The topic was rebased on v2.7.0; the bulk of builtin/mailinfo.c
   moved to mailinfo.c between the old base of the topic and v2.7.0,
   so the mechanical renaming done in Patch 3 was redone.

After Patch 9, all the original callers of strbuf_getline() that used LF line ending call strbuf_getline_lf(). No caller calls strbuf_getline() at that point, so strbuf_getline_crlf() takes over the short and nice name strbuf_getline().

The callers of strbuf_getline_lf() are inspected by the remainder of the series and updated to call strbuf_getline() instead when it makes sense to do so. They all had to be updated relative to the previous round (v2), but the callsites that are updated haven't changed.

This series converts only the callers of strbuf_getline() that would misbehave when fed a file with CRLF-terminated lines and use the data with an unwanted CR appended at the end. 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.

Notes on the remaining strbuf_getline_lf() callers:
 * Those that expect to only read from our own output do not have to
   accommodate 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 ends each line with a 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 CR at the end of
   the line and strbuf_getline() removed that CR from the payload,
   no unexpected behaviour should happen.
   It may be a good GSoC microproject to convert them to use
   strbuf_getline().
 * Those that call strbuf_trim() immediately on the result before
   doing anything, or otherwise have logic that tolerates
   whitespaces at the end of the line, can continue using
   strbuf_getline_lf() and will not misbehave on CRLF-terminated
   lines.
   This series does not touch them; converting them to use
   strbuf_getline() would a good way to document them as dealing
   with "text".  While doing so, they can also lose their custom
   logic that happens to make CRLF-terminated lines work.
   It is a good GSoC microproject to convert them to use
   strbuf_getline().
 * Those that want to only ever 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), would be broken if we replace it with
   strbuf_getline().  This series does not touch them, and no
   follow-up series should, either.
Junio C Hamano (21):
  strbuf: miniscule style fix
  strbuf: make strbuf_getline_crlf() global
  strbuf: introduce strbuf_getline_{lf,nul}()
  mktree: there are only two possible line terminations
  check-attr: there are only two possible line terminations
  check-ignore: there are only two possible line terminations
  update-index: there are only two possible line terminations
  checkout-index: there are only two possible line terminations
  strbuf: give strbuf_getline() to the "most text friendly" variant
  hash-object: read --stdin-paths with strbuf_getline()
  revision: read --stdin with strbuf_getline()
  rev-parse: read parseopt spec with strbuf_getline()
  ident.c: read /etc/mailname with strbuf_getline()
  remote.c: read $GIT_DIR/remotes/* with strbuf_getline()
  clone/sha1_file: read info/alternates with strbuf_getline()
  transport-helper: read helper response with strbuf_getline()
  cat-file: read batch stream with strbuf_getline()
  column: read lines with strbuf_getline()
  send-pack: read list of refs with strbuf_getline()
  grep: read -f file with strbuf_getline()
  test-sha1-array: read command stream with strbuf_getline()
 bisect.c                   |  8 ++++----
 builtin/am.c               | 37 +++++++++++--------------------------
 builtin/cat-file.c         |  2 +-
 builtin/check-attr.c       |  7 ++++---
 builtin/check-ignore.c     |  7 ++++---
 builtin/check-mailmap.c    |  2 +-
 builtin/checkout-index.c   | 16 ++++++++--------
 builtin/clean.c            |  6 +++---
 builtin/clone.c            |  2 +-
 builtin/column.c           |  2 +-
 builtin/commit.c           |  2 +-
 builtin/fetch-pack.c       |  2 +-
 builtin/grep.c             |  2 +-
 builtin/hash-object.c      |  2 +-
 builtin/mktree.c           | 14 ++++++++------
 builtin/notes.c            |  2 +-
 builtin/pull.c             |  2 +-
 builtin/repack.c           |  2 +-
 builtin/rev-parse.c        |  4 ++--
 builtin/send-pack.c        |  2 +-
 builtin/update-index.c     | 27 ++++++++++++++++-----------
 compat/terminal.c          |  2 +-
 credential-cache--daemon.c |  4 ++--
 credential-store.c         |  2 +-
 credential.c               |  2 +-
 daemon.c                   |  2 +-
 fast-import.c              |  4 ++--
 ident.c                    |  2 +-
 mailinfo.c                 |  8 ++++----
 remote-curl.c              |  6 +++---
 remote-testsvn.c           |  4 ++--
 remote.c                   |  4 ++--
 revision.c                 |  9 ++-------
 sequencer.c                |  2 +-
 sha1_file.c                |  2 +-
 shell.c                    |  2 +-
 strbuf.c                   | 28 +++++++++++++++++++++++++---
 strbuf.h                   | 27 ++++++++++++++++++++++-----
 test-sha1-array.c          |  2 +-
 transport-helper.c         |  5 +++--
 walker.c                   |  2 +-
 wt-status.c                |  4 ++--
 42 files changed, 151 insertions(+), 122 deletions(-)
-- 
2.7.0-250-ge1b5ba3
Previous: Jeff KingNext: Junio C Hamano
Message 72 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.