Structural Diff Library for Go
Find a file
Repository files (latest commit first)
Filename Latest commit message Latest commit date
Ralph Schaer 7069d85f0f upgrade
2026-09-27 09:01:18 +02:00
.github upgrade 2026-09-27 09:01:18 +02:00
cmd/demo upgrade 2026-02-20 09:56:43 +01:00
.gitignore Initial commit 2025-08-29 19:58:59 +02:00
basic_comparison_test.go upgrade 2026-02-20 09:56:43 +01:00
benchmark_test.go Add memory allocation reporting to benchmark functions 2025-11-27 06:12:14 +01:00
collections_test.go Enhance benchmark parsing to use median values from multiple runs and add tests for median calculation 2026-07-10 04:55:23 +02:00
compare.go Enhance JSON handling and cycle detection in comparison logic 2026-09-01 18:29:32 +02:00
configuration_test.go upgrade 2026-02-20 09:56:43 +01:00
edge_cases_and_memory_test.go Refactor comparison functions to use options for configuration 2025-11-27 19:32:06 +01:00
fuzz_test.go Refactor comparison functions to use options for configuration 2025-11-27 19:32:06 +01:00
go.mod upgrade 2026-09-03 20:36:17 +02:00
LICENSE update 2026-07-23 06:28:57 +02:00
output.go Enhance JSON handling and cycle detection in comparison logic 2026-09-01 18:29:32 +02:00
output_and_errors_test.go Enhance JSON handling and cycle detection in comparison logic 2026-09-01 18:29:32 +02:00
README.md Enhance JSON handling and cycle detection in comparison logic 2026-09-01 18:29:32 +02:00
review_fixes_test.go Enhance JSON handling and cycle detection in comparison logic 2026-09-01 18:29:32 +02:00
Taskfile.yml upgrade 2026-09-04 15:59:30 +02:00
type_handlers.go upgrade 2026-02-20 09:56:43 +01:00
types.go Enhance JSON handling and cycle detection in comparison logic 2026-09-01 18:29:32 +02:00
types_test.go Refactor comparison functions to use options for configuration 2025-11-27 19:32:06 +01:00

GoDiff

CI

Structural diff library for Go. Compares structs, slices, maps, and primitives recursively.

Requirements

Go 1.27 or newer.

Quick Start

go get github.com/ralscha/godiff
person1 := Person{Name: "Alice", Age: 30}
person2 := Person{Name: "Bob", Age: 30}

result, err := godiff.Compare(person1, person2)
if err != nil {
    panic(err)
}

fmt.Println("Has Differences:", result.HasDifferences())
// Output: Has Differences: true

fmt.Println("Total differences:", result.Count())
// Output: Total differences: 1

for _, diff := range result.Diffs {
    switch d := diff.(type) {
    case *godiff.MapDiff:
        // For map diffs
    case *godiff.SliceDiff:
        // For slice diffs
    case *godiff.StructDiff:
        fmt.Println("StructDiff - Field:", d.FieldName, "ChangeType:", d.ChangeType, "Left:", d.Left, "Right:", d.Right)
    case *godiff.Diff:
        // For primitive diffs
    }
}
// Output: StructDiff - Field: Name ChangeType: updated Left: Alice Right: Bob

fmt.Println("JSON Output:", result.ToJSON())
// Output:
// JSON Output: [
//   {
//     "type": "struct",
//     "path": "",
//     "leftValue": "Alice",
//     "rightValue": "Bob",
//     "fieldName": "Name",
//     "change": "UPDATED"
//   }
// ]


fmt.Println(result.String())
// Output:
// Found 1 differences:
// UPDATED Name: Alice -> Bob

Configuration

Options

result, err := godiff.Compare(left, right,
    godiff.WithIgnoreFields("Password", "User.Meta"),
    godiff.WithIgnoreSliceOrder(),
    godiff.WithCompareNumericValues(),
    godiff.WithMaxDepth(10),
)
Option Description
WithIgnoreFields(fields...) Skip specific fields by name or path
WithIgnoreSliceOrder() Compare slices without regard to element order
WithCompareNumericValues() Compare numeric values across different types
WithMaxDepth(n) Limit recursion depth (0 = unlimited)
WithCustomComparators(map) Custom comparison functions for specific types
WithTypeHandlers(handlers) Replace all type handlers; defaults handle time.Time, interfaces, functions, and channels
WithAdditionalTypeHandlers(handlers...) Add higher-priority handlers while retaining the defaults

Options apply recursively, including inside structs, maps, ordered slices, and unordered slices. Numeric comparison is exact: large integers are not treated as equal to rounded floating-point values.

Struct Tags

type Product struct {
    Name   string
    Tags   []string `diff:"ignoreOrder"` // Compare ignoring order
    Secret string   `diff:"ignore"`      // Skip this field
}

Demo

See the demo application for more examples:

go run ./cmd/demo

JSON

result.ToJSON() returns the formatted change array shown above using encoding/json/v2. DiffResult implements both json.Marshaler and the more efficient json.MarshalerTo, so it can be embedded directly in another JSON document with json.Marshal(result).

Benchmarks

Run the benchmark suite with allocation statistics:

go test ./... -bench . -benchmem -run ^$

License

MIT