appscript.dev
Automation Advanced Docs Drive

Build a Doc version-comparison report

Diff two Doc revisions and summarise what changed — for contract redlines.

Published Aug 31, 2025

When Northwind sends a contract back and forth with a client, the legal team needs to know exactly what moved between the version they last reviewed and the one that just came back. Scrolling through revision history in the Docs UI is slow and error-prone — a single reworded clause is easy to miss.

This script pulls the plain text of two named revisions of a Doc and compares them line by line, reporting which lines were added and which were removed. It is the raw material for a redline summary — a fast way to spot what changed without reading the whole document twice.

What you’ll need

  • A Google Doc with at least two saved revisions you want to compare.
  • The Drive API enabled under Services (the + next to Services in the Apps Script editor) — the script calls Drive.Revisions.list to find revision IDs.
  • The Doc’s file ID, taken from its URL.

The script

/**
 * Lists the saved revisions of a Doc, newest last.
 * @param {string} docId The file ID of the Doc.
 * @return {Object[]} Revision metadata objects with id and modifiedTime.
 */
function listRevisions(docId) {
  const res = Drive.Revisions.list(docId);
  return res.revisions;
}

/**
 * Exports the plain text of a single revision of a Doc.
 * @param {string} docId The file ID of the Doc.
 * @param {string} revisionId The revision to export.
 * @return {string} The revision's text content.
 */
function exportRevision(docId, revisionId) {
  // The revisions endpoint with alt=media returns the file content itself.
  const url = `https://www.googleapis.com/drive/v3/files/${docId}/revisions/${revisionId}?alt=media`;
  return UrlFetchApp.fetch(url, {
    headers: { Authorization: `Bearer ${ScriptApp.getOAuthToken()}` },
  }).getContentText();
}

/**
 * Compares two revisions of a Doc and reports the lines that were
 * added and removed between them.
 * @param {string} docId The file ID of the Doc.
 * @param {string} oldRev The earlier revision ID.
 * @param {string} newRev The later revision ID.
 * @return {{added: string[], removed: string[]}} The line-level diff.
 */
function diffRevisions(docId, oldRev, newRev) {
  // 1. Export the text of both revisions.
  const oldText = exportRevision(docId, oldRev);
  const newText = exportRevision(docId, newRev);

  // 2. Split each into lines for a line-by-line comparison.
  const oldLines = oldText.split('\n');
  const newLines = newText.split('\n');

  // 3. Added lines appear in the new version but not the old; removed
  //    lines appear in the old but not the new.
  const added = newLines.filter((l) => !oldLines.includes(l));
  const removed = oldLines.filter((l) => !newLines.includes(l));

  // 4. Log the result so it can be read straight from the execution log.
  Logger.log(`Added (${added.length}):\n${added.join('\n')}`);
  Logger.log(`Removed (${removed.length}):\n${removed.join('\n')}`);
  return { added, removed };
}

How it works

  1. listRevisions calls Drive.Revisions.list to fetch the saved revisions of the Doc. Each revision object carries an id and a modifiedTime — use it to pick the two revision IDs you want to compare.
  2. exportRevision downloads the content of one revision. It hits the Drive API’s revisions endpoint with alt=media, which returns the file body itself rather than metadata, authorised by the script’s OAuth token.
  3. diffRevisions exports the text of both the old and new revisions.
  4. It splits each export into an array of lines.
  5. It computes added — lines present in the new version but absent from the old — and removed — lines present in the old but absent from the new.
  6. It logs both lists with their counts and returns them as an object, ready to feed into a report or an email.

Example run

Given a contract Doc, first list its revisions to get the IDs:

listRevisions('1abcContractId');
// [{ id: '0B...AQ', modifiedTime: '2025-08-20T...' },
//  { id: '0B...ZX', modifiedTime: '2025-08-29T...' }]

Then diff the two:

diffRevisions('1abcContractId', '0B...AQ', '0B...ZX');

The execution log shows what moved between them:

Added (2):
Payment is due within 14 days of invoice.
Either party may terminate with 30 days' notice.

Removed (1):
Payment is due within 30 days of invoice.

At a glance: the payment window was tightened and a termination clause was added.

Run it

This is an on-demand job — run it when a revised document comes back:

  1. Run listRevisions with your Doc ID and read the execution log to find the two revision IDs you want to compare.
  2. Call diffRevisions with the Doc ID and those two IDs (oldest first).
  3. Read the added and removed lines from the execution log.

Watch out for

  • Drive only retains revisions for 30 days unless they are pinned. Compare or pin important versions before they expire.
  • This is a line-level diff, not a word-level one. Reword a single phrase and the whole line shows as one removal plus one addition — fine for spotting change, less precise than a true redline.
  • filter/includes matches whole lines exactly. Two paragraphs that differ only in whitespace count as different lines, and identical repeated lines can confuse the comparison.
  • The diff ignores line order. A clause moved unchanged to a new position will not appear as added or removed — only genuinely new or deleted text shows up.
  • For very long contracts the nested filter is O(n²). It is fine for typical documents; for huge files, build a Set of each line array first.

Related