/** * @file OpenAPI fragment for the better-conflicts and history-rewrite READS * of a workspace (server/lib/git-conflicts.js, server/lib/git-rewrite.js): one * conflicted file's three stages for the 3-way merge editor, the reflog, the * repo's remotes and the plan a rewrite dialog shows before it asks. The * writes themselves are actions of POST /api/lanes/{id}/git/{action} * (documented in server/openapi-extra/lanes.js). Exports `{ tags, schemas, * paths }` for server/openapi-extra.js. * @author Nguyễn Ngọc Trí Vĩ */ const idParam = { name: "id", in: "path", required: true, schema: { type: "integer" } }; const target = [ { name: "session", in: "query", schema: { type: "string" } }, { name: "source", in: "query", schema: { type: "integer" } }, { name: "repo", in: "query", schema: { type: "string" } }, ]; const paths = { "/api/lanes/{id}/git/conflict": { get: { tags: ["Lanes"], summary: "One conflicted file's three sides, for the merge editor", description: "Only a path `git ls-files -u` lists as unmerged right now (400 EBADPATH otherwise), with the read path's guards (lexical lane guard, 403 ESECRET, 1 MB cap). Each stage is read by its blob id: base (:1), ours / current (:2), theirs / incoming (:3); a side git does not have (added on one side, deleted on the other) is null. `working` is the file on disk with its markers and `sha` its content sha — the `baseSha` the `conflict-save` action must name. `operation` is merge, cherry-pick, revert or rebase (in a rebase, ours is the branch being rebased ONTO and theirs the commit being replayed); `incoming` is that operation's commit. Read-only, no same-origin guard.", operationId: "readLaneConflict", parameters: [ idParam, { name: "path", in: "query", required: true, schema: { type: "string" } }, ...target, ], responses: { 200: { description: "{available, path, operation, branch, incoming:{sha,short,subject}|null, base, ours, theirs, working, sha, usable, binary, tooBig}.", }, 400: { description: "EBADPATH." }, 403: { description: "ESECRET or EFOREIGN." }, 404: { description: "Lane not found." }, }, }, }, "/api/lanes/{id}/git/reflog": { get: { tags: ["Lanes"], summary: "A page of the reflog (read-only)", description: "`git log -g` of HEAD (default) or of a local branch that exists (looked up, 400 EBADBRANCH / ENOBRANCH otherwise), newest first, `limit` (default 50, max 200) entries from `skip`. A ref with no reflog is an empty list; a folder that is not a repo answers {available:false}. Read-only, no same-origin guard.", operationId: "readLaneReflog", parameters: [ idParam, { name: "ref", in: "query", schema: { type: "string", default: "HEAD" } }, { name: "skip", in: "query", schema: { type: "integer", default: 0 } }, { name: "limit", in: "query", schema: { type: "integer", default: 50 } }, ...target, ], responses: { 200: { description: "{available, ref, more, entries:[{index, selector, sha, short, action, message, time, subject}]} — action/message split git's reflog subject (`commit: fix`), time is unix seconds.", }, 400: { description: "EBADBRANCH or ENOBRANCH." }, 404: { description: "Lane not found." }, }, }, }, "/api/lanes/{id}/git/remotes": { get: { tags: ["Lanes"], summary: "The repo's remotes", description: "Each remote's fetch and push URL and its default branch (`refs/remotes//HEAD`). An http(s) URL loses its whole userinfo and any other keeps only the user name, so no password or token is ever answered. Read-only, no same-origin guard.", operationId: "listLaneRemotes", parameters: [idParam, ...target], responses: { 200: { description: "{available, remotes:[{name, url, pushUrl, defaultBranch, branches}]} — branches are the fetched remote-tracking branches (every one, a local copy or not).", }, 404: { description: "Lane not found." }, }, }, }, "/api/lanes/{id}/git/rewrite-plan": { get: { tags: ["Lanes"], summary: "What a history rewrite would do, before it asks", description: "kind=rebase&onto= — the commits replayed (oldest first, each with pushed), how far behind the branch is; kind=interactive[&base=] — the commits an interactive rebase may rewrite (default: those no remote-tracking branch holds), base and whether a merge is in the range; kind=reset&target= — related (ancestor or descendant), how many commits are dropped/added and how many dropped ones are pushed; kind=push — where a force push goes (branch, remote, remoteBranch), the lease sha (remoteSha, from the last fetch), ahead/behind, protected and allowed. Every answer carries head (the full sha a write must name, else ESTALE), branch, subdir, operation and dirty. Read-only, no same-origin guard.", operationId: "planLaneRewrite", parameters: [ idParam, { name: "kind", in: "query", required: true, schema: { type: "string", enum: ["rebase", "interactive", "reset", "push"] }, }, { name: "onto", in: "query", schema: { type: "string" } }, { name: "base", in: "query", schema: { type: "string" } }, { name: "target", in: "query", schema: { type: "string" } }, ...target, ], responses: { 200: { description: "{available, kind, head, branch, subdir, operation, dirty, …kind's facts}.", }, 400: { description: "EBADMODE, EBADBRANCH, ENOBRANCH, EBADSHA, ENOTRELATED, ENOPARENT, EDETACHED or ENOREMOTE.", }, 404: { description: "Lane not found." }, }, }, }, }; module.exports = { tags: [], schemas: {}, paths };