Skip to content

Taking one diff hunk rewrites every line ending in the file and strips its UTF-8 BOM #445

Description

@matt-edmondson

What's wrong

The file-diff view's take-left/take-right arrows (ProjectDirector/ProjectDirector.cs ~L1650 and ~L1745) rebuild the whole destination file like this:

string newText = string.Join(Environment.NewLine, newLines);
File.WriteAllText(Path.Combine(repoB.LocalPath, Options.CompareFile), newText);

DiffPlex pieces have their line terminators stripped, so every line gets Environment.NewLine, whatever the file used before. File.WriteAllText(path, text) writes UTF-8 without a BOM, and ReadFileOrEmpty (~L1411) already dropped the BOM when it read the file with File.ReadAllText.

Failure scenario

File A is EF BB BF + one\r\ntwo\r\nthree\r\n and file B is one\r\nTWO\r\nthree\r\n. Taking B's hunk into A should change only the middle line.

  • Expected bytes: EF-BB-BF-6F-6E-65-0D-0A-54-57-4F-0D-0A-74-68-72-65-65-0D-0A
  • Actual bytes (Linux): 6F-6E-65-0A-54-57-4F-0A-74-68-72-65-65-0A. The BOM is gone and every CRLF became LF.

On Windows the reverse happens: every line of an LF-only file becomes CRLF. Either way, taking one line produces a whole-file change in git. A BOM that the repo's .editorconfig requires is lost silently.

Verified with a scratch test that wrote the real bytes to disk and ran the same ReadAllText → CreateLineDiffs → prologue/lines/epilogue → string.Join(Environment.NewLine) → WriteAllText sequence as ShowDiffRight.

Suggested fix

  • Detect the destination file's line ending (first \r\n vs \n) and its encoding/BOM, for example with StreamReader(detectEncodingFromByteOrderMarks: true) and CurrentEncoding.
  • Join with that line ending and write back with the same encoding.
  • Move the "apply block" text-building into a static helper (like FilePropagation / DecidePull) so it can be unit-tested.

Acceptance criteria

  • Taking a hunk changes only the bytes of that hunk. The rest of the file keeps its line endings and BOM.

Activity

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Metadata

Metadata

Assignees

No one assigned

    Labels

    readyFully specified; implement as written

    Type

    No type

    Projects

    No projects

      Milestone

      No milestone

      Relationships

      None yet

      Development

      No branches or pull requests

      Issue actions