appscript.dev
Automation Intermediate Drive Sheets

Generate a folder-level changelog

Track additions and deletions in a Northwind folder over time — a written history.

Published Nov 5, 2025

Drive has no built-in history for a folder. If a file vanishes from Northwind’s shared Deliverables folder, there is no log saying when it went or what was added since — you are left guessing from memory. For a folder that clients and freelancers both touch, that is a real gap.

This script builds that history itself. Each run takes a snapshot of the file IDs in a folder, compares it to the previous snapshot stored in Script Properties, and appends a row to a changelog Sheet for every file added or removed since. Run it on a schedule and you get a dated, written record of exactly what changed.

What you’ll need

  • A Drive folder to watch — its ID goes in the config below.
  • A blank Google Sheet for the changelog — its ID goes in the config too.
  • Nothing else. The previous snapshot is kept in Script Properties; the script manages it for you.

The script

// The folder whose contents you want to track over time.
const WATCHED_FOLDER_ID = '1abcWatchedFolderId';

// The Sheet that the changelog rows are appended to.
const CHANGELOG_SHEET_ID = '1abcChangelogId';

// The Script Property key under which the last snapshot is stored.
const SNAPSHOT_KEY = 'PREVIOUS_FILES';

/**
 * Compares the current file list of the watched folder against the
 * last snapshot and appends an "added" or "removed" row per change.
 */
function detectChanges() {
  const props = PropertiesService.getScriptProperties();

  // 1. Load the previous snapshot of file IDs (empty on the first run).
  const previous = JSON.parse(props.getProperty(SNAPSHOT_KEY) || '[]');

  // 2. Take a fresh snapshot of what is in the folder right now.
  const current = listFileIds(WATCHED_FOLDER_ID);

  // 3. Diff the two lists: IDs new this run, and IDs that disappeared.
  const added = current.filter((id) => !previous.includes(id));
  const removed = previous.filter((id) => !current.includes(id));

  // 4. Append a dated row to the changelog for every change.
  if (added.length || removed.length) {
    const sheet = SpreadsheetApp.openById(CHANGELOG_SHEET_ID).getSheets()[0];
    added.forEach((id) => sheet.appendRow([new Date(), 'added', id]));
    removed.forEach((id) => sheet.appendRow([new Date(), 'removed', id]));
    Logger.log(`Logged ${added.length} added, ${removed.length} removed.`);
  } else {
    Logger.log('No changes since the last run.');
  }

  // 5. Store the current snapshot as the baseline for next time.
  props.setProperty(SNAPSHOT_KEY, JSON.stringify(current));
}

/**
 * Returns an array of the file IDs directly inside a folder.
 */
function listFileIds(folderId) {
  const ids = [];
  const it = DriveApp.getFolderById(folderId).getFiles();
  while (it.hasNext()) ids.push(it.next().getId());
  return ids;
}

How it works

  1. detectChanges reads the previous snapshot from Script Properties. On the very first run there is nothing stored, so it defaults to an empty array.
  2. listFileIds walks the watched folder and returns the IDs of every file in it right now — this is the current snapshot.
  3. It diffs the two lists: an ID in current but not previous is an addition; an ID in previous but not current is a removal.
  4. For each change it appends a row to the changelog Sheet — a timestamp, the word added or removed, and the file ID. If nothing changed, it logs that and writes no rows.
  5. Finally it overwrites the stored snapshot with the current list, so the next run compares against this run’s state.

Example run

The watched folder starts with three files. The first run records nothing — it just stores the baseline. Before the second run, a file is added and another is deleted. The second run appends:

DateChangeFile ID
2025-11-05 06:00:11added1xyzNewBriefId
2025-11-05 06:00:11removed1xyzOldDraftId

Over weeks the changelog Sheet becomes a complete, dated history of the folder — a record you can scroll back through whenever someone asks “when did that file go?”.

Trigger it

The script only sees changes between two runs, so it needs to run on a schedule:

  1. In the Apps Script editor open Triggers (the clock icon).
  2. Add a trigger for detectChanges, time-driven, on a daily or hourly timer.

Pick the interval to match how granular you want the history. Hourly catches near to when a change happened; daily is enough for a slow-moving folder.

Watch out for

  • The changelog stores file IDs, not names — a removed file is gone, so its name cannot be looked up after the fact. If names matter, extend listFileIds to snapshot {id, name} pairs and log the name too.
  • It only sees the gap between runs. A file added and deleted between two runs leaves no trace at all.
  • It watches one folder, not subfolders. A file moved into a subfolder reads as removed from the parent.
  • A renamed file keeps the same ID, so a rename produces no changelog entry — the script tracks presence, not metadata.
  • Clearing the PREVIOUS_FILES Script Property resets the baseline. The next run then logs every current file as added, which floods the changelog.

Related