/ˈdʏf/ - diff tool for YAML files, and sometimes JSON

δyƒƒ /ˈdʏf/ License Go Report Card Build and Tests Codecov Go Reference Release

dyff

A diff tool for YAML files, and sometimes JSON

Description

dyff is inspired by the way the old BOSH v1 deployment output reported changes from one version to another by only showing the parts of a YAML file that change.

Each difference is referenced by its location in the YAML document by using either the Spruce or go-patch path syntax. The output report aims to be as compact as possible to give a clear and simple overview of the change.

Similar to the standard diff tool, it follows the principle of describing the change by going from the from input file to the target to input file.

Input files can be local files (filesystem path), remote files (URI), or the standard input stream (using -).

All orders of keys in hashes are preserved during processing and output to the terminal, most notably in the sub-commands to convert YAML to JSON and vice versa.

Installation

On macOS, dyff is available via Homebrew:

brew install homeport/tap/dyff

Prebuilt binaries for a lot of operating systems and architectures can be downloaded from the releases section.

There is a convenience script to download the latest release for Linux or macOS if you want to keep it simple (you need curl and jq installed on your machine):

curl --silent --location https://tinyurl.com/y4qvdl4d | bash

You can download and build dyff from source using go get:

GO111MODULE=on go get github.com/homeport/dyff/cmd/dyff

Use cases and examples

  • Show the differences between two versions of cf-deployment YAMLs:

    dyff between \
      https://raw.githubusercontent.com/cloudfoundry/cf-deployment/v1.10.0/cf-deployment.yml \
      https://raw.githubusercontent.com/cloudfoundry/cf-deployment/v1.20.0/cf-deployment.yml

    dyff between example

  • Embed dyff into Git for better understandable differences

    # Setup...
    git config --local diff.dyff.command 'dyff_between() { dyff --color on between --omit-header "$2" "$5"; }; dyff_between'
    echo '*.yml diff=dyff' >> .gitattributes
    
    # And have fun, e.g.:
    git log --ext-diff -u
    git show --ext-diff HEAD

    dyff between example of a Git commit

  • Convert a JSON stream to YAML

    sometool --json | jq --raw-output '.data' | dyff yaml -
  • Sometimes you end up with YAML or JSON files, where the order of the keys in maps was sorted alphabetically. With dyff you can restructure keys in maps to a more human appealing order:

    sometool --export --json | dyff yaml --restructure -

    Or, rewrite a file in place with the restructured order of keys.

    dyff yaml --restructure --in-place somefile.yml
  • Just print a YAML (or JSON) file to the terminal to look at it. By default, dyff will use a neat output schema which includes different colors and indent helper lines to improve readability. The colors are roughly based on the default Atom schema and work best on dark terminal backgrounds. The neat output is disabled if the output of dyff is redirected into a pipe, or you can disable it explicitly using the --plain flag.

    dyff yaml somefile.yml
  • Convert a YAML file to JSON and vice versa:

    dyff json https://raw.githubusercontent.com/cloudfoundry/cf-deployment/v1.19.0/cf-deployment.yml

    The dyff sub-command (yaml, or json) defines the output format, the tool automatically detects the input format itself.

    dyff yaml https://raw.githubusercontent.com/homeport/dyff/develop/assets/bosh-yaml/manifest.json
Comments
  • Comparing output of

    Comparing output of "kubectl diff -k" fails with "unknown command"

    dyff verison 1.5.4 on linux

    I configure dyff to be the external diff for kubectl export KUBECTL_EXTERNAL_DIFF="dyff between --omit-header --set-exit-code" Then I ask kubectl to show the diff (before applying) of kustomize output kubectl diff -k . The error is

    Error occurred
    unknown command "/tmp/LIVE-1245488198" for "dyff"
    

    If I instead extract the current deployment and the result of kubectl kustomize . I can show the diff just fine.

    Thanks.

  • Ignore exclude/filter action if filterPath given is nil

    Ignore exclude/filter action if filterPath given is nil

    This hopefully solves issue 232. When comparing two YAMLs with a document removed and using the --exclude flag I noticed that dyff threw a panic. I think this is because those Diff objects don't have a Path set (must be nil) so when we do --exclude (or any of the other exclude/filter flags) it will panic trying to produce the report. Let me know if there is anything else you'd like me to do :)

  • Support different document ordering

    Support different document ordering

    Diffing two files that contain the same set of documents, but in different orders shows a diff. At least with e.g. Kubernetes resources we can identify the document uniquely using kind and metadata.name, which would allow dyff to pair up documents.

    I realise this is probably related to #16 as it's dealing with multiple documents.

    a.yaml:

    apiVersion: apps/v1
    kind: Deployment
    metadata:
      name: x
    ---
    apiVersion: v1
    kind: Service
    metadata:
      name: y
    

    b.yaml:

    apiVersion: v1
    kind: Service
    metadata:
      name: y
    ---
    apiVersion: apps/v1
    kind: Deployment
    metadata:
      name: x
    

    dyff between --ignore-order-changes --detect-kubernetes a.yaml b.yaml:

         _        __  __
       _| |_   _ / _|/ _|  between a.yaml, two documents
     / _' | | | | |_| |_       and b.yaml, two documents
    | (_| | |_| |  _|  _|
     \__,_|\__, |_| |_|   returned six differences
            |___/
    
    apiVersion  (document #1)
      ± value change
        - apps/v1
        + v1
    
    kind  (document #1)
      ± value change
        - Deployment
        + Service
    
    metadata.name  (document #1)
      ± value change
        - x
        + y
    
    apiVersion  (document #2)
      ± value change
        - v1
        + apps/v1
    
    kind  (document #2)
      ± value change
        - Service
        + Deployment
    
    metadata.name  (document #2)
      ± value change
        - y
        + x
    
    
  • arm64 support

    arm64 support

    I have some issue installing on macOS with m1:

    $ curl --silent --location https://git.io/JYfAY | sudo bash
    Unsupported operating system darwin or machine type arm64: Please check https://github.com/homeport/dyff/releases manually.
    

    arm64 is supported in goreleaser https://github.com/goreleaser/goreleaser/pull/1956 with go 1.16 version

  • Support for diffing multilines string content

    Support for diffing multilines string content

    fixes #171

    If implemented, an inline diff will be processed for multilines strings, and diff hunks will be highlighted.

    Signed-off-by: Soule BA [email protected]

  • Instructions for use in Git don't work;

    Instructions for use in Git don't work; "Unsupported protocol scheme"

    I've just tried to use this as a git-dffer and get the following errors having simply copy-pasted from the readme.

    I just created a small repo to test this in:

    $ git log
    commit 93fb76bc6324c8a45e3764478634e1977ecef5ba (HEAD -> master)
    Author: Avi Greenbury <[email protected]>
    Date:   Thu Dec 23 14:22:35 2021 +0000
    
        add stuff
    
    commit 6507158842315aa75213cc41a6305ac475b11086
    Author: Avi Greenbury <[email protected]>
    Date:   Thu Dec 23 14:22:13 2021 +0000
    
        testing
    
    $ git show
    commit 93fb76bc6324c8a45e3764478634e1977ecef5ba (HEAD -> master)
    Author: Avi Greenbury <[email protected]>
    Date:   Thu Dec 23 14:22:35 2021 +0000
    
        add stuff
    
    diff --git a/test.yml b/test.yml
    index 2eb7df4..acf0792 100644
    --- a/test.yml
    +++ b/test.yml
    @@ -3,3 +3,5 @@ test: true
     what:
       - dyff
       - diff
    +  - git-diff
    +  - things
    
    $ git config --local diff.dyff.command 'dyff_between() { dyff --color on between --omit-header "$2" "$5"; }; dyff_between'
    
    $ echo '*.yml diff=dyff' >> .gitattributes
    
    $ git log --ext-diff -u
    commit 93fb76bc6324c8a45e3764478634e1977ecef5ba (HEAD -> master)
    Author: Avi Greenbury <[email protected]>
    Date:   Thu Dec 23 14:22:35 2021 +0000
    
        add stuff
    
    ╭ Error: failed to load input files
    │ unable to load data from
    │ /tmp/YUaX0G_test.yml: Get
    │ "/tmp/YUaX0G_test.yml": unsupported protocol scheme ""
    ╵
    fatal: external diff died, stopping at test.yml
    
    $ git show --ext-diff HEAD
    commit 93fb76bc6324c8a45e3764478634e1977ecef5ba (HEAD -> master)
    Author: Avi Greenbury <[email protected]>
    Date:   Thu Dec 23 14:22:35 2021 +0000
    
        add stuff
    
    ╭ Error: failed to load input files
    │ unable to load data from
    │ /tmp/hbpFpg_test.yml: Get
    │ "/tmp/hbpFpg_test.yml": unsupported protocol scheme ""
    ╵
    fatal: external diff died, stopping at test.yml
     
    

    This is installed from Snap on ubuntu 20.04; dyff version 1.4.6 and git 2.25.1

  • error with kubectl diff compatibility

    error with kubectl diff compatibility

    seems like kubectl is expecting the external diff variable to be just the executable name? is there a workaround? thanks

    % export KUBECTL_EXTERNAL_DIFF="dyff between --omit-header --set-exit-code"
    % kubectl diff -f foo.yaml
    error: failed to run "dyff between --omit-header --set-exit-code": executable file not found in $PATH

    % kubectl version Client Version: version.Info{Major:"1", Minor:"19", GitVersion:"v1.19.7", GitCommit:"1dd5338295409edcfff11505e7bb246f0d325d15", GitTreeState:"clean", BuildDate:"2021-01-13T13:23:52Z", GoVersion:"go1.15.5", Compiler:"gc", Platform:"darwin/amd64"} Server Version: version.Info{Major:"1", Minor:"17+", GitVersion:"v1.17.17-gke.4900", GitCommit:"2812f9fb0003709fc44fc34166701b377020f1c9", GitTreeState:"clean", BuildDate:"2021-03-19T09:19:27Z", GoVersion:"go1.13.15b4", Compiler:"gc", Platform:"linux/amd64"}

  • Option to filter results

    Option to filter results

    I'm using dyff for comparing k8s manifests which can be quite huge. So I think it'll be useful if we can filter out results using some pattern like below.

    dyff between file1.json file2.json --filter deployment.spec.template.spec.containers.env
    

    PS: Great job with the visualisation 🥇

  • Configure listItemIdentifierCandidates via settings

    Configure listItemIdentifierCandidates via settings

    I am comparing YAML files that have embedded lists that use key identifiers other than "name", "key", id". It would be useful to be able to configure list identifiers through the settings (in a more flexible way than settings.KubernetesEntityDetection).

    I think that setting "branch" as an identifier will solve a problem I am having with the following false positive: (sequence ordering is disabled):

    repository.protectedbranches
      - two list entries removed:
        - allowedtoforcepush: false
          allowedtomerge:
          - name: vcdc-developers
            level: 40
          allowedtopush:
          - name: "No one"
            level: 0
          - name: "Build Systems"
            level: 40
          branch: main
          codeownerapproval: true
        - allowedtoforcepush: false
          allowedtomerge:
          - name: vcdc-developers
            level: 40
          allowedtopush:
          - name: "No one"
            level: 0
          - name: "Build Systems"
            level: 40
          branch: go1.17-boringcrypto
          codeownerapproval: true
        
      
      + two list entries added:
        - branch: main
          allowedtomerge:
          - name: vcdc-developers
            level: 40
          allowedtopush:
          - name: "Build Systems"
            level: 40
          - name: "No one"
            level: 0
          allowedtoforcepush: false
          codeownerapproval: true
        - branch: go1.17-boringcrypto
          allowedtomerge:
          - name: vcdc-developers
            level: 40
          allowedtopush:
          - name: "Build Systems"
            level: 40
          - name: "No one"
            level: 0
          allowedtoforcepush: false
          codeownerapproval: true
    
  • HumanReport output with additions including multiple '_' treats them as formatting directives in output

    HumanReport output with additions including multiple '_' treats them as formatting directives in output

    See for example:

    Screen Shot 2022-05-24 at 3 42 04 PM

    in actuality, those fields are like:

      string x_forwarded_host = 53;
      string worker_status = 54;
      uint64 worker_cpu_time_micro = 55;
    

    I'll take a look at raising a fix myself, but just wanted to raise the issue :)

  • Support Yaml Anchors and Aliases.

    Support Yaml Anchors and Aliases.

    Hi,

    This tool is really good and it helped me a lot. My request is to actually run the diff logic on the "expanded' version after expanding all the anchors and aliases.

    Thx Gil

  • interaction between regular expressions and YAML path segments is unclear

    interaction between regular expressions and YAML path segments is unclear

    This bug has morphed a bit. I had originally tried to do --exclude-regexp metadata\.annotations\.checksum/.*, mistakenly thinking that the dots were part of a string that needed to be escaped in order not to match too many strings, and not the separators between path segments.

    But that raises the question of how those two syntaxes interact. For instance, if I want to exclude any item whose path starts with metadata and ends with generation, how do I express that? It seems that metadata.*generation works, but possibly by accident? At least, I wouldn't know how to filter

    metadata:
      something:
        something-else:
          generation: 5
    

    distinctly from

    metadata.something.something-else.generation: 5
    

    or

    metadataXsomethingXsomething-elseXgeneration: 5
    

    There are a similar set of ambiguous patterns using / as the separator, too.

    In tracking this down, I noticed that the tests are also missing examples which contain regular expression metacharacters (not that they would have caught this particular problem).

    Ambiguity is probably fine 99% of the time, but even having that much documented would be really handy. Unfortunately, that seems like it's getting into manpage territory ....

    I'll note with a crazy look in my eye that helm uses (AFAIK completely undocumented) vertical tab (\v) as a separator internally, so if you have, say, a datadog repo configured, which has datadog, datadog-crds, and datadog-operator charts, you can search for \vdatadog/datadog\v to find just the first. Perhaps it might make sense to treat \v as a segment separator here, too, assuming it's not possible to use that character in a YAML key.

  • dyff stutters when diffing some multiline documents

    dyff stutters when diffing some multiline documents

    I have files with these contents:

    top: |
      object:
        aa_aaa: true
        bbb_bb_bbbbbbb: true
        ccccccc_ccccccc_ccccccc: true
      is_longenough123:
        # Stupid comment one
        # Stupid comment two
        # Stupid comment three
    

    and

    top: |
      is_longenough123:
        - name
      object:
        aa_aaa: true
        bbb_bb_bbbbbbb: true
        ccccccc_ccccccc_ccccccc: true
    

    If I dyff them, I get

         _        __  __
       _| |_   _ / _|/ _|  between dyffbug-a.yaml
     / _' | | | | |_| |_       and dyffbug-b.yaml
    | (_| | |_| |  _|  _|
     \__,_|\__, |_| |_|   returned one difference
            |___/
    
    top
      ± value change in multiline text (three inserts, one deletion)
          is_longenough123:
          object:
          object:
          object:
            aa_aaa: true
          object:
            bbb_bb_bbbbbbb: true
          object:
            ccccccc_ccccccc_ccccccc: true
          is_longenough123:
            # Stupid comment one
            # Stupid comment two
            # Stupid comment three
    

    broken-dyff which I'm pretty sure is not right.

    The issue is very sensitive to the contents of the string. I haven't been able to get it to reproduce with fewer than three lines in both object and is_longenough123, or changing the keys very much. The keys in object can't be shortened (or at least not much; I haven't tried all possible combinations) either, which is weird. Other than the length, it doesn't seem like the key names matter.

    For instance, if I drop the ccccccc_ccccccc_ccccccc key, I get what I'd expect:

    top
      ± value change in multiline text (one insert, one deletion)
          is_longenough123:
            - name
          object:
            aa_aaa: true
            bbb_bb_bbbbbbb: true
          is_longenough123:
            # Stupid comment one
            # Stupid comment two
            # Stupid comment three
    

    working-dyff

    (Screenshots are necessary, since the colors are what actually indicate what dyff thinks is being added or removed, and the problem goes away with --color=no. The blue bits are just iTerm2 being helpful.)

  • Exclude based on value

    Exclude based on value

    From my understanding is that --exclude only works for specifing the key name.

    Would it be possbile to exclude on the value?

    For example:

    from.yaml:

    some_key:
      clientID: aaa49a1-9162-bbb6-bf06-d7c787ccc
    

    to.yaml

    some_key:
      clientID: DRY_RUN
    

    dyff between from.yaml to.yaml --exclude-value DRY_RUN

    Should return no differences.

  • Error: failed to load input files while checking git diff

    Error: failed to load input files while checking git diff

    I follow the instructions to install dyff through homebrew and config it to git.

    brew install homeport/tap/dyff
    
    git config --local diff.dyff.command 'dyff_between() { dyff --color on between --omit-header "$2" "$5"; }; dyff_between'
    echo '*.yml diff=dyff' >> .gitattributes
    echo '*.yaml diff=dyff' >> .gitattributes
    

    But when I try to view the diff, dyff shows the following error.

    git log --ext-diff -u
    Date:   Mon Aug 22 09:10:40 2022 +0800
    
        Use dyff to restructure the dry run result
        
        Signed-off-by: R0CKSTAR <[email protected]>
    
    diff --git a/Makefile b/Makefile
    index 8b3491d..403161d 100644
    --- a/Makefile
    +++ b/Makefile
    @@ -60,6 +60,7 @@ go-test:
     0200: build
            bin/kustohelmize create --from=test/testdata/0200_sample.yaml test/output/0200/mychart
            helm install --dry-run xyz test/output/0200/mychart > test/output/0200/mychart.yaml
    +       dyff yaml -i --restructure test/output/0200/mychart.yaml
     
     ##@ Tools
     
    
    LAST DEPLOYED  (document #1)
      ± value change
        - Mon Aug 22 08:56:48 2022
        + Mon Aug 22 09:09:58 2022
    
    ╭ Error: failed to load input files
    │ unable to parse data from
    │ /var/folders/p9/dbq3q88x7c39y7tmwqgj6yjr0000gn/T//HsEOwh_xyz-yourchart-deployment.yaml:
    │ yaml: line 6: did not find expected node content
    ╵
    fatal: external diff died, stopping at test/output/0200/mychart/templates/xyz-yourchart-deployment.yaml
    

    BTW, dyff yaml -i --restructure test/output/0200/mychart.yaml doesn't work either. mychart.yaml can be found at https://github.com/yeahdongcn/kustohelmize/blob/main/test/output/0200/mychart.yaml

  • category of YAML diff?

    category of YAML diff?

    Hey all, thanks for this great tool!

    Can you please help me with the following questions?:

    • How can I get diff types from YAML files?
    • Is there a set of categories for this? Docs?

    I have been looking for docs for this, but I didn't find it.

    Thank you

JSON diff library for Go based on RFC6902 (JSON Patch)

jsondiff jsondiff is a Go package for computing the diff between two JSON documents as a series of RFC6902 (JSON Patch) operations, which is particula

Dec 4, 2022
A JSON diff utility

JayDiff A JSON diff utility. Install Downloading the compiled binary Download the latest version of the binary: releases extract the archive and place

Dec 11, 2022
A simple Go package to Query over JSON/YAML/XML/CSV Data
A simple Go package to Query over JSON/YAML/XML/CSV Data

A simple Go package to Query over JSON Data. It provides simple, elegant and fast ODM like API to access, query JSON document Installation Install the

Dec 27, 2022
A convenient syntax to generate JSON (or YAML) for commandline

clon A convenient syntax to generate JSON (or YAML) for commandline "mumbo-jumbo". Syntax Overview Syntax resembles that of JSON with a few caveats: a

May 14, 2021
Package json implements encoding and decoding of JSON as defined in RFC 7159

Package json implements encoding and decoding of JSON as defined in RFC 7159. The mapping between JSON and Go values is described in the documentation for the Marshal and Unmarshal functions

Jun 26, 2022
Json-go - CLI to convert JSON to go and vice versa
Json-go - CLI to convert JSON to go and vice versa

Json To Go Struct CLI Install Go version 1.17 go install github.com/samit22/js

Jul 29, 2022
JSON Spanner - A Go package that provides a fast and simple way to filter or transform a json document

JSON SPANNER JSON Spanner is a Go package that provides a fast and simple way to

Sep 14, 2022
Get JSON values quickly - JSON parser for Go
Get JSON values quickly - JSON parser for Go

get json values quickly GJSON is a Go package that provides a fast and simple way to get values from a json document. It has features such as one line

Dec 28, 2022
Fast JSON encoder/decoder compatible with encoding/json for Go
Fast JSON encoder/decoder compatible with encoding/json for Go

Fast JSON encoder/decoder compatible with encoding/json for Go

Jan 6, 2023
Devto-json2md - Convert the dev.to blog JSON export into Markdown files

devto-json2md Convert dev.to blog JSON export into Markdown files Features Exports Markdown compatible files using Hugo-compatible front-matter fields

Jan 8, 2022
A tool to aggregate and mine data from JSON reports of Go tests.

teststat A tool to aggregate and mine data from JSON reports of Go tests. Why? Mature Go projects often have a lot of tests, and not all of them are i

Sep 15, 2022
CLI tool to handle JSON logs

logit is a CLI tool that consumes logs in JSON and feeds them into a logs storage like file, Amazon CloudWatch, or Slack.

Sep 28, 2022
Online tool that convert JSON to Go.

json-to-go 将json生成go的数据结构。Online tool that convert JSON to Go. Online Link | 地址 Link1 | 国内 Link2 | 国外 Local Develope | 本地开发 suggest use(强烈建议使用) pnpm !

Aug 28, 2022
Fast JSON parser and validator for Go. No custom structs, no code generation, no reflection

fastjson - fast JSON parser and validator for Go Features Fast. As usual, up to 15x faster than the standard encoding/json. See benchmarks. Parses arb

Jan 5, 2023
Fast and flexible JSON encoder for Go
Fast and flexible JSON encoder for Go

Jettison Jettison is a fast and flexible JSON encoder for the Go programming language, inspired by bet365/jingo, with a richer features set, aiming at

Dec 21, 2022
HuJSON: JSON for Humans (comments and trailing commas)

HuJSON - Human JSON The HuJSON decoder is a JSON decoder that also allows comments, both /* ... */ and // to end of line trailing commas on arrays and

Dec 22, 2022
tson is JSON viewer and editor written in Go
tson is JSON viewer and editor written in Go

tson tson is JSON viewer and editor written in Go. This tool displays JSON as a tree and you can search and edit key or values. Support OS Mac Linux I

Nov 2, 2022
JSONata in Go Package jsonata is a query and transformation language for JSON

JSONata in Go Package jsonata is a query and transformation language for JSON. It's a Go port of the JavaScript library JSONata.

Nov 8, 2022
Slow and unreliable JSON parser generator (in progress)

VivaceJSON Fast and reliable JSON parser generator Todo List parse fields parse types generate struct generate (keypath+key) to struct Value Mapping F

Nov 26, 2022