Skip to content

"Different line endings": patching CRLF terminated files

Paul McArrow edited this page Nov 13, 2023 · 2 revisions

Problem

After you followed your usual steps to generate a patch (git diff > summary-of-edits.patch in a working Git copy, or manually with git diff ../a/source_file.c ../b/source_file.c >> summary-of-edits.patch if you are tweaking downloaded source code (I sometimes do the latter with small changes to skip cloning a Git repo, even though I have to manually adjust the paths in the diff)), the following messages appear:

(Stripping trailing CRs from patch; use --binary to disable.)
patching file src/nssm.h
Hunk #1 FAILED at 33 (different line endings).
1 out of 1 hunk FAILED -- saving rejects to file src/nssm.h.rej
make[1]: *** [Makefile:909: build-only-nssm_armv7-w64-mingw32] Error 1

CRLF-patch-different-line-endings-Screenshot 2023-02-11 080359

When you try to change the line endings in the patch from LF to CRLF, the error does not go away.

Analysis

The patch utility expects the diff headers to have native newline terminators (e.g. LF on Linux) and the body lines to match the patched file verbatim. To the best of collective Internet knowledge, there is no flag combination capable of changing this behavior to accept a uniformly line-terminated patch.

Solution

If the deltas are large, it's probably best to create a standalone fork rather than keep the changes under the distro source control. First, it's simply cleaner and easier from the maintainability perspective. Second, it provides a clear boundary line between the original codebase (which may be covered by a restrictive license) and the distro itself. If it's under GPL or CC-SA, for example, forking sends a clear message that you stick it only to your contributed code deltas, not to the entire MXE project.

There are several possible ways to create a fork:

  • If the original project is on GitHub, click the "fork" button. (Other repo hosting providers, such as GitLab, may provide similar functionality.)
  • If the original project is NOT on GitHub, but already under Git, it's possible to create an empty project on GitHub (for clarity, don't create anything there, even README or LICENSE) and git push --all your working copy there. Don't post your changes directly to master, or what they call it these days, without labeling; create a feature branch or put a meaningful tag.
  • If the original project is under a different form of revision control (e.g. CVS/SVN) that's painful to import into Git, or its revision history is not available at all, simply upload it to GitHub like you normally would. Include a proper attribution, at least a link in README to the original download location. Remove any pre-built binaries from the project prior to upload.

TODO: add a "component breeding tips" page and move the notes on forking there.

However, if integrating the component requires only a minimal modification, such as one or two lines of code, — that is, the patch file is likely to stay small and updated once or twice — "hybrid newlines" can be to the rescue.

Split the diff into two files: the diff header and the deltas. Keep the header in the original summary-of-edits.patch and move the deltas into e.g. summary-of-edits.deltas. Convert the header to LF and the deltas to CRLF. Concatenate them again with cat summary-of-edits.deltas >> summary-of-edits.patch. Build. The build will now apply the patch correctly.

Fig. 1. Patch header

LF-Screenshot 2023-02-10 172536

Fig. 2. Patch deltas

crlf-Screenshot 2023-02-10 172645

Note that you don't want the "deltas" file to reside in src and have a patch extension during build. Move it away, rename it or delete it.

(The particular patch above turned out to be responding to a false alarm and was eventually withdrawn, but hopefully you got the point.)

Another way to generate a selective [CR]LF patch

Create a file consisting of a single CRLF line ending, e.g. in Notepad. Let's call it cr.txt.

hd cr.txt will display 0d 0a as its hexadecimal representation.

Now truncate it to a single CR byte:

truncate --size 1 cr.txt

Create the patch the regular way. Make sure it's opened in LF mode in the editor (e.g. VSCode). Choose a character that does NOT appear in the patch file. It can be some special character like the tilde ~, or a non-ASCII character from a non-English alphabet. The following example assumes that it's a tilde.

Append the chosen character to the lines that need to become CRLF. For example, if you are using VSCode, switch the editor to column mode, select a zero-width column along the patch body and press "End". The cursors will be placed at the ends of the lines. Now type the chosen character: to-be-cr-lf-Screenshot 2023-11-12 210621

Save and close the file. If it's at the intended location of the patch, copy it to a temporary location. Then execute:

cat <temp-dir>/<patch-name>.patch | tr '~' `cat cr.txt` > src/<patch-name>.patch

(The command line is a little overly verbose for clarity.)

That's it. You have applied CRLF line endings selectively within the patch file.

Validity

The information in this article was true as of commit be76b8b0bf54e9c4d7e3f04ca304c7dba85e8bfe, as well as down to tag mxe/7.0.0 and earlier revisions.