land#
Merges head (with its first-parent chain since base) three-way onto target, main by default, and publishes the result as one changeset in one transaction; on E_CONFLICT, details.report lists each conflict: resolve them with resolutions, against the report's theirs, its full 64-hex id (a path the target changed since comes back in the report's stale). Give idempotency_key to make a retry safe.
- Since
0.1.0- Mutates
- yes
- Identity required
- yes
- Envelope op
land- Cli command
swarmops land- Request type
LandReq- Annotations
{"destructiveHint":false,"idempotentHint":true,"openWorldHint":false,"readOnlyHint":false}- Max result chars
null- CLI command
swarmops land
Input schema#
arguments is a LandReq: swarmops schema LandReq prints the type. A field the type does not know is ignored.
| Field | Type | Required | Default | Description |
|---|---|---|---|---|
base | string or null | no | Where the change starts: a first-parent ancestor of head, whose own changes are not part of it (anything else is E_INVALID_ARG). It need not be on the target. When omitted, the merge base of head and the target's head, along first parents. | |
head | string | yes | The changeset to land: the change's head. A revision as the caller wrote it, for the store to resolve: a ref name (main, release/1.2), a full 64-hex changeset id or a unique prefix of at least 12 hex digits, optionally followed by ~N for the N-th first-parent ancestor. | |
idempotency_key | string or null | no | Makes a retry of this land safe: 1 to 64 letters, digits and -, such as a uuid v7. A land with a key first looks down the target's first parents, to base, for a changeset that landed with the same key: one squashed from this head is the replay, which writes nothing (status is replayed); one from another head is E_INVALID_ARG, a key used for another change. | |
max_attempts | integer | no | 8 | How many times to try to publish before failing with E_CAS_RETRY_EXHAUSTED: 1 to 32. |
max_conflict_bytes | integer | no | 32768 | The most bytes of hunk text an E_CONFLICT report carries: past it, the texts are left out (truncated), and the line positions and blob ids stay. |
message | string or null | no | The landed changeset's message; head's when omitted. | |
resolutions | object or null | no | What to make of the paths an E_CONFLICT report named, decided without a checkout. Each resolution applies to its path while the target holds there what against held, whether the path conflicts or not; a path the target changed since is stale, and the land fails E_CONFLICT again, with a fresh report that names it in stale. Conflicts left unresolved fail it too. swarmops mcp refuses an against that is not a full 64-hex id (E_INVALID_ARG). | |
target | string | no | "main" | The ref to land on: main or release/<name>, which must exist. |
JSON Schema#
As tools/list gives it: each $ref replaced by its definition, so that it stands alone.
Its descriptions' citations of the design documents are left out here.
{
"$schema": "https://json-schema.org/draft/2020-12/schema",
"description": "`land`: merge a change three-way onto a target ref and publish it there as one new\nchangeset, in one transaction. The change is `head` with its\nfirst-parent chain after `base`, squashed: the landed changeset's parent is the target's\nhead, so the target stays linear. A conflict is the error `E_CONFLICT`, with the report in\n`details.report`; nothing is recorded then.",
"properties": {
"base": {
"anyOf": [
{
"description": "A revision as the caller wrote it, for the store to resolve: a ref name (`main`,\n`release/1.2`), a full 64-hex changeset id or a unique prefix of at least 12 hex digits,\noptionally followed by `~N` for the N-th first-parent ancestor.",
"type": "string"
},
{
"type": "null"
}
],
"description": "Where the change starts: a first-parent ancestor of `head`, whose own changes are not part\nof it (anything else is `E_INVALID_ARG`). It need not be on the target. When omitted, the\nmerge base of `head` and the target's head, along first parents."
},
"head": {
"description": "The changeset to land: the change's head. A revision as the caller wrote it, for the store to resolve: a ref name (`main`,\n`release/1.2`), a full 64-hex changeset id or a unique prefix of at least 12 hex digits,\noptionally followed by `~N` for the N-th first-parent ancestor.",
"type": "string"
},
"idempotency_key": {
"description": "Makes a retry of this land safe: 1 to 64 letters, digits and `-`, such as a uuid v7. A land\nwith a key first looks down the target's first parents, to `base`, for a changeset that\nlanded with the same key: one squashed from this `head` is the replay, which writes\nnothing (`status` is `replayed`); one from another head is `E_INVALID_ARG`, a key used\nfor another change.",
"type": [
"string",
"null"
]
},
"max_attempts": {
"default": 8,
"description": "How many times to try to publish before failing with `E_CAS_RETRY_EXHAUSTED`: 1 to 32.",
"format": "uint32",
"maximum": 32,
"minimum": 1,
"type": "integer"
},
"max_conflict_bytes": {
"default": 32768,
"description": "The most bytes of hunk text an `E_CONFLICT` report carries: past it, the texts are left\nout (`truncated`), and the line positions and blob ids stay.",
"format": "uint64",
"minimum": 0,
"type": "integer"
},
"message": {
"description": "The landed changeset's message; `head`'s when omitted.",
"type": [
"string",
"null"
]
},
"resolutions": {
"anyOf": [
{
"description": "A land's resolutions (`land --resolve`): what to make of some paths of the merge of the change\nonto the target, decided against one of the target's heads.",
"properties": {
"against": {
"description": "The target's head the resolutions were decided against: the conflict report's `theirs`,\nby its full 64-hex id. A resolution applies while the target holds its path as this\nchangeset does (the same file, the same directory, or nothing). A revision as the caller wrote it, for the store to resolve: a ref name (`main`,\n`release/1.2`), a full 64-hex changeset id or a unique prefix of at least 12 hex digits,\noptionally followed by `~N` for the N-th first-parent ancestor.",
"type": "string"
},
"files": {
"additionalProperties": {
"description": "What a land makes of one path, and of every path below it: exactly one of `take`, `content`\nand `delete` (anything else is `E_INVALID_ARG`, a rule the server checks), the rest of the\nmerge as it merged.",
"properties": {
"content": {
"anyOf": [
{
"description": "A `put`'s content, as an object with one key.",
"oneOf": [
{
"additionalProperties": false,
"description": "UTF-8 text, verbatim.",
"properties": {
"inline": {
"type": "string"
}
},
"required": [
"inline"
],
"type": "object"
},
{
"additionalProperties": false,
"description": "Bytes in standard, padded base64.",
"properties": {
"base64": {
"type": "string"
}
},
"required": [
"base64"
],
"type": "object"
},
{
"additionalProperties": false,
"description": "A blob the store holds already, by its id (64 hex digits), as `blob_put` returns it: the\ncommit neither sends nor reads its bytes. An id the store holds no blob under (none, or\na tree's or a changeset's) is `E_INVALID_ARG`, and nothing is written. `blob_put` takes\nthe other forms only.",
"properties": {
"blob": {
"type": "string"
}
},
"required": [
"blob"
],
"type": "object"
},
{
"additionalProperties": false,
"description": "A file on the client's machine. The client reads it and sends its bytes, so the store is\nnever asked to read a path.",
"properties": {
"file": {
"type": "string"
}
},
"required": [
"file"
],
"type": "object"
}
]
},
{
"type": "null"
}
],
"description": "One file of new content, given as a commit's change gives it (inline, base64, a client\nfile, or a stored blob by id), within a commit's limits; nothing below it."
},
"delete": {
"description": "Nothing there: no file at the path, nor below it.",
"type": "boolean"
},
"mode": {
"anyOf": [
{
"description": "The kind of entry a `put` writes.",
"oneOf": [
{
"const": "file",
"description": "A regular file (git mode 100644).",
"type": "string"
},
{
"const": "exec",
"description": "An executable file (git mode 100755).",
"type": "string"
},
{
"const": "symlink",
"description": "A symlink; the content is the link's target.",
"type": "string"
}
]
},
{
"type": "null"
}
],
"description": "The kind of the file `content` makes, only beside it; when omitted, the kind of the file\nours holds there, else theirs', else a regular file."
},
"take": {
"anyOf": [
{
"description": "One side of a land's merge.",
"oneOf": [
{
"const": "ours",
"description": "The change: its head.",
"type": "string"
},
{
"const": "theirs",
"description": "The target: its head when the land merges.",
"type": "string"
}
]
},
{
"type": "null"
}
],
"description": "What one side holds there: `ours`, the change's head, or `theirs`, the target's head."
}
},
"type": "object"
},
"description": "What to make of each path, by its repository path.",
"type": "object"
}
},
"required": [
"against",
"files"
],
"type": "object"
},
{
"type": "null"
}
],
"description": "What to make of the paths an `E_CONFLICT` report named, decided without a checkout. Each resolution applies to its path while the\ntarget holds there what `against` held, whether the path conflicts or not; a path the\ntarget changed since is stale, and the land fails `E_CONFLICT` again, with a fresh report\nthat names it in `stale`. Conflicts left unresolved fail it too. `swarmops mcp` refuses\nan `against` that is not a full 64-hex id (`E_INVALID_ARG`)."
},
"target": {
"default": "main",
"description": "The ref to land on: `main` or `release/<name>`, which must exist.",
"type": "string"
}
},
"required": [
"head"
],
"title": "LandReq",
"type": "object"
}Annotations#
It adds a changeset onto a ref's head and moves the ref to it, keeping all the ref held. Run again with the same arguments, it finds its change on the ref and adds nothing: replayed with an idempotency_key, E_NOTHING_TO_LAND without.
| Annotation | Value | Meaning |
|---|---|---|
destructiveHint | false | True: a tool that changes the store may overwrite or delete what is there, not only add to it. |
idempotentHint | true | True: calling it again with the same arguments changes nothing more. |
openWorldHint | false | True: it reaches beyond a closed set of things. Every tool here stays inside the store. |
readOnlyHint | false | True: the tool does not change its environment, the store. |
Its entry in tools/list carries no _meta.
CLI command#
swarmops land runs the same operation and prints the same envelope.
Examples#
{
"id": 12,
"jsonrpc": "2.0",
"method": "tools/call",
"params": {
"arguments": {
"head": "[head]",
"idempotency_key": "land-7"
},
"name": "land"
}
}{
"id": 12,
"jsonrpc": "2.0",
"result": {
"content": [
{
"text": "{\"ok\":true,\"op\":\"land\",\"api\":1,\"result\":{\"status\":\"landed\",\"landed\":\"[landed]\",\"rebased_onto\":\"[theirs]\",\"target\":\"main\",\"base\":\"[c1]\",\"attempts\":1,\"relocated\":false,\"files_changed\":1,\"squashed_from\":[\"[head]\"]},\"meta\":{\"repo\":\"[tmp]/store\",\"elapsed_ms\":\"[elapsed_ms]\",\"resolved\":{\"[head]\":\"[head]\",\"main\":\"[theirs]\"},\"auth\":\"trust\"}}",
"type": "text"
}
],
"isError": false,
"structuredContent": {
"api": 1,
"meta": {
"auth": "trust",
"elapsed_ms": "[elapsed_ms]",
"repo": "[tmp]/store",
"resolved": {
"[head]": "[head]",
"main": "[theirs]"
}
},
"ok": true,
"op": "land",
"result": {
"attempts": 1,
"base": "[c1]",
"files_changed": 1,
"landed": "[landed]",
"rebased_onto": "[theirs]",
"relocated": false,
"squashed_from": [
"[head]"
],
"status": "landed",
"target": "main"
}
}
}
}The server ran as swarmops mcp --repo [tmp]/store --as agent/worker-17, after initialize. Recorded by the MCP tests. A name in square brackets, such as [tmp] or [c1], stands for what differs from run to run: the test's temporary directory, an id, a time, the version.
Details#
It merges head, with its first-parent chain since base, three-way onto target (main by
default) and publishes the result as one changeset in one transaction.
On E_CONFLICT, details.report lists each conflict.
Resolve them in resolutions, decided against the report's theirs, its full 64-hex id: the
server refuses a ref name, a relative revision or a prefix there (E_INVALID_ARG).
A path the target changed since comes back in the report's stale.
A resolution's {"file": path} content follows the rules of commit.
Give an idempotency_key to make a retry safe; run again with the same arguments, a land adds
nothing.
Return information#
A tool result: the envelope, whose op is land, as structuredContent and again as the text of its one content block. isError is true exactly when the envelope's ok is false. On success the envelope's result is what swarmops land returns.
Errors#
A failed call is a tool result with isError: true; the envelope's error.code is one of the error codes.
- E_INVALID_ARG: arguments that do not fit
LandReq, a field named twice among them included, or that break one of the server's own rules. - E_IDENTITY_REQUIRED: the server runs with no identity.
- E_NOT_A_REPO: the server's store is not there; the server serves on.
- E_INTERNAL: the operation panicked, a bug.
Then the errors of the operation, as swarmops land lists them.
See also#
diff
The files that differ between two revisions, in path order: with format names (the default) each one's path and status, with stat also its line counts, with…
log
History along first parents, newest first, from main unless rev says otherwise; pass a limit of 100 or less and page on with the cursor, since clients limit…