Back to the front page

The branch model, in full

Soda is the syrup. A consumer is soda plus a base.

That sentence is lifted straight out of wine/docs/BRANCHING.md, and it is not a joke about drinks. It is the build. The soda- tiers carry no Wine of their own — they are flavour, hand-written, a short linear run of feature commits each. Pour them onto a Wine tree and you get a branch you can ship. Pour them onto a different Wine tree and you get a different one.

There are two glasses. One of them is the point of the project; the other one is how we prove the first one is safe. Most of this page is about telling them apart.

Two kinds of branch

One kind you write. One kind you throw away.

Run git branch -a on the Wine fork and it looks like a mess: soda-build, soda-core, soda-glorious, soda-ntdll, soda-fsync, soda-branding, soda-demo, spritzvalve, spritzwine, plus upstream’s master and Valve’s proton_*. It isn’t a mess. There are exactly two kinds of branch here, and once you know which is which, everything else follows.

Authored — the soda-* tiers. Hand-written feature tiers. The source of truth. Each is a linear chain of concise feature commits on its parent — one commit per feature, never a merge. You edit these. Always.
Generated — the consumers. spritzvalve and its siblings are produced mechanically by replaying the tiers onto a Wine base. Never hand-edited, force-pushed on every regeneration. A commit you make here is gone at the next pour.
The metaphor pays rent. The tiers carry no Wine at all. Which Wine you get is decided entirely by the base you pour them onto — and that is why there can be more than one glass without maintaining two forks.

Same deal as Valve’s own proton_11.0: a rebased result, not a place you commit to.

The syrup

Seven tiers, and a rule for adding an eighth.

A tier is a linear run of feature commits on its parent tier — one concise commit per feature, no merges. (It used to be exactly one squashed commit; that rule was retired once it started costing per-feature history and buying nothing — the replay does not care about commit count.) git log soda-core against the fork base prints three lines today, because soda-core sits on soda-build and has grown a feature since it was carved out.

The tier set (tagged soda-11.12-3, authored on wine 11.12)
Tier Parent Contents
soda-build fork base Rust PE-staticlib build integration: configure.ac, the generated configure, tools/makedep.c, tools/winegcc/winegcc.c. Everything else depends on it.
soda-core soda-build The HidP_* value engine, the comdlg32 XDG-portal file chooser, avrt MMCSS, the win32u DC-cache bound, hidparse, the wineusb container ID. Lands clean on the Valve base.
soda-glorious soda-core DualSense/controller Rust: winebus.sys rumble, dinput, mmdevapi endpoint pairing, winexinput.
soda-ntdll soda-core ntdll Rust: the threadpool, RtlVirtualUnwind2, SEH hardening, battery/LZNT1, plus the wow64 power thunk.
soda-fsync soda-core Valve’s futex in-process sync backend, vendored — for upstream bases only, since a Valve base already carries it. What keeps the upstream glass from losing Valve-grade sync.
soda-branding soda-core winecfg strings and icons. Per-consumer — a differently-branded consumer swaps it out.
soda-demo soda-build The Rust ping demo and the integration-test harness. Nobody ships this.

Why a tier exists at all

A tier is justified by exactly one of two things. A distinct consumer set — soda-branding is separate because another consumer would swap it; soda-demo is separate because no shipping consumer wants it. Or isolating conflict-heavy code — soda-ntdll is separate because ntdll is where a cross-base replay hurts most, and you do not want that landmine sitting under a change to comdlg32.

If a change needs neither, it lands as a new feature commit on the tier that owns the code it touches. More tiers is not more structure; it is more rebase surface.

The pour itself

One script, two glasses.

A consumer is not merged, cherry-picked or maintained. It is replayed: tools/regenerate.sh <out-branch> <wine-base> <soda-tip> checks the output branch out at the soda tip and rebases that branch --onto the Wine base, with git rerere supplying the conflict memory. The tiers themselves never move. Ever.

The figure is wider than a phone — scroll it sideways. Everything in it is written out in the cards below, too.

Authored · you edit here soda-build soda-core soda-glorious soda-ntdll soda-branding soda-fsync soda-demo Each tier: a linear run of feature commits on its parent. Dashed = in no published consumer today. regenerate.sh rebase --onto git rerere 17 recorded resolutions The endeavour · poured soda-branding soda-fsync soda-ntdll soda-glorious soda-core soda-build upstream WineHQ wine · Release 11.13 no Valve base, no Proton, no Steam-shaped parts — what Wine is becoming = spritzwine-11.13-3 · the full mix Poured, tagged and pinned by the superproject. The hand-authored fork that used to carry the name is archived as spritzwine-archive. The necessity · what Prater pours soda-branding soda-core soda-build valve-wine/proton_11.0 Valve’s patchset — WINE_SIMULATE_WRITECOPY and the rest. The reason anti-tamper titles boot at all. = spritzvalve-11.0-3 · the thin mix, on purpose Ships today. The pin the Prater distribution is built from.
The same syrup, two bases — and both glasses full now. The lower one is the one that boots games; the upper one is the one the project is actually for. Nothing in the tiers had to change to get both — that is the entire argument for the machinery.
0 merges in a tier — a linear run of concise feature commits, replayed as one range
17 conflict resolutions in the tracked git rerere cache: a cross-base stitch is resolved once by a human, then identically by everyone and by CI
0 lines of Wine in the soda tiers. They are flavour; the base is the drink

Two traps are baked into that rebase, and BRANCHING.md spells both out. The exclusive start of the replayed range is the soda home base — the parent of the fork’s first commit — not a merge-base, which would sit down around wine 11.0 and replay the entire 11.0→11.12 upstream range onto Valve’s tree. And you rebase the output branch, never the tier: if the tiers move, every other consumer’s replay silently changes and the rerere cache stops matching.

The heart of it

One glass is the endeavour. The other is the means.

It is easy to read the repository and get this backwards, because the branch that ships is spritzvalve and the branch that ships is the one you can download. But shipping is not the same as being the point. spritzvalve is the vehicle that proves the Rust doesn’t break real games. SpritzWine is the thing being proven.

The endeavour

spritzwine

The tiers poured over upstream WineHQ Wine. No Valve base, no Proton, no Steam-shaped parts — Rust in Wine for its own sake, tracking what Wine is becoming rather than what Valve froze.

upstream wine 11.13 + build + core + glorious + ntdll + fsync + branding

This is where the fuller mix lives, and where fixing plain software counts as much as fixing games: the battery and power reporting in soda-ntdll exists because Windows programs ask how much charge is left and Wine had no good answer. No frametime was harmed or improved. Its proving ground is likewise plain software: as a Bottles runner it runs everyday Windows programs without fuss.

Poured  Tagged spritzwine-11.13-3 — all six shipping tiers, including soda-fsync’s vendored Valve sync, replayed onto upstream Release 11.13 — and pinned by the superproject’s wine gitlink. The hand-authored pre-Valve fork that used to carry the name is archived as spritzwine-archive.

The necessity

spritzvalve

The same tiers poured over Valve’s Proton Wine (proton_11.0). It exists because the project’s real gate is not a test suite — it is does the game boot? — and games need Valve’s patchset: WINE_SIMULATE_WRITECOPY for anti-tamper, and the rest of it.

proton_11.0 + build + core + branding

It carries the thin mix on purpose. Avatar: Frontiers of Pandora crashed at ~13 seconds on the vanilla-Wine fork; on the Valve base it plays. Rebasing onto proton_11.0 inherits Valve’s entire patchset for free — chasing individual Proton hacks one at a time was the alternative, and it is a losing game.

Ships today  Tagged spritzvalve-11.0-3 (8c00a2018, wine-11.0-1193 — the -11.0-1 pour, re-tagged under the family release scheme). Boots Avatar: Frontiers of Pandora, Risk of Rain 2 and Watch Dogs: Legion. It is what SpritzWine-Prater is built from.

Two prepositions, one field: -11.0- on a consumer means “replayed onto that base”; -11.12- on the tier set means “authored on that base.” The asymmetry is not a typo. It is the whole model, made visible in a tag.

In the shipping pins

Four DLLs in one glass. Six in the other.

Not what is written — what is in the Wine you get. In spritzvalve-11.0-3, the glass Prater pours from, exactly four Wine DLLs link Rust: avrt (MMCSS), comdlg32 (the XDG-portal file chooser), hid (the HidP_* value engine) and hidparse.sys (the HID report-descriptor parser). In spritzwine-11.13-3, the upstream glass, ntdll (threadpool, unwinder, LZNT1, battery) and winebus.sys (DualSense rumble) join them — six in all, because the fuller tiers are poured there.

# the question, answered by the tree rather than by a web page:
git grep -l lwine_ spritzvalve-11.0-3 -- 'dlls/*/Makefile.in'

dlls/avrt/Makefile.in
dlls/comdlg32/Makefile.in
dlls/hid/Makefile.in
dlls/hidparse.sys/Makefile.in

git grep -l lwine_ spritzwine-11.13-3 -- 'dlls/*/Makefile.in'

# … the same four, plus:
dlls/ntdll/Makefile.in
dlls/winebus.sys/Makefile.in

Two of the four are literally “parse a byte blob a USB device handed you, and index into it.” That is not a coincidence, and it is not where you land if the goal is Rust everywhere — it is where you land if the goal is fewer bugs. What replacing those two parsers costs, in lines, against the Valve base:

Deleting C, against valve-wine/proton_11.0
File Delta What moved to Rust
dlls/hid/hidp.c −980 / +127 the HidP_* value engine — extracting fields out of report buffers
dlls/hidparse.sys/main.c −529 / +11 the HID report-descriptor parser — a driver, kernel-pool backed

The C that remains is the bridge. The Rust lives outside the Wine tree entirely, under rust/ in the superproject — the Wine fork carries only build glue and the C-side seams. That split is what makes a tier pourable in the first place.

The stitch

Where the base and a tier disagree, the base wins.

The tiers were authored on wine 11.12. Valve ships on wine 11.0, and there is no newer base inside the Proton 11 cycle — Valve is not going to hand you one. So the shipping consumer is 11.12-authored tiers replayed backwards onto an 11.0 tree. That is not an accident waiting to be cleaned up. It is the steady state.

The asymmetry is real

This is where the work actually happens.

Where upstream Wine moved on between 11.0 and 11.12, the tier’s version of a file is stale, and the correct resolution is Valve’s. Valve’s dlls/ntdll/Makefile.in still imports $(TOMCRYPT_PE_LIBS) and links $(UNWIND_LIBS), where upstream had already switched to symcrypt and dropped libunwind. Valve’s virtual_unwind() takes an extra argument that does not exist in 11.12 at all.

Each of those is a decision made during the replay, in favour of the base. The replay exists precisely so that asymmetry never has to be “fixed.” A one-off rebase would have to be redone, by hand, forever. A script with a tracked rr-cache is resolved once.

Why a stable base, not a moving one

Oxidation on a base that already works means that when a game breaks, the answer is “our Rust broke it.” Oxidise on a moving base and every regression comes with a free debate about whether Wine moved under you — and you spend the project bisecting somebody else’s tree.

And why the fork is built to peel off

The Rust work is kept as clean, replayable tiers precisely so it can be lifted onto a different Wine base. That is not a promise about the future — it is what the current replay already does, and a rehearsal for whatever base comes next. The base and the changes never grow into each other.

Scrupulously, today

What ships, what doesn’t, and what is only a plan.

This project has publicly retracted a bug it had misdiagnosed. The same standard applies to its own architecture: a page that describes the model as if it were finished would be lying. Here is the state of the pour, as of this writing.

Ships today
spritzvalve-11.0-3 — commit 8c00a2018, wine-11.0-1193, the -11.0-1 pour re-tagged under the family release scheme. Generated from soda-build + soda-core + soda-branding onto valve-wine/proton_11.0. It is the pin the Prater distribution is built from. Boots Avatar: Frontiers of Pandora, Risk of Rain 2, Watch Dogs: Legion.
Poured and pinned
spritzwine-11.13-3 — the generated upstream consumer, all six shipping tiers on upstream Release 11.13, tagged and pinned by the superproject’s wine gitlink. soda-ntdll and soda-glorious are in a published consumer for the first time, and soda-fsync vendors Valve’s futex in-process sync so the upstream base doesn’t give it up. The hand-authored pre-Valve fork that used to carry the name is archived as spritzwine-archive.
Poured, not pinned
The regenerated spritzwine branch tip has moved past the tag — it picked up soda-core’s SHBrowseForFolder-to-portal routing. Routine regeneration does not earn a tag; that lands in the next pinned release. The boot gate, note, does not judge this glass: spritzwine is not for games — its proof is ordinary Windows software, and it already runs everyday programs cleanly under Bottles. The games’ gate belongs to the Valve base, which is why Prater’s pin stays the thin mix.
Not negotiable
Tag every consumer you pin, and pin the tag. Consumers are force-pushed; a gitlink pinning a raw consumer SHA breaks the moment the branch is regenerated — the old commit falls out of the branch’s history and git submodule update dies with Fetched in submodule path 'wine', but it did not contain <sha>. A tag is a ref, and a ref keeps the commit reachable forever.

Downstream

Where Prater fits.

Prater — the Wiener Prater, the great public park and funfair, the place you actually go to play — is the distribution you install: the Proton fork, where the games are. It is not a fourth kind of branch. It is a consumer of a consumer: Prater pins the superproject at a commit whose wine is the spritzvalve-11.0-3 tag, and everything on this page happens upstream of it.

Which is why spritzvalve exists at all. Prater is where the boot gate is held — Avatar, Risk of Rain 2, Watch Dogs: Legion — and a gate you cannot reach is a gate you cannot pass. The Valve base is how the Rust gets to stand in front of a real game and be judged.

Wine is not Proton: Proton is the whole compatibility tool — Wine plus DXVK, vkd3d, the Steam bridge, the launch script. What we take from Valve is the Wine underneath it. Our answer to Proton is Prater.

The pin chain, top to bottom

  • soda-* authored tiers, on a wine-11.12 fork base. Tagged soda-11.12-3.
  • regenerate.sh replays them --onto a Wine base, with rerere.
  • spritzwine the upstream consumer. Tagged spritzwine-11.13-3 — the superproject’s current wine pin.
  • spritzvalve the Valve-base consumer. Tagged spritzvalve-11.0-3.
  • SpritzWine the superproject — pins a consumer + the Rust workspace.
  • Prater the Proton distribution. Pins the superproject at a spritzvalve-pinned commit. This is the download.

And that is the pour

Soda is the syrup; a consumer is soda plus a base. One glass holds still so the games can prove the Rust safe. The other one is what the Rust is actually for. Nobody has to choose between them, because the tiers were never wine to begin with.