Using Patchette =============== Patchette snapshots your branches into a patches repo, as one folder of patch files per branch, and rebuilds those exact branches from the patches, commit for commit, in any clone of the upstream. You keep working in your own repo, with plain git; patchette only reads it, and writes the branches back when you ask. Every session below is real: the test suite runs each one, and checks that patchette still prints exactly what's shown. They all start from the same place: a home dir with an upstream repo at `~/upstream/frob`, and your clone of it at `~/src/frob`, with `main` checked out, carrying two branches of your own work, `topic/fancy` and `docs-polish`. Setting up ---------- The patches live in a repo of their own, beside your clone. Make one, then pair your clone with it, so patchette knows where to look: [testmark]:# (fresh/setting-up/session) ``` ~$ cd src/frob ~/src/frob$ git init -b main ../frob-patches Initialized empty Git repository in ~/src/frob-patches/.git/ ~/src/frob$ patchette pair ../frob-patches paired: '~/src/frob' -> patches dir '../frob-patches' ~/src/frob$ patchette pair paired: patches dir is '../frob-patches' (relative to '~/src/frob') ``` Pairing is the one thing patchette keeps in your repo: a single value, in `.git/patchette`, never pushed. Delete that file to un-pair. The sessions in the rest of this guide each start as though you'd just done that (apart from the last two sections, which start unpaired, from `~`). Taking snapshots ---------------- Work on your branches however you like; then export them, and commit the result in the patches repo. Export takes the upstream commit your branches are based on, then the branches: [testmark]:# (snapshot/session) ``` ~/src/frob$ patchette export origin/main topic/fancy docs-polish export: wrote ~/src/frob-patches/topic¦fancy (8 patches) export: wrote ~/src/frob-patches/docs-polish (2 patches) ~/src/frob$ ls ../frob-patches/docs-polish STACK mention-the-oiling-schedule.patch polish-the-readme.patch ~/src/frob$ git -C ../frob-patches add . ~/src/frob$ git -C ../frob-patches commit -q -m "first snapshot" ``` Each branch becomes a folder: a `STACK` file saying where it's based and what's in it, and one patch file per commit. `~/src/frob-patches/docs-polish/STACK` names the branch, the upstream commit it's based on, and its tip; then, after `series`, its patches, in order: [testmark]:# (snapshot/STACK) ``` ref refs/heads/docs-polish base 5b0130cfd2153bcdb4a6d7ec356e7d805018afc9 tip 2dfecf1d7ce26028ef2dcc7430b5b9bc9c8aa86a series polish-the-readme mention-the-oiling-schedule ``` `~/src/frob-patches/docs-polish/polish-the-readme.patch` is one commit: its parents (`BASE` is the STACK's base), then the rest of its headers exactly as git stored them; its message, between `----` lines; and its diff: [testmark]:# (snapshot/polish-the-readme.patch) ``` parents BASE author Ada Downstream 1700000660 +0000 committer Ada Downstream 1700000660 +0000 ---- > Polish the readme ---- diff --git a/README.md b/README.md index a330cd687e7fdede54bbe0aca5df47c06d2e8b64..c195933372242cdbb3c014665ebcff2bb3fb4449 100644 --- a/README.md +++ b/README.md @@ -3,3 +3,5 @@ Upstream docs. More upstream docs. + +Downstream notes: be kind to the machinery. ``` Both formats are specified in full in [FORMAT.md](../hacking/FORMAT.md). The payoff comes when your patches change. Here, a commit on `docs-polish` gets reworked; export again, and the patches repo shows what changed about the change: [testmark]:# (paired/evolving/session) ``` ~/src/frob$ patchette export origin/main docs-polish export: wrote ~/src/frob-patches/docs-polish (2 patches) ~/src/frob$ git -C ../frob-patches add . ~/src/frob$ git -C ../frob-patches commit -q -m "first snapshot" ~/src/frob$ git switch -q docs-polish ~/src/frob$ sed -i 's/weekly/daily/' README.md ~/src/frob$ git commit -q -a --amend --no-edit ~/src/frob$ patchette export origin/main docs-polish export: wrote ~/src/frob-patches/docs-polish (2 patches) ~/src/frob$ git -C ../frob-patches diff diff --git a/docs-polish/STACK b/docs-polish/STACK index c56f84d..99ac5cd 100644 --- a/docs-polish/STACK +++ b/docs-polish/STACK @@ -1,6 +1,6 @@ ref refs/heads/docs-polish base 5b0130cfd2153bcdb4a6d7ec356e7d805018afc9 -tip 2dfecf1d7ce26028ef2dcc7430b5b9bc9c8aa86a +tip 88f146ae1801f5fd8d443d6eb5904f09b88927c6 series polish-the-readme mention-the-oiling-schedule diff --git a/docs-polish/mention-the-oiling-schedule.patch b/docs-polish/mention-the-oiling-schedule.patch index 8b0dda3..1f6c371 100644 --- a/docs-polish/mention-the-oiling-schedule.patch +++ b/docs-polish/mention-the-oiling-schedule.patch @@ -1,17 +1,17 @@ parents polish-the-readme author Ada Downstream 1700000720 +0000 -committer Ada Downstream 1700000720 +0000 +committer Ada Downstream 1700086400 +0000 ---- > Mention the oiling schedule ---- diff --git a/README.md b/README.md -index c195933372242cdbb3c014665ebcff2bb3fb4449..073a43988cb91f0fe493964670f6d52517082346 100644 +index c195933372242cdbb3c014665ebcff2bb3fb4449..61f9c849c8c9962d481e8ce3874abcc47a640eba 100644 --- a/README.md +++ b/README.md @@ -5,3 +5,4 @@ Upstream docs. More upstream docs. Downstream notes: be kind to the machinery. -+Oil the gears weekly. ++Oil the gears daily. ``` Commit that with a message saying why it changed, and the history of your patches tells its own story. Checking a snapshot ------------------- `verify` rebuilds each branch from its patches, off to the side, and checks it comes out exactly as it was recorded. It never touches your branches. Name branches to check just those: [testmark]:# (paired/verifying/session) ``` ~/src/frob$ patchette export origin/main topic/fancy docs-polish export: wrote ~/src/frob-patches/topic¦fancy (8 patches) export: wrote ~/src/frob-patches/docs-polish (2 patches) ~/src/frob$ patchette verify verify: ok: refs/heads/docs-polish reproduces recorded tip 2dfecf1d7ce26028ef2dcc7430b5b9bc9c8aa86a verify: ok: refs/heads/topic/fancy reproduces recorded tip cc261dc9f0b878a78e2b80aeb60de397205f61ad ~/src/frob$ patchette verify topic/fancy verify: ok: refs/heads/topic/fancy reproduces recorded tip cc261dc9f0b878a78e2b80aeb60de397205f61ad ``` Rebuilding branches in a fresh clone ------------------------------------ The patches repo is enough to bring your branches back, exactly, in a clone that never had them. Pair the new clone, and import: [testmark]:# (paired/recreating/session) ``` ~/src/frob$ patchette export origin/main topic/fancy docs-polish export: wrote ~/src/frob-patches/topic¦fancy (8 patches) export: wrote ~/src/frob-patches/docs-polish (2 patches) ~/src/frob$ cd .. ~/src$ git clone -q ~/upstream/frob frob2 ~/src$ cd frob2 ~/src/frob2$ patchette pair ../frob-patches paired: '~/src/frob2' -> patches dir '../frob-patches' ~/src/frob2$ patchette import import: created refs/heads/docs-polish at 2dfecf1d7ce26028ef2dcc7430b5b9bc9c8aa86a import: created refs/heads/topic/fancy at cc261dc9f0b878a78e2b80aeb60de397205f61ad ~/src/frob2$ git rev-parse topic/fancy cc261dc9f0b878a78e2b80aeb60de397205f61ad ~/src/frob2$ git -C ../frob rev-parse topic/fancy cc261dc9f0b878a78e2b80aeb60de397205f61ad ``` Editing patches by hand ----------------------- Patch files are plain text, and fine to edit directly. Then rebuild the branch from them. `import` won't replace a branch that already exists unless you say `-f`, and even then, not the branch you have checked out: switch away first. [testmark]:# (paired/hand-editing/session) ``` ~/src/frob$ patchette export origin/main topic/fancy export: wrote ~/src/frob-patches/topic¦fancy (8 patches) ~/src/frob$ sed -i 's/^> The hum is load-bearing.$/> The hum is load-bearing, and tuned./' ../frob-patches/topic¦fancy/teach-gizmo-to-hum.patch ~/src/frob$ patchette import topic/fancy patchette: 'refs/heads/topic/fancy' already exists in '~/src/frob'; refusing to touch it (-f updates it instead) [exit status: 1] ~/src/frob$ git switch -q topic/fancy ~/src/frob$ patchette import -f topic/fancy patchette: 'refs/heads/topic/fancy' is checked out in '~/src/frob'; refusing to move it under that checkout, even with -f [exit status: 1] ~/src/frob$ git switch -q main ~/src/frob$ patchette import -f topic/fancy import: updated refs/heads/topic/fancy to 57d05b16d9ad81fd92af19d210f0884dc9745e01 (previous tip cc261dc9f0b878a78e2b80aeb60de397205f61ad remains in its reflog) import: note: replayed tip differs from recorded tip cc261dc9f0b878a78e2b80aeb60de397205f61ad (patches edited since the snapshot?) ~/src/frob$ patchette verify topic/fancy verify: MISMATCH: refs/heads/topic/fancy replays to 57d05b16d9ad81fd92af19d210f0884dc9745e01, but records tip cc261dc9f0b878a78e2b80aeb60de397205f61ad verify: either the snapshot was unfaithful, or the patches were edited since (patches repo is ahead of its snapshot) [exit status: 1] ~/src/frob$ patchette export origin/main topic/fancy export: wrote ~/src/frob-patches/topic¦fancy (8 patches) ~/src/frob$ patchette verify topic/fancy verify: ok: refs/heads/topic/fancy reproduces recorded tip 57d05b16d9ad81fd92af19d210f0884dc9745e01 ``` `verify` fails right after the edit: the patches no longer match the snapshot they were taken as. Exporting the rebuilt branch takes a new snapshot of it, and they agree again. Without pairing --------------- Pairing is a convenience. From anywhere, `--subject` names your repo and `--patches` the patches repo; each one, given, wins over the pairing. [testmark]:# (fresh/flags/session) ``` ~$ git init -q -b main src/frob-patches ~$ patchette export --subject=src/frob --patches=src/frob-patches origin/main docs-polish export: wrote ~/src/frob-patches/docs-polish (2 patches) ~$ patchette verify --subject=src/frob --patches=src/frob-patches verify: ok: refs/heads/docs-polish reproduces recorded tip 2dfecf1d7ce26028ef2dcc7430b5b9bc9c8aa86a ``` When something's wrong ---------------------- [testmark]:# (fresh/refusals/session) ``` ~$ cd src/frob ~/src/frob$ patchette verify patchette: --subject/--patches not given, and '~/src/frob' is not paired; run 'patchette pair ' there, or pass the flags [exit status: 1] ~/src/frob$ cd /tmp /tmp$ patchette verify --subject="$HOME/src/frob" patchette: --subject/--patches not given, and the cwd is not inside a git repository [exit status: 1] /tmp$ cd ~/src/frob ~/src/frob$ mkdir ../frob-patches ~/src/frob$ patchette pair ../frob-patches paired: '~/src/frob' -> patches dir '../frob-patches' ~/src/frob$ patchette export origin/main docs-polish export: wrote ~/src/frob-patches/docs-polish (2 patches) ~/src/frob$ patchette verify docs-polish --patches=../frob-patches patchette: no branch folder for '--patches=../frob-patches' in '~/src/frob-patches' [exit status: 1] ~/src/frob$ patchette verify --patches= verify: ok: refs/heads/docs-polish reproduces recorded tip 2dfecf1d7ce26028ef2dcc7430b5b9bc9c8aa86a ~/src/frob$ patchette export patchette: usage: patchette export [--sigil=quote|--sigil=tab] [--subject=] [--patches=] ... [exit status: 1] ```