silk.text.diff.

Line diffs, unified diff text, and reading and applying patches.

When to use#

Use unified to show how a file changed, as diff -u and git diff do, or to make a patch that a later step applies. Use parse to read a patch from diff -u, git diff, or git format-patch, and apply to apply one of its files to the original text. Use diffLines or diffSymbols for the edits themselves, for example to show a side-by-side view or to compare words instead of lines.

Details#

A line ends after each \n. A last line without \n is a line too, and it differs from the same line with \n. Lines are compared byte for byte, so a \r before \n is part of the line.

The Algorithm chooses how the shared lines are found:

  • Algorithm.Histogram anchors the comparison on lines that are rare in both texts, as git diff --histogram does. It is the fastest, and the diffs are easy to read. Use it unless there is a reason to use another algorithm.

  • Algorithm.MyersMinimal always finds the fewest deleted and inserted lines, as git diff --minimal does. The time grows with the line count times the number of changed lines.

  • Algorithm.Myers stops a long search after about the square root of the line count, as git diff does by default, so very different texts do not take quadratic time. The diff can then have a few more changed lines than the minimum.

The edits are a list of Edit runs. unified groups the changes into hunks with context unchanged lines around them, and writes \ No newline at end of file after a last line without \n, so the text reads back with parse and applies with apply.

A Patch holds one FilePatch for each file. Its paths, hunks and lines are Span ranges of the patch text, so the patch text must stay alive while the patch is in use. apply puts each hunk where its old lines are, nearest to the line that the hunk header gives, so a patch still applies after lines were added or removed before a hunk.

The Bytes forms, such as unifiedBytes, take and return bytes that need not be UTF-8, such as file contents.

Examples#

Make a patch and apply it#

silk
import silk.text.diff
import silk.text.diff { Algorithm, ApplyError, FilePatch, PatchError }
import silk.collections.list { List }
import silk.mem.allocator { Allocator, OutOfMemoryError }
import silk.core.effect { Effect }
import silk.text.string { String }

effect fn check() -> i32 ! PatchError | ApplyError | OutOfMemoryError ? &mut Allocator {
  let old = "red\ngreen\nblue\n"
  let new = "red\nyellow\nblue\n"
  let text = run diff.unified("a/colors.txt", old, "b/colors.txt", new, 3, Algorithm.Histogram)
  let expected = "--- a/colors.txt\n+++ b/colors.txt\n@@ -1,3 +1,3 @@\n red\n-green\n+yellow\n blue\n"
  if String.view(&text) != expected {
    return 1
  }
  let patch = run diff.parse(String.view(&text))
  let file = &List<FilePatch>.asSlice(&patch.files)[0]
  let patched = run diff.apply(old, String.view(&text), file)
  if String.view(&patched) != new {
    return 1
  }
  return 0
}

effect fn failed(error: PatchError | ApplyError | OutOfMemoryError) -> i32 {
  drop error
  return 2
}

pub fn main() -> i32 {
  let mut allocator = Allocator.systemAllocatorProvider()
  return run Effect.catchAll(check(), failed) |> Effect.provideMut<Allocator>(&mut allocator)
}

Import as diff with import silk.text.diff as diff.

Public declarations: 22.

Algorithm#

silk
pub enum Algorithm

The algorithm that finds the lines two texts share.

When to use#

Use Algorithm.Histogram unless there is a reason to use another algorithm. It is the fastest, and its diffs are easy to read.

Details#

All three algorithms give correct edits: applied to the old text, they give the new text. They can differ in which lines they keep when more than one choice has the same cost, and Algorithm.Myers and Algorithm.Histogram can change a few more lines than the minimum.

Myers#

silk
Myers

Myers' algorithm, as git diff uses by default. A search that takes longer than about the square root of the line count stops at its best point so far, so the time stays low for very different texts. The diff can then have a few more changed lines than the minimum.

MyersMinimal#

silk
MyersMinimal

Myers' algorithm with no limit, as git diff --minimal. The diff has the fewest deleted and inserted lines. The time grows with the line count times the number of changed lines.

Histogram#

silk
Histogram

The histogram algorithm, as git diff --histogram. It matches the lines that are rare in both texts first, then the lines around them. It is the fastest of the three, and it keeps rare lines, such as function headers, as anchors.

EditKind#

silk
pub enum EditKind

What one Edit does: keep, delete, or insert its lines.

Equal#

silk
Equal

The lines are in both sequences.

Delete#

silk
Delete

The lines are only in the old sequence.

Insert#

silk
Insert

The lines are only in the new sequence.

Edit#

silk
pub struct Edit

One run of lines that are equal, deleted, or inserted.

Details#

The runs of a diff cover both sequences in order, with no gaps: each run starts where the last one ended. An Equal run covers length lines of each sequence. A Delete run covers length old lines and starts no new lines, and an Insert run covers length new lines and starts no old lines. Where lines change, the Delete run comes before the Insert run. Line indices start from 0.

Field kind#

silk
pub kind: EditKind

What the run does.

Field oldStart#

silk
pub oldStart: usize

The index of the first line of the run in the old sequence.

Field newStart#

silk
pub newStart: usize

The index of the first line of the run in the new sequence.

Field length#

silk
pub length: usize

The number of lines in the run.

Implementation Copy for Edit#

silk
impl Copy for Edit

diffSymbols#

silk
pub effect fn diffSymbols(old: &[u32], new: &[u32], algorithm: Algorithm) -> List<Edit>
  ! OutOfMemoryError
  ? &mut Allocator

Returns the edits that turn the sequence old into new, where each number is one symbol.

When to use#

Use this function to compare sequences of things other than lines, such as words or tokens: give equal things equal numbers, then compare the numbers. For lines of text, use diffLines.

Details#

The runs cover both sequences in order. Where symbols change, a Delete run comes before the Insert run. The memory used grows with the sequence lengths and with the largest symbol, so number the symbols from 0.

Errors#

  • OutOfMemoryError if the Allocator cannot supply the scratch storage or the result.

Examples#

Compare two sequences of word numbers#

silk
import silk.text.diff
import silk.text.diff { Algorithm, Edit, EditKind }
import silk.collections.list { List }
import silk.mem.allocator { Allocator, OutOfMemoryError }
import silk.core.effect { Effect }

effect fn check() -> i32 ! OutOfMemoryError ? &mut Allocator {
  // "the quick fox" against "the slow fox", one number per word.
  let old: [u32; 3] = [0, 1, 2]
  let new: [u32; 3] = [0, 3, 2]
  let edits = run diff.diffSymbols(&old, &new, Algorithm.Histogram)
  let runs = List<Edit>.asSlice(&edits)
  if runs.length != 4 || runs[1].kind != EditKind.Delete || runs[2].kind != EditKind.Insert {
    return 1
  }
  return 0
}

effect fn exhausted(error: OutOfMemoryError) -> i32 {
  drop error
  return 2
}

pub fn main() -> i32 {
  let mut allocator = Allocator.systemAllocatorProvider()
  return run Effect.catchAll(check(), exhausted) |> Effect.provideMut<Allocator>(&mut allocator)
}

diffLines#

silk
pub effect fn diffLines(old: string, new: string, algorithm: Algorithm) -> List<Edit>
  ! OutOfMemoryError
  ? &mut Allocator

Returns the edits that turn the lines of old into the lines of new.

When to use#

Use this function when the edits themselves are needed, for example to show a side-by-side view or to count changed lines. Use unified for diff text.

Details#

A line ends after each \n. A last line without \n is a line too, and it differs from the same line with \n. Lines are compared byte for byte. The runs cover both texts in order, with line indices from 0. Where lines change, a Delete run comes before the Insert run.

Errors#

  • OutOfMemoryError if the Allocator cannot supply the scratch storage or the result.

Examples#

Count the changed lines#

silk
import silk.text.diff
import silk.text.diff { Algorithm, Edit, EditKind }
import silk.collections.list { List }
import silk.mem.allocator { Allocator, OutOfMemoryError }
import silk.core.effect { Effect }

effect fn check() -> i32 ! OutOfMemoryError ? &mut Allocator {
  let edits = run diff.diffLines("a\nb\nc\n", "a\nc\nd\n", Algorithm.MyersMinimal)
  let runs = List<Edit>.asSlice(&edits)
  let mut deleted: usize = 0
  let mut inserted: usize = 0
  let mut at: usize = 0
  while at < runs.length {
    if runs[at].kind == EditKind.Delete {
      deleted = deleted + runs[at].length
    } else if runs[at].kind == EditKind.Insert {
      inserted = inserted + runs[at].length
    }
    at = at + 1
  }
  if deleted != 1 || inserted != 1 {
    return 1
  }
  return 0
}

effect fn exhausted(error: OutOfMemoryError) -> i32 {
  drop error
  return 2
}

pub fn main() -> i32 {
  let mut allocator = Allocator.systemAllocatorProvider()
  return run Effect.catchAll(check(), exhausted) |> Effect.provideMut<Allocator>(&mut allocator)
}

diffLinesBytes#

silk
pub effect fn diffLinesBytes(old: &[u8], new: &[u8], algorithm: Algorithm) -> List<Edit>
  ! OutOfMemoryError
  ? &mut Allocator

Returns the edits that turn the lines of the bytes old into the lines of new.

When to use#

Use this function for file contents and other bytes that need not be UTF-8. For text, use diffLines.

Details#

A line ends after each \n byte. A last line without \n is a line too, and it differs from the same line with \n. Lines are compared byte for byte. The runs cover both inputs in order, with line indices from 0. Where lines change, a Delete run comes before the Insert run.

Errors#

  • OutOfMemoryError if the Allocator cannot supply the scratch storage or the result.

unified#

silk
pub effect fn unified(
  oldName: string,
  old: string,
  newName: string,
  new: string,
  context: usize,
  algorithm: Algorithm,
) -> String
  ! OutOfMemoryError
  ? &mut Allocator

Returns the unified diff from old to new, with context unchanged lines around each change.

When to use#

Use this function to show how a text changed, or to make a patch for apply or for tools such as patch and git apply.

Details#

The diff starts with --- oldName and +++ newName, then has one @@ -start,count +start,count @@ hunk for each group of changes. Changes with at most 2 * context unchanged lines between them share a hunk. Each line starts with ' ' for an unchanged line, '-' for a deleted line, or '+' for an inserted line. A last line without \n is followed by \n\ No newline at end of file\n. The names are written as given, so pass a/path and b/path for the form that git apply expects. The usual context is 3.

Equal texts give an empty diff, with no header.

Errors#

  • OutOfMemoryError if the Allocator cannot supply the scratch storage or the result.

Gotchas#

A name that contains a tab or a newline makes a header that other tools read differently.

Examples#

Write a diff with one line of context#

silk
import silk.text.diff
import silk.text.diff { Algorithm }
import silk.mem.allocator { Allocator, OutOfMemoryError }
import silk.core.effect { Effect }
import silk.text.string { String }

effect fn check() -> i32 ! OutOfMemoryError ? &mut Allocator {
  let old = "one\ntwo\nthree\nfour\n"
  let new = "one\n2\nthree\nfour"
  let text = run diff.unified("a/n.txt", old, "b/n.txt", new, 1, Algorithm.Histogram)
  let expected = "--- a/n.txt\n+++ b/n.txt\n@@ -1,4 +1,4 @@\n one\n-two\n+2\n three\n-four\n+four\n\\ No newline at end of file\n"
  if String.view(&text) != expected {
    return 1
  }
  return 0
}

effect fn exhausted(error: OutOfMemoryError) -> i32 {
  drop error
  return 2
}

pub fn main() -> i32 {
  let mut allocator = Allocator.systemAllocatorProvider()
  return run Effect.catchAll(check(), exhausted) |> Effect.provideMut<Allocator>(&mut allocator)
}

unifiedBytes#

silk
pub effect fn unifiedBytes(
  oldName: &[u8],
  old: &[u8],
  newName: &[u8],
  new: &[u8],
  context: usize,
  algorithm: Algorithm,
) -> Bytes
  ! OutOfMemoryError
  ? &mut Allocator

Returns the unified diff from the bytes old to new, with context unchanged lines around each change.

When to use#

Use this function for file contents and paths that need not be UTF-8. For text, use unified.

Details#

The diff starts with --- oldName and +++ newName, then has one @@ -start,count +start,count @@ hunk for each group of changes. Changes with at most 2 * context unchanged lines between them share a hunk. Each line starts with ' ', '-', or '+'. A last line without \n is followed by \n\ No newline at end of file\n. Equal inputs give an empty diff, with no header.

Errors#

  • OutOfMemoryError if the Allocator cannot supply the scratch storage or the result.

Span#

silk
pub struct Span

A range of bytes in a patch text.

Details#

The parts of a Patch are spans of the text that parse read, so they do not copy it. Use Span.text or Span.bytes with that same text to get the part.

Field offset#

silk
pub offset: usize

The offset of the first byte from the start of the source.

Field length#

silk
pub length: usize

The number of bytes.

Implementation Copy for Span#

silk
impl Copy for Span

Implementation Span#

silk
impl Span

Method text#

silk
pub fn text<'source>(self: &Self, source: string<'source>) -> string<'source>

Returns the part of source that the span covers.

Gotchas#

source must be the text that the span came from. The function traps if the span ends past the end of source, or does not start and end on character boundaries.

Method bytes#

silk
pub fn bytes<'source>(self: &Self, source: &'source [u8]) -> &'source [u8]

Returns the bytes of source that the span covers.

Gotchas#

source must be the bytes that the span came from. The function traps if the span ends past the end of source.

LineKind#

silk
pub enum LineKind

What one line of a hunk does: keep, delete, or insert it.

Context#

silk
Context

The line is in the old and the new file.

Delete#

silk
Delete

The line is only in the old file.

Insert#

silk
Insert

The line is only in the new file.

PatchLine#

silk
pub struct PatchLine

One line of a hunk: what it does, and its content in the patch text.

Field kind#

silk
pub kind: LineKind

What the line does.

Field text#

silk
pub text: Span

The content of the line after its ' ', '-', or '+' prefix, with its \n. A line that a \ No newline at end of file line follows has no \n. A context line that is empty in the patch, because a tool removed its space, is the \n alone.

Implementation Copy for PatchLine#

silk
impl Copy for PatchLine

Hunk#

silk
pub struct Hunk

One hunk of a file patch: its @@ header and where its lines are.

Details#

The numbers are as the header gives them. A range with no lines, such as -0,0, gives the line before the change, so its start is one less than for a range with lines. The lines of the hunk are lineCount items of FilePatch.lines from firstLine.

Field oldStart#

silk
pub oldStart: usize

The line number of the first old line, from 1, or of the line before the change for an empty range.

Field oldLength#

silk
pub oldLength: usize

The number of context and deleted lines.

Field newStart#

silk
pub newStart: usize

The line number of the first new line, from 1, or of the line before the change for an empty range.

Field newLength#

silk
pub newLength: usize

The number of context and inserted lines.

Field section#

silk
pub section: Span

The text after the closing @@, such as a function name. Empty if there is none.

Field firstLine#

silk
pub firstLine: usize

The index of the first line of the hunk in FilePatch.lines.

Field lineCount#

silk
pub lineCount: usize

The number of lines of the hunk in FilePatch.lines.

Implementation Copy for Hunk#

silk
impl Copy for Hunk

FileChange#

silk
pub enum FileChange

What a file patch does to its file: change, create, delete, rename, or copy it.

Details#

A git patch says so in its extended header lines, such as new file mode or rename from. Other patches create a file when the old path is /dev/null, and delete one when the new path is /dev/null.

Modified#

silk
Modified

The file changes in place.

Created#

silk
Created

The file is new.

Deleted#

silk
Deleted

The file is removed.

Renamed#

silk
Renamed

The file moves to a new path, and can also change.

Copied#

silk
Copied

A copy of the file is made at a new path, and the copy can also change.

FilePatch#

silk
pub struct FilePatch

The changes to one file: its paths, modes, and hunks.

Details#

The paths and lines are Span ranges of the patch text. In a git patch, the paths lose their a/ and b/ prefixes, so they are relative to the repository. In other patches they are as written in the --- and +++ lines, up to a tab. Use apply to apply the hunks to the old text.

Field change#

silk
pub change: FileChange

What the patch does to the file.

Field oldPath#

silk
pub oldPath: Option<Span>

The path of the old file, or none for /dev/null or a patch that gives no path.

Field newPath#

silk
pub newPath: Option<Span>

The path of the new file, or none for /dev/null or a patch that gives no path.

Field oldMode#

silk
pub oldMode: Option<u32>

The old file mode of a git patch, such as 33188 for 100644 in octal.

Field newMode#

silk
pub newMode: Option<u32>

The new file mode of a git patch, such as 33261 for 100755 in octal.

Field binary#

silk
pub binary: bool

true if the patch changes binary content that it does not give as hunks.

Field hunks#

silk
pub hunks: List<Hunk>

The hunks, in order.

Field lines#

silk
pub lines: List<PatchLine>

The lines of all hunks, in order.

Patch#

silk
pub struct Patch

The file patches of one patch text, in order.

Details#

Its parts are Span ranges of the text that parse read, so keep that text alive while the patch is in use.

Field files#

silk
pub files: List<FilePatch>

One item for each file, in the order of the patch.

PatchReason#

silk
pub enum PatchReason

Why parse cannot read a patch.

InvalidHunkHeader#

silk
InvalidHunkHeader

An @@ line is not a valid hunk header.

InvalidHunkLine#

silk
InvalidHunkLine

A hunk line does not start with ' ', '-', '+' or '\\', or there are more old or new lines than the hunk header gives.

UnexpectedEnd#

silk
UnexpectedEnd

The patch ends before the hunk has all the lines that its header gives.

MissingFileHeader#

silk
MissingFileHeader

A hunk comes before any file header.

InvalidMode#

silk
InvalidMode

A file mode is not an octal number.

Implementation Debug for PatchReason#

silk
impl Debug for PatchReason

Operation debug#

silk
effect fn debug(self: &Self, formatter: &mut Formatter) -> ()
    ! WriterError
    ? &mut Writer

Writes the name of the variant, such as InvalidHunkHeader.

PatchError#

silk
pub struct PatchError

The error from parse or parseBytes: why the patch cannot be read, and on which line.

Field reason#

silk
pub reason: PatchReason

Why the patch cannot be read.

Field line#

silk
pub line: usize

The line number of the problem, from 1.

Implementation Debug for PatchError#

silk
impl Debug for PatchError

Operation debug#

silk
effect fn debug(self: &Self, formatter: &mut Formatter) -> ()
    ! WriterError
    ? &mut Writer

Writes the fields, as PatchError { reason: ..., line: ... }.

ApplyError#

silk
pub struct ApplyError

The error from apply or applyBytes: a hunk whose old lines are not in the original.

Field hunk#

silk
pub hunk: usize

The index of the hunk in FilePatch.hunks.

Implementation Debug for ApplyError#

silk
impl Debug for ApplyError

Operation debug#

silk
effect fn debug(self: &Self, formatter: &mut Formatter) -> ()
    ! WriterError
    ? &mut Writer

Writes the fields, as ApplyError { hunk: ... }.

parse#

silk
pub effect fn parse(text: string) -> Patch
  ! PatchError | OutOfMemoryError
  ? &mut Allocator

Reads the patch text, as diff -u, git diff, and git format-patch write it.

When to use#

Use this function to read a patch before apply applies its files, or to list the files and changes that a patch makes.

Details#

A file starts at a diff --git line, or at a --- line that a +++ line follows. Git extended header lines give the modes, renames, copies, and binary changes. Each hunk reads as many lines as its @@ header gives, and a \ No newline at end of file line removes the \n of the line before it. Other lines, such as a commit message or index lines, are skipped. The parts of the result are Span ranges of text.

Errors#

  • PatchError with reason InvalidHunkHeader for an @@ line that is not a hunk header, InvalidHunkLine for a hunk line that does not start with ' ', '-', '+', or '\\' or that is more than the header gives, UnexpectedEnd if the text ends in a hunk, MissingFileHeader for a hunk before any file header, and InvalidMode for a mode that is not octal.

  • OutOfMemoryError if the Allocator cannot supply the result.

Gotchas#

Quoted git paths, which git writes for names with special characters, keep their quotes and escapes.

Examples#

List the files of a git patch#

silk
import silk.text.diff
import silk.text.diff { FileChange, FilePatch, PatchError, Span }
import silk.collections.list { List }
import silk.core.option { Option }
import silk.mem.allocator { Allocator, OutOfMemoryError }
import silk.core.effect { Effect }

effect fn check() -> i32 ! PatchError | OutOfMemoryError ? &mut Allocator {
  let text = "diff --git a/src/app.js b/src/app.js\nindex 1111111..2222222 100644\n--- a/src/app.js\n+++ b/src/app.js\n@@ -1 +1 @@\n-let x = 1\n+let x = 2\ndiff --git a/notes.txt b/notes.txt\nnew file mode 100644\n--- /dev/null\n+++ b/notes.txt\n@@ -0,0 +1 @@\n+hello\n"
  let patch = run diff.parse(text)
  let files = List<FilePatch>.asSlice(&patch.files)
  if files.length != 2 || files[1].change != FileChange.Created {
    return 1
  }
  let path = match files[0].newPath {
    Option<Span>.Some {value} => value.text(text)
    Option<Span>.None => ""
  }
  if path != "src/app.js" {
    return 1
  }
  return 0
}

effect fn failed(error: PatchError | OutOfMemoryError) -> i32 {
  drop error
  return 2
}

pub fn main() -> i32 {
  let mut allocator = Allocator.systemAllocatorProvider()
  return run Effect.catchAll(check(), failed) |> Effect.provideMut<Allocator>(&mut allocator)
}

parseBytes#

silk
pub effect fn parseBytes(text: &[u8]) -> Patch
  ! PatchError | OutOfMemoryError
  ? &mut Allocator

Reads the patch text, given as bytes that need not be UTF-8, as diff -u, git diff, and git format-patch write it.

When to use#

Use this function for a patch read from a file, whose contents need not be UTF-8. For text, use parse.

Details#

A file starts at a diff --git line, or at a --- line that a +++ line follows. Git extended header lines give the modes, renames, copies, and binary changes. Each hunk reads as many lines as its @@ header gives, and a \ No newline at end of file line removes the \n of the line before it. Other lines are skipped. The parts of the result are Span ranges of text.

Errors#

  • PatchError with reason InvalidHunkHeader, InvalidHunkLine, UnexpectedEnd, MissingFileHeader, or InvalidMode, and the line number of the problem.

  • OutOfMemoryError if the Allocator cannot supply the result.

apply#

silk
pub effect fn apply(original: string, patch: string, file: &FilePatch) -> String
  ! ApplyError | OutOfMemoryError
  ? &mut Allocator

Returns original with the hunks of file applied, where file was read from patch.

When to use#

Use this function to apply one file of a patch that parse read, for example to patch a dependency before a build.

Details#

The hunks apply in order. Each hunk goes where its context and deleted lines are the lines of original, first at the line that its header gives, adjusted by how far the last hunk moved, then at the nearest line before or after that place. Lines are compared byte for byte, including the \n. A new file applies to an empty original. Only the hunks change the text: renames, modes, and binary changes are for the caller to make.

Errors#

  • ApplyError with the index of the first hunk whose old lines are not in original after the last applied hunk.

  • OutOfMemoryError if the Allocator cannot supply the result.

Gotchas#

patch must be the text that file was read from, because the lines of file are ranges of it.

Examples#

Apply a hunk after the file grew#

silk
import silk.text.diff
import silk.text.diff { ApplyError, FilePatch, PatchError }
import silk.collections.list { List }
import silk.mem.allocator { Allocator, OutOfMemoryError }
import silk.core.effect { Effect }
import silk.text.string { String }

effect fn check() -> i32 ! PatchError | ApplyError | OutOfMemoryError ? &mut Allocator {
  let text = "--- a/list.txt\n+++ b/list.txt\n@@ -1,3 +1,3 @@\n apple\n-banana\n+cherry\n melon\n"
  let patch = run diff.parse(text)
  let file = &List<FilePatch>.asSlice(&patch.files)[0]
  let patched = run diff.apply("fig\napple\nbanana\nmelon\n", text, file)
  if String.view(&patched) != "fig\napple\ncherry\nmelon\n" {
    return 1
  }
  return 0
}

effect fn failed(error: PatchError | ApplyError | OutOfMemoryError) -> i32 {
  drop error
  return 2
}

pub fn main() -> i32 {
  let mut allocator = Allocator.systemAllocatorProvider()
  return run Effect.catchAll(check(), failed) |> Effect.provideMut<Allocator>(&mut allocator)
}

applyBytes#

silk
pub effect fn applyBytes(original: &[u8], patch: &[u8], file: &FilePatch) -> Bytes
  ! ApplyError | OutOfMemoryError
  ? &mut Allocator

Returns the bytes original with the hunks of file applied, where file was read from the bytes patch.

When to use#

Use this function for file contents that need not be UTF-8. For text, use apply.

Details#

The hunks apply in order. Each hunk goes where its context and deleted lines are the lines of original, first at the line that its header gives, adjusted by how far the last hunk moved, then at the nearest line before or after that place. Lines are compared byte for byte, including the \n. Only the hunks change the bytes: renames, modes, and binary changes are for the caller to make.

Errors#

  • ApplyError with the index of the first hunk whose old lines are not in original after the last applied hunk.

  • OutOfMemoryError if the Allocator cannot supply the result.

Gotchas#

patch must be the bytes that file was read from, because the lines of file are ranges of them.

Category: Text.

Profiles: aarch64-apple-darwin, aarch64-unknown-linux-gnu, aarch64-unknown-linux-gnu-no-libc, x86_64-unknown-linux-gnu, x86_64-unknown-linux-gnu-no-libc, wasm32-unknown-unknown, wasm32-wasip1, wasm32-wasip2, x86_64-pc-windows-msvc, wasm64-unknown-unknown.

View the source of this page