Skip to content

Repository files navigation

diff

License Go Reference Go Report Card GitHub CI codecov

demo

A pure Go text diff library providing an anchored diff algorithm, character-level inline diff, and colourised terminal rendering. It started life as internal/diff in test until I needed it in a few places so here it is 🎉

Project Description

diff computes the difference between two texts using an anchored diff algorithm. Unlike standard diff, which finds the smallest edit by line count, the anchored algorithm matches on lines that appear exactly once in both old and new — unique lines act as anchors, preventing unrelated blank lines or closing braces from being reused as false matches. The result is typically cleaner and more readable output, and the algorithm runs in O(n log n) rather than O(n²).

The library is split into two packages:

  • go.followtheprocess.codes/diff — the diff algorithm and all types. No external dependencies.
  • go.followtheprocess.codes/diff/render — colourised terminal rendering using ANSI escape codes via hue. Import only when you need colour output.

Installation

go get go.followtheprocess.codes/diff@latest

For colourised rendering also get the render subpackage (its hue dependency is pulled in automatically):

go get go.followtheprocess.codes/diff/render@latest

Quickstart

Plain unified diff

package main

import (
    "os"

    "go.followtheprocess.codes/diff"
)

func main() {
    old := []byte("hello\nworld\n")
    new := []byte("hello\nearth\n")

    // Returns nil when inputs are equal.
    out := diff.Diff("old.txt", old, "new.txt", new)
    os.Stdout.Write(out)
}

Output:

diff old.txt new.txt
--- old.txt
+++ new.txt
@@ -1,2 +1,2 @@
  hello
- world
+ earth

Colourised terminal rendering

package main

import (
    "os"

    "go.followtheprocess.codes/diff"
    "go.followtheprocess.codes/diff/render"
)

func main() {
    old := []byte("hello\nworld\n")
    new := []byte("hello\nearth\n")

    lines := diff.Lines("old.txt", old, "new.txt", new)
    os.Stdout.Write(render.Render(lines))
}

render.Render applies ANSI colour: red for removed lines, green for added lines, bold for headers. When a removed and added line appear as a 1:1 pair, changed characters are highlighted with a coloured background for precise inline diffing.

Character-level diff

ic := diff.CharDiff([]byte("hello world\n"), []byte("hello earth\n"))

for _, seg := range ic.Removed {
    fmt.Printf("removed segment changed=%v: %q\n", seg.Changed, seg.Text)
}
for _, seg := range ic.Added {
    fmt.Printf("added  segment changed=%v: %q\n", seg.Changed, seg.Text)
}

API Overview

Symbol Package Description
Lines diff Structured []Line output; nil if inputs are equal
Diff diff Raw unified-diff []byte; nil if inputs are equal
CharDiff diff Character-level segment diff for a removed/added line pair
Render diff/render Colourised []byte for terminal output

Acknowledgements

The core diff algorithm (diff.go) is derived from the Go standard library's internal/diff package.

Copyright 2022 The Go Authors. All rights reserved. Used under a BSD-style license.

Several optimisations — including common prefix/suffix trimming, the disjoint fast-path, and the CharDiff similarity ratio gate — were inspired by the similar Rust crate by Armin Ronacher.

Credits

This package was created with copier and the FollowTheProcess/go-template project template.

About

A pure Go text diff library

Resources

Code of conduct

Contributing

Stars

Watchers

Forks

Releases

Packages

Contributors

Languages