21. Contributions: reviewing changes
A contribution is a branch proposed for merging into another, as on Unison Share: reviewed as a diff of
definitions rather than text, commented on, and merged on the remote by history. Contributions
live in the codebase you push to: a tcm serve server, or another codebase directory.
21.1 Proposing a change
Make the change on a branch, then contribute it:
$ tcm branch faster && tcm switch faster $ tcm update # change lib.square, add lib.cube $ tcm contribute --title "Faster squares" --description "Uses ** now." pushed origin/faster: 3 names (new branch); sent 3 definition(s) opened contribution #1 on origin review: tcm contribution view 1 · https://tcm.example.com:7737/forge/contributions/1#token=…
tcm contribute [remote]pushes the current branch (under its own name, or--as <name>) and opens a contribution of it intomain(or--to <branch>). Without--titleyou're asked at a terminal; elsewhere the branch's name is the title.- Pushing again updates it:
tcm contribute(ortcm push) on the same branch moves the open contribution to your latest changes. - From
mainitself there's nothing to propose: make a branch first.
21.2 Contributors
A project with contributors works as on Unison Share:
nobody but its maintainer pushes main. Each contributor has a token of their own, made by the maintainer, and
pushes contributor branches, under their handle, into the project itself (no fork):
$ tcm user add grace # the maintainer, with the server's token added @grace on origin: they push branches under @grace/ and propose them their token (shown once; adding them again makes a new one): 8bS… they connect with: tcm remote add origin https://tcm.example.com --token 8bS… $ tcm remote add origin https://tcm.example.com --token 8bS… # grace $ tcm pull && tcm branch cubes && tcm switch cubes $ tcm contribute --title "Cubes" pushed origin/@grace/cubes: 1 added, 0 changed, 0 removed, 0 renamed; sent 1 definition(s) opened contribution #1 on origin
- A contributor's branches are named for them:
tcm pushandtcm contributepushcubesas@grace/cubes, and--as spareas@grace/spare(a name already under@grace/stays as it is). The server refuses them any other branch. - What they write is signed by their token: contributions, comments, closing and reopening are
@grace's, whatever$TCM_AUTHORsays. The maintainer's token signs as$TCM_AUTHOR(or your user name). - Merging is the maintainer's, as are releases and the test results and lint findings a push sends (a contributor's leave them out: the maintainer's tests run on what's merged). A contributor closes and reopens only their own contributions.
tcm usersays who you are on a remote; to the maintainer, it lists the contributors.tcm user rm gracestops grace's token working;tcm user add graceagain gives grace a new one.- On a
--publicserver anyone can pull, and read contributions and their review pages, without a token.
21.3 Reviewing
$ tcm contribution view 1
#1 Faster squares open
faster → main · opened by ada 2026-10-29 01:30 · 0 comment(s)
Uses ** now.
✓ merges as a fast-forward
changes on faster since it left main (#2jut5etj)
edited
~ lib.square #tvtxd4fk → #kndlow5j
- export const square = (x: number): number => x * x
+ export const square = (x: number): number => x ** 2
updated because a dependency changed (code unchanged)
~ lib.sumSquares #675kvzb7 → #d72molby via lib.square
added
+ lib.cube #dzuntsd3 : (x: number) => number
tcm contributionlists the remote's open contributions (--allfor merged and closed ones too).tcm contribution view <id>shows the changes from where the branch left its target, the waytcm diffdoes: edits, what was re-hashed because of them (and why), what's added, removed and renamed. Renames and reformatting aren't changes. It also says whether it merges: as a fast-forward, cleanly (naming what both sides edited apart, which the merge merges as text), or with conflicts in which names. Merging one with definitions merged as text tries the merge first on a branch of its own, and refuses it if they don't typecheck merged.tcm contribution comment <id> <text> [--about <name>]comments on the whole change, or on one definition.- Comments and merges are signed with
$TCM_AUTHOR, or your user name; a contributor's, with their handle (above).
21.3.1 The review page
tcm serve's web UI has a page for each contribution, at the link tcm contribute prints (which signs
the browser in with the token in its fragment, never sent to a server): its conversation, and its changes read
along a review path (what's new before what uses it, then tests; what only re-hashed, folded), each definition's diff
with its comments, whether it merges, and what the viewer may do: comment, close or reopen their own, merge (the
maintainer). /forge/contributions lists them all (a site served beside it has every other
path). With no token, on a public server, the pages only read.
21.4 Merging
tcm contribution merge <id> (or the page's button) merges it on the remote, by history:
- a fast-forward if the target hasn't moved since the branch left it;
- otherwise a three-way merge from their common ancestor, recorded with both as parents;
- refused if what it brings doesn't typecheck: the code its branch reaches that the target didn't, typechecked on the remote, under each definition's recorded check profile, with the errors said. A server checks that what it receives is what it claims, not its types (sync): this is where a contribution's types are judged;
- refused if the two changed the same names differently. Bring the target into the branch, settle the conflicts, and push again:
$ tcm contribution merge 3 #3 conflicts with main in lib.square: merge main into b (`tcm pull <remote> main` on that branch), resolve, push, and merge again $ tcm switch b && tcm pull origin main # merges origin/main into b $ tcm resolve lib.square '#…' $ tcm contribute # updated contribution #3 $ tcm contribution merge 3 merged #3 on origin as #mio7i2g5
Afterwards tcm pull on your main fast-forwards to the merge, and the contribution shows what the merge
brought. tcm contribution close <id> closes one without merging; reopen opens it again.