GitHub API - how to compare 2 commits

The official commit comparison API is Compare two commits:

GET /repos/:owner/:repo/compare/:base...:head

Both :base and :head can be either branch names in :repo or branch names in other repositories in the same network as :repo. For the latter case, use the format user:branch:

GET /repos/:owner/:repo/compare/user1:branchname...user2:branchname

Note that you can use tags or commit SHAs as well. For instance:

https://api.github.com/repos/git/git/compare/v2.2.0-rc1...v2.2.0-rc2

Note the '...', not '..' between the two tags.
And you need to have the oldest tag first, then the newer tag.

That gives a status:

  "status": "behind",
  "ahead_by": 1,
  "behind_by": 2,
  "total_commits": 1,

And for each commit, information about the files:

"files": [
    {
      "sha": "bbcd538c8e72b8c175046e27cc8f907076331401",
      "filename": "file1.txt",
      "status": "added",
      "additions": 103,
      "deletions": 21,
      "changes": 124,
      "blob_url": "https://github.com/octocat/Hello-World/blob/6dcb09b5b57875f334f61aebed695e2e4193db5e/file1.txt",
      "raw_url": "https://github.com/octocat/Hello-World/raw/6dcb09b5b57875f334f61aebed695e2e4193db5e/file1.txt",
      "contents_url": "https://api.github.com/repos/octocat/Hello-World/contents/file1.txt?ref=6dcb09b5b57875f334f61aebed695e2e4193db5e",
      "patch": "@@ -132,7 +132,7 @@ module Test @@ -1000,7 +1000,7 @@ module Test"
    }
  ]

BUT:

  • The response will include a comparison of up to 250 commits. If you are working with a larger commit range, you can use the Commit List API to enumerate all commits in the range.

  • For comparisons with extremely large diffs, you may receive an error response indicating that the diff took too long to generate. You can typically resolve this error by using a smaller commit range.


Notes:

"same network" means: two repositories hosted by the same Git repository hosting services (two repositories on github.com for example, or on the same on-premise GHE -- GitHub Enterprise -- instance)

You can therefore compare two branches between a repo and its fork.
Example:

https://api.github.com/repos/030/learn-go-with-tests/compare/master...quii:master
  • compare link
  • diff link

(this example compares a fork to its original repo, not the original repo to the fork: that is because the fork, in this case, is behind the original repo)


As noted by Tom Carver in the comments:

this suggested API silently maxes out at 300 files shown;
I haven't yet found an API that avoids this limitation


Investigating answers coming with the official API, one can find a barely mentioned way to get diffs from Github. Try this:

  wget -H 'Accept: application/vnd.github.v3.diff' \
    http://github.com/github/linguist/compare/96d29b76...a20631af.diff
  wget -H 'Accept: application/vnd.github.v3.diff' \
    http://github.com/github/linguist/compare/a20631af...96d29b76.diff

This is the link you provided as an example, with .diff appended. And the reverse diff of the same.

The header given makes sure the request is handled by the Github's v3 API. That's currently the default, but might change in the future. See Media Types.

Why two downloads?

Github serves linear diffs from older to newer versions, only. If the requested diff is indeed linear and from an older to a newer version, the second download will be empty.

If the requested diff is linear, but from a newer to an older version, the first download is empty. Instead, the whole diff is in the second download. Depending on what one want to achieve, one can normally apply it to the newer version or reverse-apply (patch -R) it to the older version.

If there is no linear relationship between the pair of requested commits, both downloads get answered with non-zero content. One from the common anchestor to the first commit and another, reversed one from this common anchestor to the other commit. Applying one diff normally and the other one reversed gives what applying the output of git diff 96d29b76..a20631af would give, too.

As far as I can tell, these raw diffs aren't subject to Github's API limitations. Requests for 540 commits with 1002 file changes went flawlessly.

Note: one can also append .patch instead of .diff. Then one still gets one big file for each, but a set of individual patches for each commit inside this file.

Tags:

Github