diff --git a/progress.asc b/progress.asc
new file mode 100644
index 0000000..bbbd6ba
--- /dev/null
+++ b/progress.asc
@@ -0,0 +1,133 @@
+= Pro Git, 3rd Edition — Progress
+:toc:
+
+Working task list for the third edition, derived from the chapter-by-chapter
+inventory in `REVISION_PLAN.md`. This file is edited by hand: mark a task done by
+changing `[ ]` to `[x]`, and add new tasks — or whole new sections — as they come
+up. The site's progress page renders this file directly, so keep the format:
+`== Section title` headings with `* [ ]` checklist items under them.
+
+== Project setup
+
+* [x] Import the 2nd-edition sources as the starting point
+* [x] Write the revision plan (`REVISION_PLAN.md`)
+* [x] Build the line-level `master` -> `main` inventory (`book_master_to_main_inventory.md`)
+* [x] Launch the project site — book reader, blog, history
+* [x] Set up the cloud build/test environment for agents
+
+== Policy decisions
+
+These gate the cross-cutting sweeps; decide them first.
+
+* [ ] Decide the `master` -> `main` rename policy (gates X1, ~602 renames)
+* [ ] Decide how to represent example hashes: regenerate everything at SHA-256, or keep labeled SHA-1 legacy examples (gates X2)
+
+== Cross-cutting sweeps
+
+* [ ] X1 — `master` -> `main` across the book (see the inventory for the per-line breakdown)
+* [ ] X1 — regenerate diagrams and screenshots that show a `master` branch
+* [ ] X2 — SHA-1 -> SHA-256 default-hash pass (~31 files reference hashes)
+* [ ] X3 — version framing: bump "written using Git version 2" to 3.x; reframe "since Git 2.23/2.27/2.28" notes as baseline behavior
+* [ ] X4 — cover the `safe.bareRepository` and `safe.directory` security defaults
+* [ ] X5 — forge/IDE screenshot refresh (Ch 4, Ch 6, Appendix A)
+
+== Chapter 1 — Getting Started
+
+* [ ] Update the headline SHA-1 passage in `what-is-git.asc` (X2)
+* [ ] Refresh per-platform install steps in `installing.asc`
+* [ ] Add the Rust toolchain requirement to "compile from source"
+* [ ] Rewrite the default-branch subsection of `first-time-setup.asc` for a main-default world
+* [ ] Note Git's maturation and the 3.0 transition in the history section
+
+== Chapter 2 — Git Basics
+
+* [ ] Normalize `restore`/`switch` as standard commands (drop the "new in 2.23" framing)
+* [ ] Decide whether to teach `restore`/`switch` before `checkout`/`reset`
+* [ ] Update the `pull.rebase` warning framing in `remotes.asc`
+* [ ] Regenerate example hashes per the X2 policy
+* [ ] Note the `main` and reftable defaults in `getting-a-repository.asc`
+
+== Chapter 3 — Git Branching
+
+* [ ] X1 sweep (101 renames — the heaviest concentration)
+* [ ] Regenerate the branch diagrams in `diagram-source/`
+* [ ] Revisit the branch-renaming discussion in `branch-management.asc` for a main-default world
+
+== Chapter 4 — Git on the Server
+
+* [ ] Demote Gitweb and `git daemon`; lead with modern self-hosting
+* [ ] Add Gitea/Forgejo coverage
+* [ ] Cover protocol v2; retire dumb HTTP
+* [ ] Recommend Ed25519 keys in `generating-ssh-key.asc`
+* [ ] Add a sidebar on reftable and SHA-256 hosting/interop implications
+* [ ] Refresh screenshots (X5)
+
+== Chapter 5 — Distributed Git
+
+* [ ] X1 sweep (`contributing` 48 renames, `maintaining` 39)
+* [ ] Contextualize the email-based workflow against PR-based norms
+
+== Chapter 6 — GitHub
+
+* [ ] Re-capture all screenshots (X5)
+* [ ] Text pass for the current PR review UI, org settings, and account setup flows
+* [ ] Scope decision on Actions, Codespaces, and the current review experience
+* [ ] Default-branch language in examples (26 renames in `2-contributing`)
+
+== Chapter 7 — Git Tools
+
+* [ ] Add SSH commit/tag signing to `signing.asc` (plus `gpgsm` for X.509)
+* [ ] Rework `replace.asc` — grafts are removed in 3.0
+* [ ] Add the new `git history` command to `rewriting-history.asc`
+* [ ] Lead `rewriting-history.asc` with `git filter-repo`; mark `filter-branch` deprecated
+* [ ] Update `credentials.asc` for Git Credential Manager
+* [ ] Add `git worktree` coverage
+* [ ] Add `git sparse-checkout` coverage
+* [ ] Evaluate `git range-diff`, `git replay`, `git bugreport`, `git diagnose` for coverage
+* [ ] X1 sweep (147 renames — the highest chapter total)
+
+== Chapter 8 — Customizing Git
+
+* [ ] Add `safe.bareRepository` and `safe.directory` to `config.asc` (X4)
+* [ ] Rewrite `init.defaultBranch` as default-is-main
+* [ ] Note `--object-format` (SHA-256) and `extensions.refStorage` (reftable)
+* [ ] Note removed config: `core.commentString=auto`, `core.preferSymlinkRefs=true`
+* [ ] Consider `git for-each-repo` in the scripting material
+* [ ] X1 sweep (`policy.asc` enforced-workflow example, 8 renames)
+
+== Chapter 9 — Git and Other Systems
+
+* [ ] Trim `git svn` coverage
+* [ ] Demote the Mercurial and Perforce bridges to a short "bridges exist" section
+* [ ] Verify the bridge tooling runs on modern Python
+* [ ] X1 sweep (23+ renames in the Hg sections, 37 in `git-p4`)
+
+== Chapter 10 — Git Internals
+
+* [ ] Rewrite `objects`/`packfiles` with the object-format framing and the SHA-256 interop story
+* [ ] Add reftable to `refs.asc` — the new default, and the why (case-collision, performance)
+* [ ] Fold protocol v2 into `transfer-protocols.asc`
+* [ ] Verify `maintenance.asc` against current `git maintenance`
+* [ ] Add `commit-graph` and `multi-pack-index` to the performance story
+* [ ] X1 sweep (70 renames)
+
+== Appendices
+
+* [ ] Appendix A: version/screenshot refresh for Sublime Text, Visual Studio, VS Code, JetBrains IDEs
+* [ ] Appendix A: version-check the shell completion sections
+* [ ] Appendix B: refresh binding versions (`libgit2`, `jgit`, `go-git`, `dulwich`)
+* [ ] Appendix B: promote `go-git`; add per-library SHA-256 support notes
+* [ ] Appendix C: add post-2.1 commands to the command index; note the 3.0 removals
+
+== New material
+
+* [ ] Dedicated "Git 3.0 / migrating to SHA-256" section
+* [ ] Reftable explainer (pairs with the Ch 10 refs rewrite)
+* [ ] Monorepo-scale material: sparse-checkout, partial clone, `scalar`, `git backfill`
+* [ ] `git maintenance` + commit-graph as a first-class performance story
+
+== Publication
+
+* [ ] Redo the contributors list and dedication
+* [ ] Final pass on the appendices (fastest to rot; do near publication)
+* [ ] Watch for the LTS tag (the last 2.x before 3.0) and time the release to it
diff --git a/site/src/content/blog/a-progress-page-for-the-third-edition.mdx b/site/src/content/blog/a-progress-page-for-the-third-edition.mdx
new file mode 100644
index 0000000..c67aeab
--- /dev/null
+++ b/site/src/content/blog/a-progress-page-for-the-third-edition.mdx
@@ -0,0 +1,30 @@
+---
+title: 'A progress page for the third edition'
+description: 'Every task the third edition needs, tracked in one hand-edited file and rendered as a live progress page on this site.'
+date: 2026-08-02
+---
+
+The third edition now has a **[progress page](../../blog/progress/)** — one place
+to see everything the revision needs and how much of it is done, section by
+section, with progress bars to match.
+
+Behind it is a single file:
+[`progress.asc`](https://github.com/progit/progit3/blob/main/progress.asc), a
+plain AsciiDoc checklist at the root of the repository. It distills the
+chapter-by-chapter inventory from
+[`REVISION_PLAN.md`](https://github.com/progit/progit3/blob/main/REVISION_PLAN.md)
+into concrete tasks: the policy decisions that gate everything else, the
+book-wide sweeps like `master` → `main` and SHA-256, a section for every chapter
+and appendix, and the brand-new material Git 3.0 calls for.
+
+The file is the source of truth, and it is edited by hand. Checking a task off
+means changing `[ ]` to `[x]`; adding a task means adding a line. When new work
+turns up — and it will — it gets a new line or a new section, and the page picks
+it up on the next build. Because it is standard AsciiDoc checklist syntax, the
+same file renders as tick-boxes right in the repository on GitHub.
+
+At the time of writing the counter sits at **5 of 75 tasks** — the project
+scaffolding is done, and the writing is about to begin. Watch the bars fill in
+on the [progress page](../../blog/progress/), and if you spot work that belongs
+on the list, [`progress.asc`](https://github.com/progit/progit3/blob/main/progress.asc)
+is one pull request away.
diff --git a/site/src/lib/progress.ts b/site/src/lib/progress.ts
new file mode 100644
index 0000000..426e8c4
--- /dev/null
+++ b/site/src/lib/progress.ts
@@ -0,0 +1,71 @@
+// Parse the repo-root progress.asc — a hand-edited AsciiDoc checklist — into
+// structured data for the progress page. The file's contract is simple:
+// `== Section title` headings with `* [ ]` / `* [x]` checklist items below.
+// Anything else (the doc title, paragraphs, comments) is ignored.
+
+import progressSrc from '../../../progress.asc?raw';
+
+export interface Task {
+ text: string;
+ done: boolean;
+ /** 0 for `*` items, 1 for `**`, … */
+ depth: number;
+}
+
+export interface Section {
+ title: string;
+ tasks: Task[];
+ done: number;
+ total: number;
+}
+
+export interface Progress {
+ sections: Section[];
+ done: number;
+ total: number;
+ percent: number;
+}
+
+export function parseProgress(src: string): Progress {
+ const sections: Section[] = [];
+ let current: Section | null = null;
+
+ for (const line of src.split(/\r?\n/)) {
+ const heading = line.match(/^={2,3} +(.+?)\s*$/);
+ if (heading) {
+ current = { title: heading[1], tasks: [], done: 0, total: 0 };
+ sections.push(current);
+ continue;
+ }
+
+ const task = line.match(/^(\*+) +\[([ xX*])\] +(.+?)\s*$/);
+ if (task && current) {
+ const done = task[2] !== ' ';
+ current.tasks.push({ text: task[3], done, depth: task[1].length - 1 });
+ current.total += 1;
+ if (done) current.done += 1;
+ }
+ }
+
+ const kept = sections.filter((s) => s.total > 0);
+ const done = kept.reduce((n, s) => n + s.done, 0);
+ const total = kept.reduce((n, s) => n + s.total, 0);
+ return {
+ sections: kept,
+ done,
+ total,
+ percent: total === 0 ? 0 : Math.round((done / total) * 100),
+ };
+}
+
+/** Render a task's text as HTML: escape, then apply `backtick code` and `->` arrows. */
+export function renderInline(text: string): string {
+ return text
+ .replaceAll('&', '&')
+ .replaceAll('<', '<')
+ .replaceAll('>', '>')
+ .replace(/`([^`]+)`/g, '$1')
+ .replaceAll('->', '\u2192');
+}
+
+export const progress = parseProgress(progressSrc);
diff --git a/site/src/pages/blog/index.astro b/site/src/pages/blog/index.astro
index 83d7032..3ecdec2 100644
--- a/site/src/pages/blog/index.astro
+++ b/site/src/pages/blog/index.astro
@@ -2,6 +2,7 @@
import { getCollection } from 'astro:content';
import Base from '../../layouts/Base.astro';
import { url } from '../../lib/url';
+import { progress } from '../../lib/progress';
const posts = (await getCollection('blog')).sort(
(a, b) => b.data.date.valueOf() - a.data.date.valueOf(),
@@ -15,6 +16,17 @@ const dateFmt = new Intl.DateTimeFormat('en', { dateStyle: 'long' });
Updates and progress on the third edition of Pro Git.
+ +