Squares and merging
A square is one piece of work: what one agent or one task works in. This page covers its whole life, from opening it to its patch on main.
Open a square
$ quilt square open "Rate limit sign-in" --quilt my-app --dir rate-limit -- src
sq:77aa01bc "Rate limit sign-in" · open · no commits yet · held by agent:5c1e…
Checked out into /work/rate-limit.- The square starts at the tip of its line (
main) and you are its holder: the member who commits to it. --task <id>links it to a task; its commits then carry the task id.--dirchecks it out in the same step. Paths after--make the block sparse.
See what is open, and one square in detail:
$ quilt square list --quilt my-app
sq:77aa01bc "Rate limit sign-in" · open · head 0c1d2e3f4a5b · 1 patch behind main · held by agent:5c1e…
sq:1a2b3c4d "Fix sign-in" · open · no commits yet · held by person:7f3e…
$ quilt square show sq:77aa01bc --quilt my-app--mine shows only yours, --all adds merged, abandoned and folded squares. Inside a block folder, quilt square show needs no arguments.
Commit
In the block folder, quilt commit -m "<why>" seals every changed file, or only the paths you name. Only the holder can commit (a quilt's admins can too). Each commit says which head it builds on. If the square moved since your folder last looked, the commit is refused with the square moved on since you looked, so nobody's work is overwritten. Run quilt status and try again.
Merge into main
$ quilt merge # in the block folder
$ quilt merge sq:77aa01bc --into main --quilt my-app # from anywhere
Merged as patch main:784 (b7c8d9e0f1a2).The server does the whole merge in one call:
- It checks the square holds no conflicts.
- It replays the square's changes onto the current tip of
mainas one commit. Changes to different parts of the same file are combined, and the answer says so:Combined with changes already on main: src/auth.js (lines). - It writes the patch and moves
mainwith a compare-and-swap. If another merge got there first, it starts again from the new tip by itself, up to five attempts. After three lost races the merge waits its turn for the line, one at a time.
The square is then merged, and quilt square show says which patch it became.
| Flag | What it does |
|---|---|
-m <message> | The patch's message. By default: the square's title and its commit messages. |
--dry-run | Says what would happen and changes nothing: Would merge as patch main:784. or Would be refused: would conflict in src/auth.js. |
--on-conflict rebase | The default. A refused merge leaves the square rebased onto the tip, with the conflicts in it. |
--on-conflict leave | A refused merge leaves the square exactly as it was. |
--expect-tip <commit> | Adds a warning to the answer if main has moved past that commit. |
Conflicts
A conflict never reaches main. When your square and a patch that merged after you opened it changed the same lines, the merge is refused:
$ quilt merge
Not merged: conflict in src/auth.js.
Conflicts, now kept in the square:
src/auth.js (content): agent:5c1e… vs line
Edit those files and commit them without the markers (or quilt resolve <path> --ours|--theirs), then merge again.
The folder now holds the rebased files (4 written).Your square now sits on the new tip of main, and the clashing file holds conflict markers. Only the clashing lines are marked. Each block starts with a tag that names the conflict and its kind; inside it, your side is written as it is and the other side as a diff from the base:
<<<<<<< quilt-conflict 3f2a9c1b7d4e content
+++++++ ours (agent:5c1e… · sq:77aa01bc · 0c1d2e3f)
return session.keep()
%%%%%%% theirs (line · 5f4e3d2c) (diff from base)
- return session.drop()
+ return session.renew()
>>>>>>> quilt-conflict 3f2a9c1b7d4eKinds of conflict: content (the same lines changed two ways), edit_vs_delete (one side changed a file the other deleted), mode (file modes changed two ways), order (both sides added lines at the same spot) and name (both sides added the same path, or a path clashes with a folder or with a path that differs only in case).
Settle it
Either edit the file and commit it without the markers:
$ quilt commit -m "Keep the session and renew it"
$ quilt mergeOr pick a side for each file with quilt resolve. It rewrites the square's newest commit with those files settled (the old commit stays readable) and updates the folder:
$ quilt resolve src/auth.js --theirs
$ quilt merge| Flag | Takes |
|---|---|
--ours | Your square's side. |
--theirs | The other side: the line, or the square or commit you merged in. |
--base | The file as it was before either change. |
--suggestion | A word-level merge, when the server could make one. The refused merge says when one is offered. |
--side <n> | Side number n, counting from 0. |
--delete | Deletes the file. |
- A file committed while it still holds its tag line stays conflicted. You can commit other work in the meantime.
- While any file is conflicted,
quilt mergeanswersthe square holds 1 conflicted path (src/auth.js); resolve them first. quilt cat src/auth.js@sq:77aa01bc --format gitshows the conflict with git-style markers (two sides only);--format snapshotwrites every side in full.
Rebase
$ quilt rebase
Rebased 1e2f3a4b5c6d "Rate limit the sign-in route"
square sq:77aa01bc "Rate limit sign-in" · open · head 1e2f3a4b5c6d · held by agent:5c1e…
3 file(s) updated in the folder.Moves your square onto the tip of its line, or onto any revision with --onto <rev>. Clashes become conflict markers in the square; a rebase never stops halfway. On the tip already, it answers Already on the tip.
Merge another square into yours
$ quilt merge sq:1a2b3c4d # in your block folder
Merged sq:1a2b3c4d into sq:77aa01bc: …Writes a two-parent commit in your square, as git merge does into the current branch. Conflicts stay in your square as markers. The other square stays open. Outside a folder, use quilt merge sq:<from> --into sq:<yours> --quilt <q>.
Cherry-pick
$ quilt cherry-pick patch:770Applies one commit's change to your square as a new commit. Its message ends with (cherry picked from commit …).
Revert
$ quilt revert patch:783 --quilt my-app --dir undo-783
Reverted in a new square …
Merge it with: quilt merge sq:0d9c8b7a --into main
Checked out into /work/undo-783.Opens a new square from the tip of main whose one commit undoes the patch (or any commit). Nothing changes on main until you merge that square, like any other.
Hand off, abandon, reopen
$ quilt square handoff --to person:7f3e… --note "Tests pass; the README still needs the new flag"
Handed sq:77aa01bc to person:7f3e….
$ quilt square abandon sq:1a2b3c4d
Abandoned sq:1a2b3c4d; quilt square reopen brings it back.
$ quilt square reopen sq:1a2b3c4d
Reopened sq:1a2b3c4d; you hold it.- Handoff gives the square to another member who can write to the quilt. You can no longer commit to it. The note (up to 2,000 characters) goes to both of you as a
square.handoffevent. The holder or an admin can hand off. - Abandon ends a square whose work is not wanted. Its commits stay readable by id.
- Reopen brings an abandoned square back, and whoever reopens it holds it.
Fold and split
$ quilt square fold sq:1a2b3c4d --into sq:77aa01bc
Folded sq:1a2b3c4d into sq:77aa01bc: …
$ quilt square split docs,README.md src/limits
Kept in sq:77aa01bc: src/auth.js
New sq:9f8e7d6c "Rate limit sign-in (docs, README.md)" · open · head 6a5b4c3d2e1f · held by agent:5c1e…: docs/limits.md, README.md
New sq:4c3b2a19 "Rate limit sign-in (src/limits)" · open · head 2f1e0d9c8b7a · held by agent:5c1e…: src/limits/index.js- Fold moves one square's work into another as one commit, and closes the first as
folded. You need to hold both. Changes that overlap become conflict markers in the target. - Split moves groups of paths out of your square. Each argument is one group, its paths joined by commas, and each group becomes a new square with the same base and one commit. The paths no group names stay; if the groups name every changed path, the largest group stays.
--titlenames the square made from the first group.
When a patch merges under your square
Squares are opened from main, and main keeps moving. Whenever a patch merges that changed a file your open square also changed, you get a patch.merged event. It names the patch, its author and message, the files you share, and whether your merge would now conflict.
$ quilt events
#41 patch.merged: patch 784 by person:7f3e… "Fix sign-in" changed src/auth.js in your square sq:77aa01bc; would conflict in src/auth.js
(next: after=41)
$ quilt events --after 41The same facts are in quilt square show and quilt status:
Merged under you (patches since your base that changed your files):
patch 784 by person:7f3e…: Fix sign-in (src/auth.js)
Would conflict if merged now: src/auth.js (with patch 784 by person:7f3e…). Rebase (quilt rebase) to settle it in your square first.Two more events: conflict.added when a commit writes a new conflict into your square (a rebase, a refused merge, or a merge, fold or cherry-pick), and square.handoff when a square is handed to you or by you. To be woken instead of checking, set a webhook: see events and webhooks.
Limits
- Each line takes at most 200 merges an hour, and a quilt at most 200 an hour across its lines. Above that a merge is refused with
merge_windowand the seconds to wait. A dry run is never limited. - When a check reports red on a line (
POST /lines/main/checks, from CI or a person), the line's hourly window halves, down to 5. The same check going green puts it back. See the API. - Each token has rate limits too. They are listed on the API page.