XLSX savior: Git for Excel

Inspiration

The idea came from my dad, he works as an accountant. We were taking about a recurring nightmare at his work. A junior accountant would open a shared spreadsheet, accidentally overwrite a formula or delete a row of data, and save the file. Or even the file get corrupt, and just like that, weeks of work down the drain with no way to know what changed or how to get it back.

What I Learned

  • The internal structure of .xlsx files. An Excel file isn't a single binary file, it's actually a ZIP archive of XML documents (worksheets, shared strings, styles, and formula definitions all live in separate XML files inside it). Understanding this structure was essential before I could track changes.
  • Version control concepts, applied outside of code. Concepts like snapshots, diffs, and commits translate surprisingly well to spreadsheets, but a cell has both a value and a formula, and either one can change independently.
  • Diffing algorithms. I learned how to compare two states of a grid of cells efficiently, rather than naively comparing every cell in every version.

How I Built It

Three main components:

1. Python versioning engine

Reads .xlsx files with openpyxl, creates snapshots, compares cell values/formulas/formats, detects row shifts, and restores original workbook bytes safely. Stores committed versions and pending changes in .excel_versions.json

2. React/Vite frontend

Main controller for workspace initialization, status polling, diff loading, commits, and rollback. Displays committed versions and pending changes.

3. Cloud synchronization

The cloud feature is separate from local versioning. Archives a workspace into a ZIP file, uploads/downloads revisions, and tracks sync state. Controls sign-in, organizations, uploading, downloading, and cloud status.

Challenges I Faced

The biggest challenge by far was formulas.

Early versions only tracked values, if a cell showed 150, it stored 150. But this completely missed the actual underlying formula

I got stuck on this for a while, but with help from Codex and GPT-5.6, I was able to work through parsing the formula strings out of the underlying XML independently from their cached values, and hash and diff them separately.

Built With

Share this project:

Updates