Skip to content

Git Like Versioning

iamvirul edited this page Apr 4, 2026 · 2 revisions

Git-Like Versioning

DeepDiff DB's version command group brings Git-style commit history, branching, and ASCII graph visualization to database diff snapshots. Every commit is content-addressable (SHA-256), stored as a zlib-compressed object in a fanout directory layout, and contains enough schema context to generate rollback SQL offline — no live database required.


Overview

deepdiffdb version <subcommand> [flags]
Subcommand Description
version init Initialise a .deepdiffdb/ repository
version commit Run a diff and store it as a new commit
version log Show commit history newest-first
version diff Compare schema evolution between two commits
version rollback Generate rollback SQL for a commit
version branch List branches or create a new one
version checkout Switch the current branch
version tree ASCII commit graph for all branches

Storage Layout

.deepdiffdb/
  HEAD                        ← symbolic ref: "ref: refs/heads/main"
  config                      ← verified GitHub identity (0o600, token never stored)
  objects/
    <2-hex>/
      <62-hex>                ← zlib-compressed commit object (Git fanout)
  refs/
    heads/
      main                    ← tip hash for main branch
      feature                 ← tip hash for feature branch (one file per branch)

This mirrors Git's internal layout exactly:

  • Fanout: the first 2 hex characters of the SHA-256 hash form the directory name, the remaining 62 form the filename. This keeps directory entry counts bounded on large histories.
  • Symbolic refs: HEAD stores ref: refs/heads/<branch>. Each branch file stores the plain hash of its tip commit.
  • Content-addressable: the hash is computed over a "commit <size>\x00<json>" header + the JSON body, matching Git's object-header convention. The hash changes whenever the content changes.
  • Identity config: .deepdiffdb/config stores {"github_user": "<username>"} at 0o600 permissions. The GitHub access token is used only to verify identity and is never written to disk.

Add .deepdiffdb/config to your .gitignore to keep your personal identity local. Commit the rest of .deepdiffdb/ to share history with your team.


version init

Creates the .deepdiffdb/ structure and optionally authenticates with GitHub to establish verified commit authorship. Idempotent — safe to re-run on an existing repo (e.g. to add GitHub identity after an initial --skip-auth run).

deepdiffdb version init [--dir <path>] [--skip-auth]
Flag Default Description
--dir . Directory to initialise
--skip-auth false Skip GitHub authentication (use --author on commits instead)

GitHub Device Flow Authentication

After initialising, version init prompts you to authenticate via the GitHub device flow — no browser redirect required:

Authenticate with GitHub to verify commit authorship? [Y/n]:

  Open:  https://github.com/login/device
  Code:  ABCD-1234

  Waiting for authorization ........ ✓
Authenticated as github:iamvirul — your commits will be signed automatically.

The verified username is stored in .deepdiffdb/config as {"github_user": "iamvirul"}. The access token is discarded immediately after identity confirmation — it is never persisted.

Use --skip-auth to bypass the prompt in CI pipelines and use --author on version commit instead.

Setup required: version init needs DEEPDIFFDB_GITHUB_CLIENT_ID to be set (or baked into the binary at build time via -ldflags). See GitHub OAuth Setup below.


version commit

Connects to both databases, runs a full schema + data diff, and saves the result as a new commit on the currently checked-out branch.

Each commit stores:

  • Author, message, UTC timestamp
  • Full SchemaDiff and DataDiff results
  • ProdSchema and DevSchema snapshots (needed for offline rollback)
  • SHA-256 content hash
deepdiffdb version commit --message "describe the change" [flags]
Flag Default Description
--config deepdiffdb.config.yaml Path to config file
--message (required) Commit message
--author (see below) Author name — ignored when GitHub identity is stored

Author Resolution

The author is resolved in this order:

  1. Verified GitHub identity — if .deepdiffdb/config contains a github_user, the author is automatically set to github:<username>. Passing --author alongside a stored identity prints a warning and the flag is ignored.
  2. --author flag — used when no config identity exists.
  3. Error — if neither is available, version commit exits asking you to authenticate or pass --author.
# With GitHub authentication (author resolved automatically)
deepdiffdb version commit \
  --config deepdiffdb.config.yaml \
  --message "V2: add reviews table"

# [e35c16c9] V2: add reviews table
#   Author: github:iamvirul
#   Schema drift detected.

# Without authentication (explicit author)
deepdiffdb version commit \
  --config deepdiffdb.config.yaml \
  --message "V2: add reviews table" \
  --author "Alice"

version log

Walks the commit chain from HEAD backwards.

deepdiffdb version log [--limit N]
Flag Default Description
--limit 20 Maximum commits to show
commit 142b1305...
Author: github:iamvirul
Date:   2026-03-31 14:33:29 +0000

    V3: add reviews table and avg_rating column [schema drift] [data changes]

commit e35c16c9...
Author: github:iamvirul
Date:   2026-03-31 14:33:22 +0000

    V2: add category_id FK and customer_email column [schema drift] [data changes]

version diff

Compares the DevSchema snapshots of two commits and prints what changed between them.

deepdiffdb version diff <hash1> <hash2>

Full hashes and 8-character short hashes are both accepted.

$ deepdiffdb version diff cad39fda 142b1305

Schema evolution from cad39fda → 142b1305

  + TABLE reviews (added)
  ~ TABLE orders
      + COLUMN customer_email varchar(255)
  ~ TABLE products
      + COLUMN avg_rating decimal(3,2)
      + COLUMN category_id int
      + INDEX fk_product_category

version rollback

Generates driver-aware rollback SQL by inverting a commit's diff (dev → prod direction). Uses the same SQL generator as schema-migrate — destructive operations (DROP TABLE, DROP COLUMN) are commented out by default.

deepdiffdb version rollback [--out <file>] [--driver <driver>] <hash>
Flag Default Description
--out (stdout) Write SQL to this file
--driver (from commit) Override dialect: mysql, postgres, sqlite, mssql, oracle
-- Generated rollback SQL
BEGIN;

-- DROP TABLE `reviews`;          ← commented for safety, uncomment to apply
-- ALTER TABLE `orders` DROP COLUMN `customer_email`;
-- ALTER TABLE `products` DROP COLUMN `avg_rating`;
-- ALTER TABLE `products` DROP COLUMN `category_id`;

COMMIT;

version branch

Lists all branches (current branch marked with *), or creates a new one.

# List branches
deepdiffdb version branch

# Create from current HEAD
deepdiffdb version branch <name>

# Create from a specific commit
deepdiffdb version branch <name> --from <hash>
Flag Default Description
--from (current HEAD) Hash to branch from
$ deepdiffdb version branch
  feature  e35c16c9
* main     142b1305

$ deepdiffdb version branch hotfix --from cad39fda
Branch "hotfix" created.

version checkout

Switches HEAD to point to the named branch as a symbolic ref. All subsequent version commit calls advance only the checked-out branch.

deepdiffdb version checkout <branch>
$ deepdiffdb version checkout feature
Switched to branch "feature".

Returns an error if the branch does not exist. Create it first with version branch <name>.


version tree

Renders an ASCII commit graph showing all branches, newest commit first.

deepdiffdb version tree
| * e2a3d002 (feature)       2026-03-31  V2: category link + customer email
* | 761e26c5 (HEAD -> main)  2026-03-31  V3: reviews + avg_rating
* | 18bf631b                 2026-03-31  V1: baseline schema
  • Each column is a branch lane: * = commit on this lane, | = lane active elsewhere
  • (HEAD -> main) = current branch and its latest commit
  • (feature) = non-current branch tip
  • Commits shared by multiple branches (e.g. the fork point) appear in the leftmost active lane

Typical Workflow

# 1. Initialise once per project — authenticate with GitHub for verified authorship
deepdiffdb version init
# → prompts for GitHub device flow (no browser redirect needed)
# → stores {"github_user":"iamvirul"} in .deepdiffdb/config; token discarded

# 2. Commit a clean baseline on main — author resolved automatically
deepdiffdb version commit \
  --config deepdiffdb.config.yaml \
  --message "V1: baseline e-commerce schema"

# 3. Create a feature branch
deepdiffdb version branch feature
deepdiffdb version checkout feature

# 4. Apply schema changes to the dev database (ALTER TABLE, CREATE TABLE…)

# 5. Commit the drift on the feature branch
deepdiffdb version commit \
  --config deepdiffdb.config.yaml \
  --message "V2: experimental schema"

# 6. Switch back to main for a hotfix
deepdiffdb version checkout main
deepdiffdb version commit \
  --config deepdiffdb.config.yaml \
  --message "V2: hotfix — drop unused index"

# 7. Visualise the divergence
deepdiffdb version tree

# 8. Compare any two commits
deepdiffdb version diff <hash_v1> <hash_v2>

# 9. Generate rollback SQL if needed
deepdiffdb version rollback --out rollback.sql <hash>

CI / Scripted Environments

Use --skip-auth to bypass the GitHub prompt in pipelines and pass --author to identify the pipeline:

deepdiffdb version init --skip-auth

deepdiffdb version commit \
  --config deepdiffdb.config.yaml \
  --message "CI snapshot: $GIT_SHA" \
  --author "ci/github-actions"

GitHub OAuth Setup

To enable verified authorship, register a GitHub OAuth App:

  1. Go to github.com/settings/applications/new
  2. Fill in:
    • Application name: DeepDiff DB
    • Homepage URL: https://github.com/iamvirul/deepdiff-db
    • Authorization callback URL: http://localhost (not used by device flow)
  3. Tick Enable Device Flow
  4. Click Register application and copy the Client ID

Environment variable (personal use):

export DEEPDIFFDB_GITHUB_CLIENT_ID="your_client_id"

Baked into released binaries (build-time injection):

- name: Build
  run: |
    go build \
      -ldflags "-X github.com/iamvirul/deepdiff-db/internal/version.GitHubClientID=${{ secrets.DEEPDIFFDB_GITHUB_CLIENT_ID }}" \
      -o deepdiffdb ./cmd/deepdiffdb

See Also

  • CLI Reference — all commands at a glance
  • Sample 17 — full end-to-end demo with MySQL Docker containers, branches, and ASCII tree output

Clone this wiki locally