Git
Version control, one repository at a time
On this page(45 sections)
- Uncommitted work across every repo
- Every git repo in the workspace
- Repos matched to their remotes
- Commit history for one repo
- What one commit changed
- One file's before and after at a commit
- Whether a merge or rebase is halted mid-flight
- Abandon a halted merge or rebase
- What undoing the last action would do
- Move the branch back one step
- Everything set aside in the stash
- What one stash entry holds
- Set the current changes aside
- Bring a stash entry back
- Discard a stash entry
- Start a branch at a commit
- Tag a commit
- Remove a tag
- Send a tag to the remote
- Switch to a branch or commit
- Replay one commit onto this branch
- Undo a commit with a new commit
- Remove a commit from history
- Merge another branch in
- Replay this branch onto another
- Move the branch to a commit
- One file's committed and working copies
- One repo's branch and pending changes
- Commit the pending changes
- Throw away pending changes
- Mark changes for the next commit
- Take changes back out of the next commit
- Local branches and how far each has drifted
- Create a branch from a starting point
- Delete a local branch
- Sync position against the remote
- Refresh what the remote holds
- Bring remote commits down
- Start sending commits to the remote
- How the push is going
- Stop the push
- Every tracked path in the repo
- Read a file from the repo
- Write a file into the repo
- Write, commit and push one file
The largest group here, and the shape is consistent: the repo rides in the address and the verb is the route. Reading is history, differences and status; writing is staging, committing, branching, tagging, stashing, cherry-picking, reverting and pushing. Two routes cover a repo caught mid-merge or mid-rebase, and one answers what undoing would actually do before you undo it.
45 calls. Pick one to open it, or use the list on the right.
GET/git/changesUncommitted work across every repo
The workspace's whole review set in one answer: every repo that has something uncommitted, and within it every changed file with its status and line counts. This is what the Changes panel draws, and it is the call to make when you want to know whether a workspace is clean without walking the repos yourself.
What you send
Nothing. Call it as it is.
What comes back
| Field | Type |
|---|---|
reposOne entry per repository that has… | object[] |
repo | string |
branchThe checked-out branch | string |
conflictedPaths a merge or rebase could… | object[] |
pathThe path, relative to the repository… | string |
statusWhat happened to it | "added" | "modified" | "deleted" | "renamed" … (6) |
fromWhere a renamed file came from | string |
additionsLines added | number |
deletionsLines removed | number |
codeThe same +/− with every comment… | object |
additions | number |
deletions | number |
operationWhat halted, when something did | "merge" | "rebase" | "cherry-pick" | "revert" |
stagedWhat a plain commit would record… | object[] |
pathThe path, relative to the repository… | string |
statusWhat happened to it | "added" | "modified" | "deleted" | "renamed" … (6) |
fromWhere a renamed file came from | string |
additionsLines added | number |
deletionsLines removed | number |
codeThe same +/− with every comment… | object |
additions | number |
deletions | number |
unstagedEdits on disk that are not… | object[] |
pathThe path, relative to the repository… | string |
statusWhat happened to it | "added" | "modified" | "deleted" | "renamed" … (6) |
fromWhere a renamed file came from | string |
additionsLines added | number |
deletionsLines removed | number |
codeThe same +/− with every comment… | object |
additions | number |
deletions | number |
truncatedHow many changes were cut from… | object |
stagedStaged changes not listed above | number |
unstagedUnstaged changes not listed above | number |
scratchUntracked paths that look like scratch | object[] |
pathRelative to its repository | string |
reasonWhy it looks like scratch | "hidden" | "byproduct" | "checkout" | "oversized" … (5) |
filesHow many files it holds | number |
bytesTheir total size in bytes | number |
remoteWhere this repository stands against its… | object |
remoteThe remote this branch pushes to | string |
branchThe checked-out branch | string |
upstreamThe branch on the remote this… | string |
aheadCommits you have that the remote… | number |
behindCommits the remote has that you… | number |
originsWhich conversation put each path here,… | object |
errorWhy the repository could not be… | string |
originAgentsWho each conversation named above is,… | object |
committingRepositories with a commit running right… | string[] |
curl "$SANDBOX/git/changes" \
-H "x-intentic-control: $INTENTIC_TOKEN"import { sandbox } from "@intentic/sandbox-client";
const result = await sandbox.git.changes();GET/git/reposEvery git repo in the workspace
The repos the daemon found under the workspace root, each with the id every other call in this group expects as its `{repo}` segment. The workspace root itself is always present as `root`.
What you send
Nothing. Call it as it is.
What comes back
| Field | Type |
|---|---|
reposEvery repository's id | string[] |
curl "$SANDBOX/git/repos" \
-H "x-intentic-control: $INTENTIC_TOKEN"import { sandbox } from "@intentic/sandbox-client";
const result = await sandbox.git.repos();GET/git/remote-reposRepos matched to their remotes
The same repo list, but with the forge host and `owner/name` each one's remote points at. Use it to recognise a workspace repo in a list of names that came from somewhere else, such as a set of pull requests. Costs a remote lookup per repo, which is why it is separate from the plain repo list.
What you send
Nothing. Call it as it is.
What comes back
| Field | Type |
|---|---|
reposEach repository matched to the project… | object[] |
repoThe workspace repository | string |
hostWhich forge its remote points at | string |
projectWhich project there, as owner and… | string |
curl "$SANDBOX/git/remote-repos" \
-H "x-intentic-control: $INTENTIC_TOKEN"import { sandbox } from "@intentic/sandbox-client";
const result = await sandbox.git.remoteRepos();GET/git/{repo}/logCommit history for one repo
A page of commits on the current branch, newest first, each with its author, subject, timestamp and the refs pointing at it. Paginate with the cursor the answer hands back rather than by offset, so a commit landing mid-scroll does not shift the page under you.
What you send
| Field | Type | Where |
|---|---|---|
reporequiredWhich repository | string | address |
limitHow many commits to return | integer | query |
skipHow many newer commits to step… | integer | query |
What comes back
| Field | Type |
|---|---|
repoWhich repository | string |
branchWhich branch these are from | string |
commitsThe commits, newest first | object[] |
shaThe commit, in full | string |
shortThe abbreviated form, for showing | string |
parentsWhat it came from | string[] |
subjectIts first line | string |
bodyEverything after that | string |
authorWho wrote it | string |
emailTheir address | string |
atWhen they wrote it, in milliseconds | number |
refsBranches and tags sitting on it | string[] |
headWhether this is where the repository… | boolean |
hasMoreThere are older ones behind this… | boolean |
curl "$SANDBOX/git/root/log" \
-H "x-intentic-control: $INTENTIC_TOKEN"import { sandbox } from "@intentic/sandbox-client";
const result = await sandbox.git.log({
"repo": "root"
});GET/git/{repo}/commit-diffWhat one commit changed
The list of files a single commit touched, with per-file status and line counts but not the content. Fetch the content of any one of them with the commit file diff call, so a commit with a thousand files stays one cheap answer.
What you send
| Field | Type | Where |
|---|---|---|
reporequiredWhich repository | string | address |
sharequiredWhich commit | string | query |
What comes back
| Field | Type |
|---|---|
filesWhich files it touched, with counts… | object[] |
pathThe path, relative to the repository… | string |
statusWhat happened to it | "added" | "modified" | "deleted" | "renamed" … (6) |
fromWhere a renamed file came from | string |
additionsLines added | number |
deletionsLines removed | number |
codeThe same +/− with every comment… | object |
additions | number |
deletions | number |
curl "$SANDBOX/git/root/commit-diff?sha=9f2c1ab3d4e5f60718293a4b5c6d7e8f90a1b2c3" \
-H "x-intentic-control: $INTENTIC_TOKEN"import { sandbox } from "@intentic/sandbox-client";
const result = await sandbox.git.commitDiff({
"repo": "root",
"sha": "9f2c1ab3d4e5f60718293a4b5c6d7e8f90a1b2c3"
});GET/git/{repo}/commit-file-diffOne file's before and after at a commit
Both sides of a single file as of one commit: the content its parent had and the content that commit left. The daemon returns whole sides rather than a patch, so a caller can render the comparison however it likes.
What you send
| Field | Type | Where |
|---|---|---|
reporequiredWhich repository | string | address |
sharequiredWhich commit | string | query |
pathrequiredWhich file in it | string | query |
What comes back
| Field | Type |
|---|---|
beforeThe whole file as it was | string |
afterThe whole file as it is… | string |
binaryThe file is not text, so… | boolean |
partialSet when the file was too… | object |
beforeBytesHow big the before side is,… | integer |
afterBytesHow big the after side is,… | integer |
patchThe changed regions as unified-diff hunks… | string |
moreThere were more changed regions than… | boolean |
curl "$SANDBOX/git/root/commit-file-diff?sha=9f2c1ab3d4e5f60718293a4b5c6d7e8f90a1b2c3&path=src%2Fapp.ts" \
-H "x-intentic-control: $INTENTIC_TOKEN"import { sandbox } from "@intentic/sandbox-client";
const result = await sandbox.git.commitFileDiff({
"repo": "root",
"sha": "9f2c1ab3d4e5f60718293a4b5c6d7e8f90a1b2c3",
"path": "src/app.ts"
});GET/git/{repo}/operationWhether a merge or rebase is halted mid-flight
Names the git operation the worktree is stuck inside, if any: a conflicted merge, an interrupted rebase, a half-applied cherry-pick. Check this first when another call refuses, because a halted worktree is the usual reason and the abort call is the way out.
What you send
| Field | Type | Where |
|---|---|---|
reporequiredWhich repository | string | address |
What comes back
| Field | Type |
|---|---|
repoThe repository asked about | string |
operationWhich operation the working tree is… | "merge" | "rebase" | "cherry-pick" | "revert" |
curl "$SANDBOX/git/root/operation" \
-H "x-intentic-control: $INTENTIC_TOKEN"import { sandbox } from "@intentic/sandbox-client";
const result = await sandbox.git.operation({
"repo": "root"
});POST/git/{repo}/abortAbandon a halted merge or rebase
Runs git's own abort for whichever operation has the worktree halted, putting the repo back where it stood before the operation started. Nothing else clears that state.
What you send
| Field | Type | Where |
|---|---|---|
reporequiredWhich repository | string | address |
What comes back
| Field | Type |
|---|---|
okWhether it worked | boolean |
reasonWhy not, in git's own words | string |
curl -X POST "$SANDBOX/git/root/abort" \
-H "x-intentic-control: $INTENTIC_TOKEN"import { sandbox } from "@intentic/sandbox-client";
const result = await sandbox.git.abort({
"repo": "root"
});GET/git/{repo}/undoWhat undoing the last action would do
Reads the branch's reflog to describe the move that undo would reverse, and hands back the commit it would land on. Pass that commit to the undo call as proof you looked, and an undo prepared against a view that has since moved is refused rather than landing somewhere unexamined.
What you send
| Field | Type | Where |
|---|---|---|
reporequiredWhich repository | string | address |
What comes back
| Field | Type |
|---|---|
repoWhich repository | string |
actionWhat undoing would reverse | object |
kindWhat the last action was | "commit" | "amend" | "merge" | "rebase" … (9) |
descriptionWhat undoing it would do, in… | string |
branchWhich branch would move | string |
shaWhere it stands now | string |
previousShaWhere it would go back to | string |
changesWorkingTreeUndoing would rewrite files as well… | boolean |
curl "$SANDBOX/git/root/undo" \
-H "x-intentic-control: $INTENTIC_TOKEN"import { sandbox } from "@intentic/sandbox-client";
const result = await sandbox.git.undoable({
"repo": "root"
});POST/git/{repo}/undoMove the branch back one step
Walks the current branch back to where it pointed before its last action. This moves the branch ref and leaves the working tree alone, which is the opposite of restoring a checkpoint. Requires the commit the matching read handed you.
What you send
| Field | Type | Where |
|---|---|---|
reporequiredWhich repository | string | address |
previousSharequiredWhere to go back to, from… | string | body |
discardChangesAlso rewrite the files, rather than… | boolean | body |
What comes back
| Field | Type |
|---|---|
okWhether it worked | boolean |
reasonWhy not, in git's own words | string |
curl -X POST "$SANDBOX/git/root/undo" \
-H "x-intentic-control: $INTENTIC_TOKEN" \
-H "content-type: application/json" \
-d '{"previousSha":"1a2b3c4d5e6f708192a3b4c5d6e7f8091a2b3c4d","discardChanges":true}'import { sandbox } from "@intentic/sandbox-client";
const result = await sandbox.git.undo({
"repo": "root",
"previousSha": "1a2b3c4d5e6f708192a3b4c5d6e7f8091a2b3c4d",
"discardChanges": true
});GET/git/{repo}/stashesEverything set aside in the stash
The repo's stash entries, newest first, each with the message and the commit behind it. A stash entry is a commit, so it reads the same way a log entry does and its contents come back from the stash diff call.
What you send
| Field | Type | Where |
|---|---|---|
reporequiredWhich repository | string | address |
What comes back
| Field | Type |
|---|---|
repoWhich repository | string |
stashesWhat is set aside, newest first | object[] |
refHow to address it, which applying… | string |
shaThe commit behind it, because a… | string |
shortThe abbreviated form, for showing | string |
subjectWhat it was set aside as,… | string |
branchWhich branch it was set aside… | string |
atWhen, in milliseconds | number |
parentsWhat it sits on, so a… | string[] |
curl "$SANDBOX/git/root/stashes" \
-H "x-intentic-control: $INTENTIC_TOKEN"import { sandbox } from "@intentic/sandbox-client";
const result = await sandbox.git.stashes({
"repo": "root"
});GET/git/{repo}/stash-diffWhat one stash entry holds
The files a single stash entry would bring back, with per-file status and line counts. The same shape a commit diff has, because a stash entry is a commit.
What you send
| Field | Type | Where |
|---|---|---|
reporequiredWhich repository | string | address |
refrequiredWhich entry | string | query |
What comes back
| Field | Type |
|---|---|
filesWhich files it touched, with counts… | object[] |
pathThe path, relative to the repository… | string |
statusWhat happened to it | "added" | "modified" | "deleted" | "renamed" … (6) |
fromWhere a renamed file came from | string |
additionsLines added | number |
deletionsLines removed | number |
codeThe same +/− with every comment… | object |
additions | number |
deletions | number |
curl "$SANDBOX/git/root/stash-diff?ref=refs%2Fheads%2Fmain" \
-H "x-intentic-control: $INTENTIC_TOKEN"import { sandbox } from "@intentic/sandbox-client";
const result = await sandbox.git.stashDiff({
"repo": "root",
"ref": "refs/heads/main"
});POST/git/{repo}/stashSet the current changes aside
Moves the working tree's changes onto the stash and leaves a clean tree behind. Nothing is lost: the entry is a commit you can inspect, apply or drop afterwards.
What you send
| Field | Type | Where |
|---|---|---|
reporequiredWhich repository | string | address |
messageWhat to call it, so you… | string | body |
includeUntrackedAlso set aside files git is… | boolean | body |
What comes back
| Field | Type |
|---|---|
okWhether it worked | boolean |
reasonWhy not, in git's own words | string |
curl -X POST "$SANDBOX/git/root/stash" \
-H "x-intentic-control: $INTENTIC_TOKEN" \
-H "content-type: application/json" \
-d '{"message":"Fix the flaky parser test","includeUntracked":true}'import { sandbox } from "@intentic/sandbox-client";
const result = await sandbox.git.stashPush({
"repo": "root",
"message": "Fix the flaky parser test",
"includeUntracked": true
});POST/git/{repo}/stash/applyBring a stash entry back
Replays one stash entry onto the working tree. A conflict is reported in the answer rather than raised as a failure, because a conflicting apply is an ordinary outcome a screen has to render.
What you send
| Field | Type | Where |
|---|---|---|
reporequiredWhich repository | string | address |
refrequiredWhich entry | string | body |
popRemove it from the stash once… | boolean | body |
What comes back
| Field | Type |
|---|---|
okWhether it worked | boolean |
reasonWhy not, in git's own words | string |
curl -X POST "$SANDBOX/git/root/stash/apply" \
-H "x-intentic-control: $INTENTIC_TOKEN" \
-H "content-type: application/json" \
-d '{"ref":"refs/heads/main","pop":true}'import { sandbox } from "@intentic/sandbox-client";
const result = await sandbox.git.stashApply({
"repo": "root",
"ref": "refs/heads/main",
"pop": true
});POST/git/{repo}/stash/dropDiscard a stash entry
Deletes one stash entry. This is the only unrecoverable call in the stash set, so the daemon takes a checkpoint of the workspace first.
What you send
| Field | Type | Where |
|---|---|---|
reporequiredWhich repository | string | address |
refrequiredWhich entry | string | body |
What comes back
| Field | Type |
|---|---|
okAlways true | true |
curl -X POST "$SANDBOX/git/root/stash/drop" \
-H "x-intentic-control: $INTENTIC_TOKEN" \
-H "content-type: application/json" \
-d '{"ref":"refs/heads/main"}'import { sandbox } from "@intentic/sandbox-client";
const result = await sandbox.git.stashDrop({
"repo": "root",
"ref": "refs/heads/main"
});POST/git/{repo}/branchStart a branch at a commit
Points a new branch name at any commit, without moving HEAD. Use the checkout call if you also want to switch to it.
What you send
| Field | Type | Where |
|---|---|---|
reporequiredWhich repository | string | address |
sharequiredWhich commit to start it at | string | body |
namerequiredThe new branch's name | string | body |
What comes back
| Field | Type |
|---|---|
okAlways true | true |
curl -X POST "$SANDBOX/git/root/branch" \
-H "x-intentic-control: $INTENTIC_TOKEN" \
-H "content-type: application/json" \
-d '{"sha":"9f2c1ab3d4e5f60718293a4b5c6d7e8f90a1b2c3","name":"nightly changelog"}'import { sandbox } from "@intentic/sandbox-client";
const result = await sandbox.git.createBranch({
"repo": "root",
"sha": "9f2c1ab3d4e5f60718293a4b5c6d7e8f90a1b2c3",
"name": "nightly changelog"
});POST/git/{repo}/tagTag a commit
Puts a tag on any commit. Local only: pushing it to the remote is a separate call.
What you send
| Field | Type | Where |
|---|---|---|
reporequiredWhich repository | string | address |
sharequiredWhich commit to tag | string | body |
namerequiredThe tag's name | string | body |
What comes back
| Field | Type |
|---|---|
okAlways true | true |
curl -X POST "$SANDBOX/git/root/tag" \
-H "x-intentic-control: $INTENTIC_TOKEN" \
-H "content-type: application/json" \
-d '{"sha":"9f2c1ab3d4e5f60718293a4b5c6d7e8f90a1b2c3","name":"nightly changelog"}'import { sandbox } from "@intentic/sandbox-client";
const result = await sandbox.git.createTag({
"repo": "root",
"sha": "9f2c1ab3d4e5f60718293a4b5c6d7e8f90a1b2c3",
"name": "nightly changelog"
});POST/git/{repo}/tag/deleteRemove a tag
Deletes a tag locally. A tag already pushed stays on the remote until it is deleted there too.
What you send
| Field | Type | Where |
|---|---|---|
reporequiredWhich repository | string | address |
namerequiredWhich tag | string | body |
remoteAlso delete it there | string | body |
What comes back
| Field | Type |
|---|---|
okAlways true | true |
curl -X POST "$SANDBOX/git/root/tag/delete" \
-H "x-intentic-control: $INTENTIC_TOKEN" \
-H "content-type: application/json" \
-d '{"name":"nightly changelog","remote":"origin"}'import { sandbox } from "@intentic/sandbox-client";
const result = await sandbox.git.deleteTag({
"repo": "root",
"name": "nightly changelog",
"remote": "origin"
});POST/git/{repo}/tag/pushSend a tag to the remote
Pushes one tag to the repo's remote. Reports the outcome rather than failing, since a missing remote or missing credentials are ordinary answers here.
What you send
| Field | Type | Where |
|---|---|---|
reporequiredWhich repository | string | address |
namerequiredWhich tag | string | body |
remoterequiredWhich remote to send it to | string | body |
What comes back
| Field | Type |
|---|---|
okWhether it worked | boolean |
reasonWhy not, in git's own words | string |
curl -X POST "$SANDBOX/git/root/tag/push" \
-H "x-intentic-control: $INTENTIC_TOKEN" \
-H "content-type: application/json" \
-d '{"name":"nightly changelog","remote":"origin"}'import { sandbox } from "@intentic/sandbox-client";
const result = await sandbox.git.pushTag({
"repo": "root",
"name": "nightly changelog",
"remote": "origin"
});POST/git/{repo}/checkoutSwitch to a branch or commit
Moves HEAD to a branch, tag or commit and reshapes the working tree to match. The daemon takes a checkpoint first, so an unexpected result is recoverable. Uncommitted work that would be overwritten is reported instead of being trampled.
What you send
| Field | Type | Where |
|---|---|---|
reporequiredWhich repository | string | address |
refrequiredWhere to switch to: a branch,… | string | body |
What comes back
| Field | Type |
|---|---|
okWhether it worked | boolean |
reasonWhy not, in git's own words | string |
curl -X POST "$SANDBOX/git/root/checkout" \
-H "x-intentic-control: $INTENTIC_TOKEN" \
-H "content-type: application/json" \
-d '{"ref":"refs/heads/main"}'import { sandbox } from "@intentic/sandbox-client";
const result = await sandbox.git.checkout({
"repo": "root",
"ref": "refs/heads/main"
});POST/git/{repo}/cherry-pickReplay one commit onto this branch
Applies a single commit's changes on top of the current branch as a new commit. A conflict comes back in the answer, with the halted state readable from the operation call.
What you send
| Field | Type | Where |
|---|---|---|
reporequiredWhich repository | string | address |
sharequiredWhich commit to act on | string | body |
What comes back
| Field | Type |
|---|---|
okWhether it worked | boolean |
reasonWhy not, in git's own words | string |
curl -X POST "$SANDBOX/git/root/cherry-pick" \
-H "x-intentic-control: $INTENTIC_TOKEN" \
-H "content-type: application/json" \
-d '{"sha":"9f2c1ab3d4e5f60718293a4b5c6d7e8f90a1b2c3"}'import { sandbox } from "@intentic/sandbox-client";
const result = await sandbox.git.cherryPick({
"repo": "root",
"sha": "9f2c1ab3d4e5f60718293a4b5c6d7e8f90a1b2c3"
});POST/git/{repo}/revertUndo a commit with a new commit
Adds a commit that reverses an earlier one, leaving the history intact. This is the safe way to take something back on a branch other people have pulled.
What you send
| Field | Type | Where |
|---|---|---|
reporequiredWhich repository | string | address |
sharequiredWhich commit to act on | string | body |
What comes back
| Field | Type |
|---|---|
okWhether it worked | boolean |
reasonWhy not, in git's own words | string |
curl -X POST "$SANDBOX/git/root/revert" \
-H "x-intentic-control: $INTENTIC_TOKEN" \
-H "content-type: application/json" \
-d '{"sha":"9f2c1ab3d4e5f60718293a4b5c6d7e8f90a1b2c3"}'import { sandbox } from "@intentic/sandbox-client";
const result = await sandbox.git.revert({
"repo": "root",
"sha": "9f2c1ab3d4e5f60718293a4b5c6d7e8f90a1b2c3"
});POST/git/{repo}/dropRemove a commit from history
Rewrites the branch so one commit is no longer in it. History changes, so this is for branches nobody else has pulled. A checkpoint is taken first.
What you send
| Field | Type | Where |
|---|---|---|
reporequiredWhich repository | string | address |
sharequiredWhich commit to act on | string | body |
What comes back
| Field | Type |
|---|---|
okWhether it worked | boolean |
reasonWhy not, in git's own words | string |
curl -X POST "$SANDBOX/git/root/drop" \
-H "x-intentic-control: $INTENTIC_TOKEN" \
-H "content-type: application/json" \
-d '{"sha":"9f2c1ab3d4e5f60718293a4b5c6d7e8f90a1b2c3"}'import { sandbox } from "@intentic/sandbox-client";
const result = await sandbox.git.drop({
"repo": "root",
"sha": "9f2c1ab3d4e5f60718293a4b5c6d7e8f90a1b2c3"
});POST/git/{repo}/mergeMerge another branch in
Merges a branch or commit into the current one. Conflicts are reported in the answer and leave the worktree halted, which the operation call explains and the abort call clears.
What you send
| Field | Type | Where |
|---|---|---|
reporequiredWhich repository | string | address |
sharequiredWhich commit to act on | string | body |
What comes back
| Field | Type |
|---|---|
okWhether it worked | boolean |
reasonWhy not, in git's own words | string |
curl -X POST "$SANDBOX/git/root/merge" \
-H "x-intentic-control: $INTENTIC_TOKEN" \
-H "content-type: application/json" \
-d '{"sha":"9f2c1ab3d4e5f60718293a4b5c6d7e8f90a1b2c3"}'import { sandbox } from "@intentic/sandbox-client";
const result = await sandbox.git.merge({
"repo": "root",
"sha": "9f2c1ab3d4e5f60718293a4b5c6d7e8f90a1b2c3"
});POST/git/{repo}/rebaseReplay this branch onto another
Moves the current branch's commits on top of a different base. History changes. Conflicts halt the rebase and are reported rather than raised, so the operation and abort calls are the way through.
What you send
| Field | Type | Where |
|---|---|---|
reporequiredWhich repository | string | address |
sharequiredWhich commit to act on | string | body |
What comes back
| Field | Type |
|---|---|
okWhether it worked | boolean |
reasonWhy not, in git's own words | string |
curl -X POST "$SANDBOX/git/root/rebase" \
-H "x-intentic-control: $INTENTIC_TOKEN" \
-H "content-type: application/json" \
-d '{"sha":"9f2c1ab3d4e5f60718293a4b5c6d7e8f90a1b2c3"}'import { sandbox } from "@intentic/sandbox-client";
const result = await sandbox.git.rebase({
"repo": "root",
"sha": "9f2c1ab3d4e5f60718293a4b5c6d7e8f90a1b2c3"
});POST/git/{repo}/resetMove the branch to a commit
Repoints the current branch at another commit, optionally reshaping the working tree to match. The destructive modes take a checkpoint first.
What you send
| Field | Type | Where |
|---|---|---|
reporequiredWhich repository | string | address |
sharequiredWhich commit to move the branch… | string | body |
moderequiredHow much to take with it:… | "soft" | "mixed" | "hard" | body |
What comes back
| Field | Type |
|---|---|
okWhether it worked | boolean |
reasonWhy not, in git's own words | string |
curl -X POST "$SANDBOX/git/root/reset" \
-H "x-intentic-control: $INTENTIC_TOKEN" \
-H "content-type: application/json" \
-d '{"sha":"9f2c1ab3d4e5f60718293a4b5c6d7e8f90a1b2c3","mode":"soft"}'import { sandbox } from "@intentic/sandbox-client";
const result = await sandbox.git.reset({
"repo": "root",
"sha": "9f2c1ab3d4e5f60718293a4b5c6d7e8f90a1b2c3",
"mode": "soft"
});GET/git/{repo}/file-diffOne file's committed and working copies
Both sides of a file as it stands right now: what the last commit holds and what is on disk. This is what a review pane shows for an uncommitted change.
What you send
| Field | Type | Where |
|---|---|---|
reporequiredWhich repository | string | address |
pathrequiredThe file, relative to the repository… | string | query |
siderequiredWhich comparison you want | "staged" | "unstaged" | "conflicted" | query |
What comes back
| Field | Type |
|---|---|
beforeThe whole file as it was | string |
afterThe whole file as it is… | string |
binaryThe file is not text, so… | boolean |
partialSet when the file was too… | object |
beforeBytesHow big the before side is,… | integer |
afterBytesHow big the after side is,… | integer |
patchThe changed regions as unified-diff hunks… | string |
moreThere were more changed regions than… | boolean |
curl "$SANDBOX/git/root/file-diff?path=src%2Fapp.ts&side=staged" \
-H "x-intentic-control: $INTENTIC_TOKEN"import { sandbox } from "@intentic/sandbox-client";
const result = await sandbox.git.fileDiff({
"repo": "root",
"path": "src/app.ts",
"side": "staged"
});GET/git/{repo}/statusOne repo's branch and pending changes
The current branch, its sync position against the remote, and every staged, unstaged and untracked path. The single-repo counterpart to the workspace-wide changes call.
What you send
| Field | Type | Where |
|---|---|---|
reporequiredWhich repository | string | address |
What comes back
| Field | Type |
|---|---|
branchThe checked-out branch | string |
dirtyWhether anything is uncommitted | boolean |
filesEvery path with something pending, staged… | string[] |
curl "$SANDBOX/git/root/status" \
-H "x-intentic-control: $INTENTIC_TOKEN"import { sandbox } from "@intentic/sandbox-client";
const result = await sandbox.git.status({
"repo": "root"
});POST/git/{repo}/commitCommit the pending changes
Records a commit with your message. It commits whatever is staged; add `stage` to stage something first — an empty object for everything pending, or a scope such as one side or one conversation's landed files. The answer carries the commit it created.
What you send
| Field | Type | Where |
|---|---|---|
reporequiredWhich repository | string | address |
messagerequiredThe commit message | string | body |
stageWhat to stage before committing | object | body |
pathsExactly these repository-relative paths | string[] | body |
scopeWhat to act on, described rather… | object | body |
sideNarrow to one of the three… | "staged" | "unstaged" | "conflicted" | body |
originNarrow to the files one conversation… | string | body |
What comes back
| Field | Type |
|---|---|
committedWhether a commit was actually recorded | boolean |
changesWhat this repository looks like now,… | object |
repo | string |
branchThe checked-out branch | string |
conflictedPaths a merge or rebase could… | object[] |
pathThe path, relative to the repository… | string |
statusWhat happened to it | "added" | "modified" | "deleted" | "renamed" … (6) |
fromWhere a renamed file came from | string |
additionsLines added | number |
deletionsLines removed | number |
codeThe same +/− with every comment… | object |
additions | number |
deletions | number |
operationWhat halted, when something did | "merge" | "rebase" | "cherry-pick" | "revert" |
stagedWhat a plain commit would record… | object[] |
pathThe path, relative to the repository… | string |
statusWhat happened to it | "added" | "modified" | "deleted" | "renamed" … (6) |
fromWhere a renamed file came from | string |
additionsLines added | number |
deletionsLines removed | number |
codeThe same +/− with every comment… | object |
additions | number |
deletions | number |
unstagedEdits on disk that are not… | object[] |
pathThe path, relative to the repository… | string |
statusWhat happened to it | "added" | "modified" | "deleted" | "renamed" … (6) |
fromWhere a renamed file came from | string |
additionsLines added | number |
deletionsLines removed | number |
codeThe same +/− with every comment… | object |
additions | number |
deletions | number |
truncatedHow many changes were cut from… | object |
stagedStaged changes not listed above | number |
unstagedUnstaged changes not listed above | number |
scratchUntracked paths that look like scratch | object[] |
pathRelative to its repository | string |
reasonWhy it looks like scratch | "hidden" | "byproduct" | "checkout" | "oversized" … (5) |
filesHow many files it holds | number |
bytesTheir total size in bytes | number |
remoteWhere this repository stands against its… | object |
remoteThe remote this branch pushes to | string |
branchThe checked-out branch | string |
upstreamThe branch on the remote this… | string |
aheadCommits you have that the remote… | number |
behindCommits the remote has that you… | number |
originsWhich conversation put each path here,… | object |
errorWhy the repository could not be… | string |
originAgentsWho the conversations named in those… | object |
curl -X POST "$SANDBOX/git/root/commit" \
-H "x-intentic-control: $INTENTIC_TOKEN" \
-H "content-type: application/json" \
-d '{"message":"Fix the flaky parser test","stage":{"paths":["src/app.ts","README.md"],"scope":{"side":"staged","origin":"…"}}}'import { sandbox } from "@intentic/sandbox-client";
const result = await sandbox.git.commit({
"repo": "root",
"message": "Fix the flaky parser test",
"stage": {
"paths": [
"src/app.ts",
"README.md"
],
"scope": {
"side": "staged",
"origin": "…"
}
}
});POST/git/{repo}/discardThrow away pending changes
Restores files to their committed state and deletes untracked ones. Name paths or a scope to narrow it; with neither it throws away every uncommitted change in the repository. The daemon checkpoints the workspace first, so this is recoverable from the timeline.
What you send
| Field | Type | Where |
|---|---|---|
reporequiredWhich repository | string | address |
pathsExactly these repository-relative paths | string[] | body |
scopeWhat to act on, described rather… | object | body |
sideNarrow to one of the three… | "staged" | "unstaged" | "conflicted" | body |
originNarrow to the files one conversation… | string | body |
What comes back
| Field | Type |
|---|---|
okAlways true | true |
curl -X POST "$SANDBOX/git/root/discard" \
-H "x-intentic-control: $INTENTIC_TOKEN" \
-H "content-type: application/json" \
-d '{"paths":["src/app.ts","README.md"],"scope":{"side":"staged","origin":"…"}}'import { sandbox } from "@intentic/sandbox-client";
const result = await sandbox.git.discard({
"repo": "root",
"paths": [
"src/app.ts",
"README.md"
],
"scope": {
"side": "staged",
"origin": "…"
}
});POST/git/{repo}/stageMark changes for the next commit
Adds changes to the index: exactly the paths you name, everything a scope describes, or the whole repository when you name neither. Nothing on disk changes, so this is always safe and always reversible with the unstage call.
What you send
| Field | Type | Where |
|---|---|---|
reporequiredWhich repository | string | address |
pathsExactly these repository-relative paths | string[] | body |
scopeWhat to act on, described rather… | object | body |
sideNarrow to one of the three… | "staged" | "unstaged" | "conflicted" | body |
originNarrow to the files one conversation… | string | body |
What comes back
| Field | Type |
|---|---|
okAlways true | true |
curl -X POST "$SANDBOX/git/root/stage" \
-H "x-intentic-control: $INTENTIC_TOKEN" \
-H "content-type: application/json" \
-d '{"paths":["src/app.ts","README.md"],"scope":{"side":"staged","origin":"…"}}'import { sandbox } from "@intentic/sandbox-client";
const result = await sandbox.git.stage({
"repo": "root",
"paths": [
"src/app.ts",
"README.md"
],
"scope": {
"side": "staged",
"origin": "…"
}
});POST/git/{repo}/unstageTake changes back out of the next commit
Removes changes from the index and leaves the files themselves untouched, on the same terms as staging. The exact reverse of it.
What you send
| Field | Type | Where |
|---|---|---|
reporequiredWhich repository | string | address |
pathsExactly these repository-relative paths | string[] | body |
scopeWhat to act on, described rather… | object | body |
sideNarrow to one of the three… | "staged" | "unstaged" | "conflicted" | body |
originNarrow to the files one conversation… | string | body |
What comes back
| Field | Type |
|---|---|
okAlways true | true |
curl -X POST "$SANDBOX/git/root/unstage" \
-H "x-intentic-control: $INTENTIC_TOKEN" \
-H "content-type: application/json" \
-d '{"paths":["src/app.ts","README.md"],"scope":{"side":"staged","origin":"…"}}'import { sandbox } from "@intentic/sandbox-client";
const result = await sandbox.git.unstage({
"repo": "root",
"paths": [
"src/app.ts",
"README.md"
],
"scope": {
"side": "staged",
"origin": "…"
}
});GET/git/{repo}/branchesLocal branches and how far each has drifted
Every local branch with how many commits it sits ahead of and behind its remote counterpart, so a branch switcher can show sync state without a call per branch.
What you send
| Field | Type | Where |
|---|---|---|
reporequiredWhich repository | string | address |
What comes back
| Field | Type |
|---|---|
branchesBranches in this repository | object[] |
nameThe branch name | string |
currentWhether this is the one checked… | boolean |
upstreamThe branch on the remote it… | string |
aheadCommits this branch has that its… | number |
behindCommits its remote counterpart has that… | number |
goneThe branch it followed no longer… | boolean |
atWhen its tip was committed, in… | number |
remotesBranches on its remotes, as last… | object[] |
nameThe full name, such as origin/main | string |
remoteJust the remote part, so a… | string |
branchJust the branch part | string |
atWhen its tip was committed, in… | number |
curl "$SANDBOX/git/root/branches" \
-H "x-intentic-control: $INTENTIC_TOKEN"import { sandbox } from "@intentic/sandbox-client";
const result = await sandbox.git.branches({
"repo": "root"
});POST/git/{repo}/branchesCreate a branch from a starting point
Makes a branch at a named start point and optionally switches to it. The branch-switcher counterpart to creating a branch at a specific commit.
What you send
| Field | Type | Where |
|---|---|---|
reporequiredWhich repository | string | address |
namerequiredThe new branch's name | string | body |
startWhere to start it: a commit… | string | body |
checkoutSwitch to it as well as… | boolean | body |
What comes back
| Field | Type |
|---|---|
okAlways true | true |
curl -X POST "$SANDBOX/git/root/branches" \
-H "x-intentic-control: $INTENTIC_TOKEN" \
-H "content-type: application/json" \
-d '{"name":"nightly changelog","start":"…","checkout":true}'import { sandbox } from "@intentic/sandbox-client";
const result = await sandbox.git.createBranchAt({
"repo": "root",
"name": "nightly changelog",
"start": "…",
"checkout": true
});POST/git/{repo}/branches/deleteDelete a local branch
Removes a branch from the repo. Unmerged work is refused unless you ask for it to be forced, and the remote branch is untouched either way.
What you send
| Field | Type | Where |
|---|---|---|
reporequiredWhich repository | string | address |
namerequiredThe branch to delete | string | body |
forceDelete it even though it holds… | boolean | body |
What comes back
| Field | Type |
|---|---|
okAlways true | true |
curl -X POST "$SANDBOX/git/root/branches/delete" \
-H "x-intentic-control: $INTENTIC_TOKEN" \
-H "content-type: application/json" \
-d '{"name":"nightly changelog","force":false}'import { sandbox } from "@intentic/sandbox-client";
const result = await sandbox.git.deleteBranch({
"repo": "root",
"name": "nightly changelog",
"force": false
});GET/git/{repo}/remoteSync position against the remote
How far the current branch sits ahead of and behind its remote, as of the last fetch, plus whether a remote and working credentials exist at all. This is a read of what the daemon already knows, not a network call, which is why fetching is a separate button.
What you send
| Field | Type | Where |
|---|---|---|
reporequiredWhich repository | string | address |
What comes back
| Field | Type |
|---|---|
remoteThe remote this branch pushes to | string |
branchThe checked-out branch | string |
upstreamThe branch on the remote this… | string |
aheadCommits you have that the remote… | number |
behindCommits the remote has that you… | number |
curl "$SANDBOX/git/root/remote" \
-H "x-intentic-control: $INTENTIC_TOKEN"import { sandbox } from "@intentic/sandbox-client";
const result = await sandbox.git.remote({
"repo": "root"
});POST/git/{repo}/fetchRefresh what the remote holds
Contacts the remote and updates the daemon's picture of it without touching your branch. Run this before trusting the sync position.
What you send
| Field | Type | Where |
|---|---|---|
reporequiredWhich repository | string | address |
What comes back
| Field | Type |
|---|---|
okWhether it worked | boolean |
reasonWhy not, in git's own words | string |
curl -X POST "$SANDBOX/git/root/fetch" \
-H "x-intentic-control: $INTENTIC_TOKEN"import { sandbox } from "@intentic/sandbox-client";
const result = await sandbox.git.fetch({
"repo": "root"
});POST/git/{repo}/pullBring remote commits down
Fetches and integrates the remote's commits into the current branch. A pull that cannot fast-forward is reported in the answer rather than raised, because that is an ordinary thing to be told.
What you send
| Field | Type | Where |
|---|---|---|
reporequiredWhich repository | string | address |
What comes back
| Field | Type |
|---|---|
okWhether it worked | boolean |
reasonWhy not, in git's own words | string |
curl -X POST "$SANDBOX/git/root/pull" \
-H "x-intentic-control: $INTENTIC_TOKEN"import { sandbox } from "@intentic/sandbox-client";
const result = await sandbox.git.pull({
"repo": "root"
});POST/git/{repo}/pushStart sending commits to the remote
Starts pushing the current branch, setting its upstream on first push, and answers at once: the push runs in a real terminal (it runs this repository's pre-push hook, which can be a whole suite), so watch it there and poll pushState for the verdict. A second start while one is going joins it rather than pushing twice.
What you send
| Field | Type | Where |
|---|---|---|
reporequiredWhich repository | string | address |
branchWhich branch to push | string | body |
What comes back
| Field | Type |
|---|---|
okAlways true | true |
curl -X POST "$SANDBOX/git/root/push" \
-H "x-intentic-control: $INTENTIC_TOKEN" \
-H "content-type: application/json" \
-d '{"branch":"main"}'import { sandbox } from "@intentic/sandbox-client";
const result = await sandbox.git.push({
"repo": "root",
"branch": "main"
});GET/git/{repo}/pushHow the push is going
The verdict, or the progress so far: where it is, the terminal it runs in, and for a push that did not go, git's last words and who refused it, the repository's own pre-push hook, the remote, or the transport. Idle when nothing has been started for this repository.
What you send
| Field | Type | Where |
|---|---|---|
reporequiredWhich repository | string | address |
What comes back
| Field | Type |
|---|---|
statusWhere the run is | "idle" | "running" | "passed" | "failed" … (6) |
commandWhat actually ran, echoed here rather… | string |
startedAtWhen it began, in milliseconds | number |
finishedAtWhen it ended, in milliseconds | number |
exitCodeHow the command exited | number |
timedOutIt was killed for taking too… | boolean |
sessionThe terminal it runs in, which… | string |
outputThe end of what it printed,… | string |
repoThe repository this run is about,… | string |
reasonWhy not, in git's own words:… | string |
refusedByWho refused a failed push: this… | "hook" | "remote" | "transport" |
curl "$SANDBOX/git/root/push" \
-H "x-intentic-control: $INTENTIC_TOKEN"import { sandbox } from "@intentic/sandbox-client";
const result = await sandbox.git.pushState({
"repo": "root"
});POST/git/{repo}/push/cancelStop the push
Kills the run. It settles as cancelled; nothing that git had not already sent reaches the remote.
What you send
| Field | Type | Where |
|---|---|---|
reporequiredWhich repository | string | address |
What comes back
| Field | Type |
|---|---|
okAlways true | true |
curl -X POST "$SANDBOX/git/root/push/cancel" \
-H "x-intentic-control: $INTENTIC_TOKEN"import { sandbox } from "@intentic/sandbox-client";
const result = await sandbox.git.pushCancel({
"repo": "root"
});GET/git/{repo}/filesEvery tracked path in the repo
The flat list of files git tracks, which is what a file picker or a search box wants. Ignored and untracked files are not in it.
What you send
| Field | Type | Where |
|---|---|---|
reporequiredWhich repository | string | address |
What comes back
| Field | Type |
|---|---|
filesEvery path git tracks, relative to… | string[] |
curl "$SANDBOX/git/root/files" \
-H "x-intentic-control: $INTENTIC_TOKEN"import { sandbox } from "@intentic/sandbox-client";
const result = await sandbox.git.files({
"repo": "root"
});GET/git/{repo}/fileRead a file from the repo
The contents of one file as it stands on disk. A path that climbs out of the repo is refused.
What you send
| Field | Type | Where |
|---|---|---|
reporequiredWhich repository | string | address |
pathrequiredThe file to read, relative to… | string | query |
What comes back
| Field | Type |
|---|---|
pathThe path, as asked for | string |
contentThe file's contents as they stand… | string |
curl "$SANDBOX/git/root/file?path=src%2Fapp.ts" \
-H "x-intentic-control: $INTENTIC_TOKEN"import { sandbox } from "@intentic/sandbox-client";
const result = await sandbox.git.readFile({
"repo": "root",
"path": "src/app.ts"
});PUT/git/{repo}/fileWrite a file into the repo
Replaces one file's contents, creating it and its parent folders if they are missing. Nothing is committed: the change shows up as pending work.
What you send
| Field | Type | Where |
|---|---|---|
reporequiredWhich repository | string | address |
pathrequiredWhere to write, relative to the… | string | body |
contentrequiredThe file's whole new contents | string | body |
What comes back
| Field | Type |
|---|---|
okAlways true | true |
curl -X PUT "$SANDBOX/git/root/file" \
-H "x-intentic-control: $INTENTIC_TOKEN" \
-H "content-type: application/json" \
-d '{"path":"src/app.ts","content":"export const start = () => listen(PORT);\n"}'import { sandbox } from "@intentic/sandbox-client";
const result = await sandbox.git.writeFile({
"repo": "root",
"path": "src/app.ts",
"content": "export const start = () => listen(PORT);\n"
});POST/git/{repo}/publish-fileWrite, commit and push one file
The three steps as a single call with a single answer, committing only the path you named and leaving any other pending work alone. Being on a side branch, having no remote and having no credentials are all reported rather than raised.
What you send
| Field | Type | Where |
|---|---|---|
reporequiredWhich repository | string | address |
pathrequiredWhich file, relative to the repository | string | body |
contentrequiredIts whole new contents | string | body |
messagerequiredThe commit message | string | body |
What comes back
| Field | Type |
|---|---|
okWhether the whole thing went through | boolean |
wroteThe file was written | boolean |
committedThe commit was recorded | boolean |
pushedIt reached the remote | boolean |
branchWhich branch it happened on | string |
defaultBranchWhich branch the repository considers its… | string |
reasonWhy it stopped where it did | string |
curl -X POST "$SANDBOX/git/root/publish-file" \
-H "x-intentic-control: $INTENTIC_TOKEN" \
-H "content-type: application/json" \
-d '{"path":"src/app.ts","content":"export const start = () => listen(PORT);\n","message":"Fix the flaky parser test"}'import { sandbox } from "@intentic/sandbox-client";
const result = await sandbox.git.publishFile({
"repo": "root",
"path": "src/app.ts",
"content": "export const start = () => listen(PORT);\n",
"message": "Fix the flaky parser test"
});