{
  "$type": "site.standard.document",
  "bskyPostRef": {
    "cid": "bafyreiht2n62dzkvg6nw5iddukwf4zaziqrwobysu5ltrhzclbbm6oljrq",
    "uri": "at://did:plc:wszrgoqdwy3i2dfeub2mt3wf/app.bsky.feed.post/3mj7r5jraos72"
  },
  "coverImage": {
    "$type": "blob",
    "ref": {
      "$link": "bafkreifg34x3rrfytkhhnsp366yltzosmjtmeakkmsj7csaa262odbf5oa"
    },
    "mimeType": "image/png",
    "size": 2383
  },
  "description": "How to write an external tool for `git diff` to delegate complex diffs to.",
  "path": "/posts/2026/04/11/how-git-diff-driver/",
  "publishedAt": "2026-04-11T11:32:10.000Z",
  "site": "https://www.jvt.me",
  "tags": [
    "blogumentation",
    "openapi",
    "since November 2024",
    "while I was implementing renovate-packagedata-diff",
    "the Git Diffs man page",
    "Andrew Nesbitt",
    "Git Diff Drivers",
    "a separate for oasdiff as a diff driver",
    "my separate post about this",
    "oasdiff"
  ],
  "textContent": "Something I've been meaning to write about since November 2024 is how to create an external command for diffing between files with `git diff`.\n\nI found that while I was implementing renovate-packagedata-diff that there seemed to be a lack of documentation around how to do it, so it would be worthwhile me blogging about it.\n\n(I've since found that there is _some_ documentation in the Git Diffs man page, but it wasn't exactly well discoverable)\n\nIt's been at the back of my mind as a TODO for some time, and then I was nudged about it fairly recently by Andrew Nesbitt's good post on Git Diff Drivers, and this week looking at diffing OpenAPI specs with `oasdiff`.\n\nI thought I'd use this as an opportunity for blogging about how this works, as well as adding a separate for oasdiff as a diff driver.\n\nNote that this is a case where we want to expose more information in our output, and can't rely on the `textconv` method to convert a (binary) file to a more diff-able textual format.\n\nIn a lot of cases, using `textconv` is likely sufficient!\n\n## What arguments do we need to handle?\n\nAlthough many tools would work out-of-the-box if they expect to be run as `tool [before] [after]`, `git diff` passes _7_ arguments to the external tool it's calling.\n\nAlthough surprising, this does provide some richer data that is useful to have.\n\nTo give a more \"worked example\", let's look at what this looks like for updating an existing file, or adding/deleting a file:\n\n\n    # newlines added for readability purpose only\n\n    # when an existing file is updated\n    renovate-packagedata-diff\n      renovate/github-co-cddo-api-catalogue.json             # 1: filename in the repo\n      /tmp/git-blob-shryRa/github-co-cddo-api-catalogue.json # 2: \"before\"\n      f0a1311ae439fff36f994a3be5d5a7eb7d7a34dc               # 3: SHA-1 hash of the \"before\" file\n      100644                                                 # 4: octal mode of the \"before\" file\n      /tmp/git-blob-y2mrZp/github-co-cddo-api-catalogue.json # 5: \"after\"\n      e39975894a72f706e6a59bccf31120ffaa219ff3               # 6: SHA-1 hash of the \"after\" file\n      100644                                                 # 7: octal mode of the \"after\" file\n\n    # when a net-new file is created\n    renovate-packagedata-diff\n      renovate/github-co-cddo-api-catalogue.json             # 1: filename in the repo\n      /dev/null                                              # 2: \"before\"\n      .                                                      # 3: SHA-1 hash of the \"before\" file\n      .                                                      # 4: octal mode of the \"before\" file\n      /tmp/git-blob-iAbnMD/github-co-cddo-api-catalogue.json # 5: \"after\"\n      5c55e5b99b21db68c360419d44dac906c336bec6               # 6: SHA-1 hash of the \"after\" file\n      100644                                                 # 7: octal mode of the \"after\" file\n\n    # when a file is deleted\n    renovate-packagedata-diff\n      renovate/github-co-cddo-api-catalogue.json             # 1: filename in the repo\n      /tmp/git-blob-aBldZ4/github-co-cddo-api-catalogue.json # 2: \"before\"\n      6b24e38aa4aac8e00e44ab68e156744138ef6afc               # 3: SHA-1 hash of the \"before\" file\n      100644                                                 # 4: octal mode of the \"before\" file\n      /dev/null                                              # 5: \"after\"\n      .                                                      # 6: SHA-1 hash of the \"after\" file\n      .                                                      # 7: octal mode of the \"after\" file\n\n\nNote that `/dev/null` is used when a file was created/deleted, and that arguments that aren't relevant in these cases are provided as a `.`.\n\nIt can also be useful to see if the environment variable `GIT_PAGER_IN_USE` is set, if you'd like your command to be able to handle regular arguments and the `git diff` arguments.\n\n## Example with `oasdiff`\n\nAs noted in my separate post about this, with this information it's straightforward to write a lightweight wrapped script around the oasdiff tool for comparing OpenAPI specs.\n\nFor instance, the most basic implementation we can add is:\n\n\n    #!/usr/bin/env bash\n\n    # Via https://www.jvt.me/posts/2026/04/11/oasdiff-driver/\n    # A diff driver for `git diff` to provide a human-readable changelog for a given OpenAPI spec\n\n    if [[ \"$2\" == \"/dev/null\" ]]; then\n    \techo \"$1 was added\"\n    \texit 0\n    elif [[ \"$5\" == \"/dev/null\" ]]; then\n    \techo \"$1 was deleted\"\n    \texit 0\n    fi\n\n    # I prefer to have colour always reported\n    oasdiff changelog \"$2\" \"$5\" --color always\n\n\nThis doesn't handle any changes in permissions - which may be useful to report - and it may be worth using the SHA-1 checksums of the files to cache the resulting diffs.",
  "title": "How to build a git diff driver",
  "updatedAt": "2026-04-11T11:32:10.000Z"
}