diff2html + GitHub API = ???#

2024 Jul 20

This is just a quick & dirty (yeehaw) post to get community feedback on whether “I’m holding this thing correctly.” If anything useful comes out of the discussion I will be a good OSSamaritan and translate into PRs, issues, feature requests, etc.

I’m trying to use diff2html and the GitHub API together. That is, I pull commit data with the Get a commit endpoint and want to render the diff with diff2html.

I get commit data like this:

// Example commit that demonstrates almost all the ways you can change a file
const url = 'https://api.github.com/repos/kaycebasques/biodigitaljazz/commits/f0a19c4d1d9267669dcc0c124387e057cbda83ac';
const headers = {
  'X-GitHub-Api-Version': '2022-11-28',
  'Accept': 'application/vnd.github+json'
};
const response = await fetch(url, headers);
const data = await response.json();
console.log(data);

The GitHub endpoint lists a bunch of different ways that a file can change:

I created a demo commit to attempt to trigger all the change types but couldn’t figure out the last threee (copied, changed, and unchanged). What’s the difference between modified and changed? How the hell do I unchanged a file?? (Reverting it?)

Here’s the thing that I feel I’m holding wrong. On my naive first attempt, I basically passed the commit data from GitHub directly to diff2html, but nothing got rendered. On closer inspection it seems that diff2html expects more metadata than what GitHub provides. I concluded that I need to manually assembly the diff data into the format that diff2html expects.

// Construct the diff string in the "unified format" that diff2html requires
const parentCommitSha = data.parents[0].sha;
let diffLines = [];
for (let i = 0; i < data.files.length; i++) {
  const file = data.files[i];
  const {sha, filename, patch} = file;
  // 100644 means non-executable file which may not always be true but there doesn't
  // seem to be a way to extract file mode from this GitHub endpoint
  const fileMode = '100644';
  switch (file.status) {
    case 'added':
      diffLines.push(`diff --git a/${filename} b/${filename}`);
      diffLines.push(`new file mode ${fileMode}`);
      diffLines.push(`index 000000000..${parentCommitSha}`);
      diffLines.push(`--- /dev/null`);
      diffLines.push(`+++ b/${filename}`);
      break;
    case 'removed':
      diffLines.push(`diff --git a/${filename} b/${filename}`);
      diffLines.push(`deleted file mode ${fileMode}`);
      diffLines.push(`index ${sha}..000000000`);
      diffLines.push(`--- a/${filename}`);
      diffLines.push(`+++ /dev/null`);
      break;
    case 'renamed':
      const previousFilename = file.previous_filename;
      diffLines.push(`diff --git a/${previousFilename} b/${filename}`);
      diffLines.push(`similarity index 100%`);
      diffLines.push(`rename from ${previousFilename}`);
      diffLines.push(`rename to ${filename}`);
      break;
    case 'copied':  // ???
    case 'changed':  // ???
    case 'unchanged':  // ???
    case 'modified':
      diffLines.push(`diff --git a/${filename} b/${filename}`);
      diffLines.push(`index ${sha}..${parentCommitSha}`);
      diffLines.push(`--- a/${filename}`);
      diffLines.push(`+++ b/${filename}`);
      break;
  }
  if (patch) {
    diffLines.push(patch);
  }
}
const diffString = diffLines.join('\n');
console.log(diffString);

So am I actually supposed to manually construct the diff strings like this? Or is there some way to finagle the GitHub API into providing the data in the way that diff2html expects? Or maybe I missed a way to pass the GitHub API data directly to diff2html and let diff2html assemble it?

(For sake of completeness) here’s how I render with diff2html:

var targetElement = document.getElementById('myDiffElement');
var configuration = {
  drawFileList: false,
  fileListToggle: false,
  fileListStartVisible: false,
  fileContentToggle: false,
  matching: 'lines',
  outputFormat: 'line-by-line',
  synchronisedScroll: true,
  highlight: true,
  renderNothingWhenEmpty: false,
};
var diff2htmlUi = new Diff2HtmlUI(targetElement, diffString, configuration);
diff2htmlUi.draw();
diff2htmlUi.highlightCode();