---
title: "Git · intentic sandbox API"
description: "Version control, one repository at a time. Every route in the git group of the intentic sandbox API, with its input, its answer and a playground."
url: "https://intentic.dev/api/git/"
---

The workspace

# Git

Version control, one repository at a time

**On this page (43 sections)**

- [Uncommitted work across every repo](#git-changes)
- [Every git repo in the workspace](#git-repos)
- [Repos matched to their remotes](#git-remoteRepos)
- [Commit history for one repo](#git-log)
- [What one commit changed](#git-commitDiff)
- [One file's before and after at a commit](#git-commitFileDiff)
- [Whether a merge or rebase is halted mid-flight](#git-operation)
- [Abandon a halted merge or rebase](#git-abort)
- [What undoing the last action would do](#git-undoable)
- [Move the branch back one step](#git-undo)
- [Everything set aside in the stash](#git-stashes)
- [What one stash entry holds](#git-stashDiff)
- [Set the current changes aside](#git-stashPush)
- [Bring a stash entry back](#git-stashApply)
- [Discard a stash entry](#git-stashDrop)
- [Start a branch at a commit](#git-createBranch)
- [Tag a commit](#git-createTag)
- [Remove a tag](#git-deleteTag)
- [Send a tag to the remote](#git-pushTag)
- [Switch to a branch or commit](#git-checkout)
- [Replay one commit onto this branch](#git-cherryPick)
- [Undo a commit with a new commit](#git-revert)
- [Remove a commit from history](#git-drop)
- [Merge another branch in](#git-merge)
- [Replay this branch onto another](#git-rebase)
- [Move the branch to a commit](#git-reset)
- [One file's committed and working copies](#git-fileDiff)
- [One repo's branch and pending changes](#git-status)
- [Commit the pending changes](#git-commit)
- [Throw away pending changes](#git-discard)
- [Mark paths for the next commit](#git-stage)
- [Take paths back out of the next commit](#git-unstage)
- [Local branches and how far each has drifted](#git-branches)
- [Create a branch from a starting point](#git-createBranchAt)
- [Delete a local branch](#git-deleteBranch)
- [Sync position against the remote](#git-remote)
- [Refresh what the remote holds](#git-fetch)
- [Bring remote commits down](#git-pull)
- [Send commits to the remote](#git-push)
- [Every tracked path in the repo](#git-files)
- [Read a file from the repo](#git-readFile)
- [Write a file into the repo](#git-writeFile)
- [Write, commit and push one file](#git-publishFile)

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.

43 calls. Pick one to open it, or use the list on the right.

**GET`/git/changes` Uncommitted 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 |
| --- | --- |
| `repos` One entry per repository that has… | object[] |
| `repo` | string |
| `branch` The checked-out branch | string |
| `conflicted` Paths a merge or rebase could… | object[] |
| `path` The path, relative to the repository… | string |
| `status` What happened to it | "added" | "modified" | "deleted" | "renamed" … (6) |
| `from` Where a renamed file came from | string |
| `additions` Lines added | number |
| `deletions` Lines removed | number |
| `operation` What halted, when something did | "merge" | "rebase" | "cherry-pick" | "revert" |
| `staged` What a plain commit would record… | object[] |
| `path` The path, relative to the repository… | string |
| `status` What happened to it | "added" | "modified" | "deleted" | "renamed" … (6) |
| `from` Where a renamed file came from | string |
| `additions` Lines added | number |
| `deletions` Lines removed | number |
| `unstaged` Edits on disk that are not… | object[] |
| `path` The path, relative to the repository… | string |
| `status` What happened to it | "added" | "modified" | "deleted" | "renamed" … (6) |
| `from` Where a renamed file came from | string |
| `additions` Lines added | number |
| `deletions` Lines removed | number |
| `truncated` How many changes were cut from… | number |
| `remote` Where this repository stands against its… | object |
| `remote` The remote this branch pushes to | string |
| `branch` The checked-out branch | string |
| `upstream` The branch on the remote this… | string |
| `ahead` Commits you have that the remote… | number |
| `behind` Commits the remote has that you… | number |
| `origins` Which conversation put each path here,… | object |
| `error` Why the repository could not be… | string |
| `originAgents` Who each conversation named above is,… | object |
| `committing` Repositories with a commit running right… | string[] |

Try it answered in this tab

curl

```bash
curl "$SANDBOX/git/changes" \
 -H "x-intentic-control: $INTENTIC_TOKEN"
```

TypeScript

```typescript
import { sandbox } from "@intentic/sandbox-client";

const result = await sandbox.git.changes();
```

**GET`/git/repos` Every 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 |
| --- | --- |
| `repos` Every repository's id | string[] |

Try it answered in this tab

curl

```bash
curl "$SANDBOX/git/repos" \
 -H "x-intentic-control: $INTENTIC_TOKEN"
```

TypeScript

```typescript
import { sandbox } from "@intentic/sandbox-client";

const result = await sandbox.git.repos();
```

**GET`/git/remote-repos` Repos 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 |
| --- | --- |
| `repos` Each repository matched to the project… | object[] |
| `repo` The workspace repository | string |
| `host` Which forge its remote points at | string |
| `project` Which project there, as owner and… | string |

Try it answered in this tab

curl

```bash
curl "$SANDBOX/git/remote-repos" \
 -H "x-intentic-control: $INTENTIC_TOKEN"
```

TypeScript

```typescript
import { sandbox } from "@intentic/sandbox-client";

const result = await sandbox.git.remoteRepos();
```

**GET`/git/{repo}/log` Commit 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 |
| --- | --- | --- |
| `repo` required Which repository | string | address |
| `limit` How many commits to return | integer | query |
| `skip` How many newer commits to step… | integer | query |

### What comes back

| Field | Type |
| --- | --- |
| `repo` Which repository | string |
| `branch` Which branch these are from | string |
| `commits` The commits, newest first | object[] |
| `sha` The commit, in full | string |
| `short` The abbreviated form, for showing | string |
| `parents` What it came from | string[] |
| `subject` Its first line | string |
| `body` Everything after that | string |
| `author` Who wrote it | string |
| `email` Their address | string |
| `at` When they wrote it, in milliseconds | number |
| `refs` Branches and tags sitting on it | string[] |
| `head` Whether this is where the repository… | boolean |
| `hasMore` There are older ones behind this… | boolean |

Try it answered in this tab

curl

```bash
curl "$SANDBOX/git/root/log" \
 -H "x-intentic-control: $INTENTIC_TOKEN"
```

TypeScript

```typescript
import { sandbox } from "@intentic/sandbox-client";

const result = await sandbox.git.log({
 "repo": "root"
});
```

**GET`/git/{repo}/commit-diff` What 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 |
| --- | --- | --- |
| `repo` required Which repository | string | address |
| `sha` required Which commit | string | query |

### What comes back

| Field | Type |
| --- | --- |
| `files` Which files it touched, with counts… | object[] |
| `path` The path, relative to the repository… | string |
| `status` What happened to it | "added" | "modified" | "deleted" | "renamed" … (6) |
| `from` Where a renamed file came from | string |
| `additions` Lines added | number |
| `deletions` Lines removed | number |

Try it answered in this tab

curl

```bash
curl "$SANDBOX/git/root/commit-diff?sha=9f2c1ab3d4e5f60718293a4b5c6d7e8f90a1b2c3" \
 -H "x-intentic-control: $INTENTIC_TOKEN"
```

TypeScript

```typescript
import { sandbox } from "@intentic/sandbox-client";

const result = await sandbox.git.commitDiff({
 "repo": "root",
 "sha": "9f2c1ab3d4e5f60718293a4b5c6d7e8f90a1b2c3"
});
```

**GET`/git/{repo}/commit-file-diff` One 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 |
| --- | --- | --- |
| `repo` required Which repository | string | address |
| `sha` required Which commit | string | query |
| `path` required Which file in it | string | query |

### What comes back

| Field | Type |
| --- | --- |
| `before` The whole file as it was | string |
| `after` The whole file as it is… | string |
| `binary` The file is not text, so… | boolean |
| `truncated` The file was too large to… | boolean |

Try it answered in this tab

curl

```bash
curl "$SANDBOX/git/root/commit-file-diff?sha=9f2c1ab3d4e5f60718293a4b5c6d7e8f90a1b2c3&path=src%2Fapp.ts" \
 -H "x-intentic-control: $INTENTIC_TOKEN"
```

TypeScript

```typescript
import { sandbox } from "@intentic/sandbox-client";

const result = await sandbox.git.commitFileDiff({
 "repo": "root",
 "sha": "9f2c1ab3d4e5f60718293a4b5c6d7e8f90a1b2c3",
 "path": "src/app.ts"
});
```

**GET`/git/{repo}/operation` Whether 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 |
| --- | --- | --- |
| `repo` required Which repository | string | address |

### What comes back

| Field | Type |
| --- | --- |
| `repo` The repository asked about | string |
| `operation` Which operation the working tree is… | "merge" | "rebase" | "cherry-pick" | "revert" |

Try it answered in this tab

curl

```bash
curl "$SANDBOX/git/root/operation" \
 -H "x-intentic-control: $INTENTIC_TOKEN"
```

TypeScript

```typescript
import { sandbox } from "@intentic/sandbox-client";

const result = await sandbox.git.operation({
 "repo": "root"
});
```

**POST`/git/{repo}/abort` Abandon 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 |
| --- | --- | --- |
| `repo` required Which repository | string | address |

### What comes back

| Field | Type |
| --- | --- |
| `ok` Whether it worked | boolean |
| `reason` Why not, in git's own words | string |

Try it answered in this tab

curl

```bash
curl -X POST "$SANDBOX/git/root/abort" \
 -H "x-intentic-control: $INTENTIC_TOKEN"
```

TypeScript

```typescript
import { sandbox } from "@intentic/sandbox-client";

const result = await sandbox.git.abort({
 "repo": "root"
});
```

**GET`/git/{repo}/undo` What 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 |
| --- | --- | --- |
| `repo` required Which repository | string | address |

### What comes back

| Field | Type |
| --- | --- |
| `repo` Which repository | string |
| `action` What undoing would reverse | object |
| `kind` What the last action was | "commit" | "amend" | "merge" | "rebase" … (9) |
| `description` What undoing it would do, in… | string |
| `branch` Which branch would move | string |
| `sha` Where it stands now | string |
| `previousSha` Where it would go back to | string |
| `changesWorkingTree` Undoing would rewrite files as well… | boolean |

Try it answered in this tab

curl

```bash
curl "$SANDBOX/git/root/undo" \
 -H "x-intentic-control: $INTENTIC_TOKEN"
```

TypeScript

```typescript
import { sandbox } from "@intentic/sandbox-client";

const result = await sandbox.git.undoable({
 "repo": "root"
});
```

**POST`/git/{repo}/undo` Move 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 |
| --- | --- | --- |
| `repo` required Which repository | string | address |
| `previousSha` required Where to go back to, from… | string | body |
| `discardChanges` Also rewrite the files, rather than… | boolean | body |

### What comes back

| Field | Type |
| --- | --- |
| `ok` Whether it worked | boolean |
| `reason` Why not, in git's own words | string |

Try it answered in this tab

curl

```bash
curl -X POST "$SANDBOX/git/root/undo" \
 -H "x-intentic-control: $INTENTIC_TOKEN" \
 -H "content-type: application/json" \
 -d '{"previousSha":"1a2b3c4d5e6f708192a3b4c5d6e7f8091a2b3c4d","discardChanges":true}'
```

TypeScript

```typescript
import { sandbox } from "@intentic/sandbox-client";

const result = await sandbox.git.undo({
 "repo": "root",
 "previousSha": "1a2b3c4d5e6f708192a3b4c5d6e7f8091a2b3c4d",
 "discardChanges": true
});
```

**GET`/git/{repo}/stashes` Everything 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 |
| --- | --- | --- |
| `repo` required Which repository | string | address |

### What comes back

| Field | Type |
| --- | --- |
| `repo` Which repository | string |
| `stashes` What is set aside, newest first | object[] |
| `ref` How to address it, which applying… | string |
| `sha` The commit behind it, because a… | string |
| `short` The abbreviated form, for showing | string |
| `subject` What it was set aside as,… | string |
| `branch` Which branch it was set aside… | string |
| `at` When, in milliseconds | number |
| `parents` What it sits on, so a… | string[] |

Try it answered in this tab

curl

```bash
curl "$SANDBOX/git/root/stashes" \
 -H "x-intentic-control: $INTENTIC_TOKEN"
```

TypeScript

```typescript
import { sandbox } from "@intentic/sandbox-client";

const result = await sandbox.git.stashes({
 "repo": "root"
});
```

**GET`/git/{repo}/stash-diff` What 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 |
| --- | --- | --- |
| `repo` required Which repository | string | address |
| `ref` required Which entry | string | query |

### What comes back

| Field | Type |
| --- | --- |
| `files` Which files it touched, with counts… | object[] |
| `path` The path, relative to the repository… | string |
| `status` What happened to it | "added" | "modified" | "deleted" | "renamed" … (6) |
| `from` Where a renamed file came from | string |
| `additions` Lines added | number |
| `deletions` Lines removed | number |

Try it answered in this tab

curl

```bash
curl "$SANDBOX/git/root/stash-diff?ref=refs%2Fheads%2Fmain" \
 -H "x-intentic-control: $INTENTIC_TOKEN"
```

TypeScript

```typescript
import { sandbox } from "@intentic/sandbox-client";

const result = await sandbox.git.stashDiff({
 "repo": "root",
 "ref": "refs/heads/main"
});
```

**POST`/git/{repo}/stash` Set 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 |
| --- | --- | --- |
| `repo` required Which repository | string | address |
| `message` What to call it, so you… | string | body |
| `includeUntracked` Also set aside files git is… | boolean | body |

### What comes back

| Field | Type |
| --- | --- |
| `ok` Whether it worked | boolean |
| `reason` Why not, in git's own words | string |

Try it answered in this tab

curl

```bash
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}'
```

TypeScript

```typescript
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/apply` Bring 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 |
| --- | --- | --- |
| `repo` required Which repository | string | address |
| `ref` required Which entry | string | body |
| `pop` Remove it from the stash once… | boolean | body |

### What comes back

| Field | Type |
| --- | --- |
| `ok` Whether it worked | boolean |
| `reason` Why not, in git's own words | string |

Try it answered in this tab

curl

```bash
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}'
```

TypeScript

```typescript
import { sandbox } from "@intentic/sandbox-client";

const result = await sandbox.git.stashApply({
 "repo": "root",
 "ref": "refs/heads/main",
 "pop": true
});
```

**POST`/git/{repo}/stash/drop` Discard 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 |
| --- | --- | --- |
| `repo` required Which repository | string | address |
| `ref` required Which entry | string | body |

### What comes back

| Field | Type |
| --- | --- |
| `ok` Always true | true |

Try it answered in this tab

curl

```bash
curl -X POST "$SANDBOX/git/root/stash/drop" \
 -H "x-intentic-control: $INTENTIC_TOKEN" \
 -H "content-type: application/json" \
 -d '{"ref":"refs/heads/main"}'
```

TypeScript

```typescript
import { sandbox } from "@intentic/sandbox-client";

const result = await sandbox.git.stashDrop({
 "repo": "root",
 "ref": "refs/heads/main"
});
```

**POST`/git/{repo}/branch` Start 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 |
| --- | --- | --- |
| `repo` required Which repository | string | address |
| `sha` required Which commit to start it at | string | body |
| `name` required The new branch's name | string | body |

### What comes back

| Field | Type |
| --- | --- |
| `ok` Always true | true |

Try it answered in this tab

curl

```bash
curl -X POST "$SANDBOX/git/root/branch" \
 -H "x-intentic-control: $INTENTIC_TOKEN" \
 -H "content-type: application/json" \
 -d '{"sha":"9f2c1ab3d4e5f60718293a4b5c6d7e8f90a1b2c3","name":"nightly changelog"}'
```

TypeScript

```typescript
import { sandbox } from "@intentic/sandbox-client";

const result = await sandbox.git.createBranch({
 "repo": "root",
 "sha": "9f2c1ab3d4e5f60718293a4b5c6d7e8f90a1b2c3",
 "name": "nightly changelog"
});
```

**POST`/git/{repo}/tag` Tag 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 |
| --- | --- | --- |
| `repo` required Which repository | string | address |
| `sha` required Which commit to tag | string | body |
| `name` required The tag's name | string | body |

### What comes back

| Field | Type |
| --- | --- |
| `ok` Always true | true |

Try it answered in this tab

curl

```bash
curl -X POST "$SANDBOX/git/root/tag" \
 -H "x-intentic-control: $INTENTIC_TOKEN" \
 -H "content-type: application/json" \
 -d '{"sha":"9f2c1ab3d4e5f60718293a4b5c6d7e8f90a1b2c3","name":"nightly changelog"}'
```

TypeScript

```typescript
import { sandbox } from "@intentic/sandbox-client";

const result = await sandbox.git.createTag({
 "repo": "root",
 "sha": "9f2c1ab3d4e5f60718293a4b5c6d7e8f90a1b2c3",
 "name": "nightly changelog"
});
```

**POST`/git/{repo}/tag/delete` Remove 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 |
| --- | --- | --- |
| `repo` required Which repository | string | address |
| `name` required Which tag | string | body |
| `remote` Also delete it there | string | body |

### What comes back

| Field | Type |
| --- | --- |
| `ok` Always true | true |

Try it answered in this tab

curl

```bash
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"}'
```

TypeScript

```typescript
import { sandbox } from "@intentic/sandbox-client";

const result = await sandbox.git.deleteTag({
 "repo": "root",
 "name": "nightly changelog",
 "remote": "origin"
});
```

**POST`/git/{repo}/tag/push` Send 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 |
| --- | --- | --- |
| `repo` required Which repository | string | address |
| `name` required Which tag | string | body |
| `remote` required Which remote to send it to | string | body |

### What comes back

| Field | Type |
| --- | --- |
| `ok` Whether it worked | boolean |
| `reason` Why not, in git's own words | string |

Try it answered in this tab

curl

```bash
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"}'
```

TypeScript

```typescript
import { sandbox } from "@intentic/sandbox-client";

const result = await sandbox.git.pushTag({
 "repo": "root",
 "name": "nightly changelog",
 "remote": "origin"
});
```

**POST`/git/{repo}/checkout` Switch 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 |
| --- | --- | --- |
| `repo` required Which repository | string | address |
| `ref` required Where to switch to: a branch,… | string | body |

### What comes back

| Field | Type |
| --- | --- |
| `ok` Whether it worked | boolean |
| `reason` Why not, in git's own words | string |

Try it answered in this tab

curl

```bash
curl -X POST "$SANDBOX/git/root/checkout" \
 -H "x-intentic-control: $INTENTIC_TOKEN" \
 -H "content-type: application/json" \
 -d '{"ref":"refs/heads/main"}'
```

TypeScript

```typescript
import { sandbox } from "@intentic/sandbox-client";

const result = await sandbox.git.checkout({
 "repo": "root",
 "ref": "refs/heads/main"
});
```

**POST`/git/{repo}/cherry-pick` Replay 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 |
| --- | --- | --- |
| `repo` required Which repository | string | address |
| `sha` required Which commit to act on | string | body |

### What comes back

| Field | Type |
| --- | --- |
| `ok` Whether it worked | boolean |
| `reason` Why not, in git's own words | string |

Try it answered in this tab

curl

```bash
curl -X POST "$SANDBOX/git/root/cherry-pick" \
 -H "x-intentic-control: $INTENTIC_TOKEN" \
 -H "content-type: application/json" \
 -d '{"sha":"9f2c1ab3d4e5f60718293a4b5c6d7e8f90a1b2c3"}'
```

TypeScript

```typescript
import { sandbox } from "@intentic/sandbox-client";

const result = await sandbox.git.cherryPick({
 "repo": "root",
 "sha": "9f2c1ab3d4e5f60718293a4b5c6d7e8f90a1b2c3"
});
```

**POST`/git/{repo}/revert` Undo 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 |
| --- | --- | --- |
| `repo` required Which repository | string | address |
| `sha` required Which commit to act on | string | body |

### What comes back

| Field | Type |
| --- | --- |
| `ok` Whether it worked | boolean |
| `reason` Why not, in git's own words | string |

Try it answered in this tab

curl

```bash
curl -X POST "$SANDBOX/git/root/revert" \
 -H "x-intentic-control: $INTENTIC_TOKEN" \
 -H "content-type: application/json" \
 -d '{"sha":"9f2c1ab3d4e5f60718293a4b5c6d7e8f90a1b2c3"}'
```

TypeScript

```typescript
import { sandbox } from "@intentic/sandbox-client";

const result = await sandbox.git.revert({
 "repo": "root",
 "sha": "9f2c1ab3d4e5f60718293a4b5c6d7e8f90a1b2c3"
});
```

**POST`/git/{repo}/drop` Remove 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 |
| --- | --- | --- |
| `repo` required Which repository | string | address |
| `sha` required Which commit to act on | string | body |

### What comes back

| Field | Type |
| --- | --- |
| `ok` Whether it worked | boolean |
| `reason` Why not, in git's own words | string |

Try it answered in this tab

curl

```bash
curl -X POST "$SANDBOX/git/root/drop" \
 -H "x-intentic-control: $INTENTIC_TOKEN" \
 -H "content-type: application/json" \
 -d '{"sha":"9f2c1ab3d4e5f60718293a4b5c6d7e8f90a1b2c3"}'
```

TypeScript

```typescript
import { sandbox } from "@intentic/sandbox-client";

const result = await sandbox.git.drop({
 "repo": "root",
 "sha": "9f2c1ab3d4e5f60718293a4b5c6d7e8f90a1b2c3"
});
```

**POST`/git/{repo}/merge` Merge 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 |
| --- | --- | --- |
| `repo` required Which repository | string | address |
| `sha` required Which commit to act on | string | body |

### What comes back

| Field | Type |
| --- | --- |
| `ok` Whether it worked | boolean |
| `reason` Why not, in git's own words | string |

Try it answered in this tab

curl

```bash
curl -X POST "$SANDBOX/git/root/merge" \
 -H "x-intentic-control: $INTENTIC_TOKEN" \
 -H "content-type: application/json" \
 -d '{"sha":"9f2c1ab3d4e5f60718293a4b5c6d7e8f90a1b2c3"}'
```

TypeScript

```typescript
import { sandbox } from "@intentic/sandbox-client";

const result = await sandbox.git.merge({
 "repo": "root",
 "sha": "9f2c1ab3d4e5f60718293a4b5c6d7e8f90a1b2c3"
});
```

**POST`/git/{repo}/rebase` Replay 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 |
| --- | --- | --- |
| `repo` required Which repository | string | address |
| `sha` required Which commit to act on | string | body |

### What comes back

| Field | Type |
| --- | --- |
| `ok` Whether it worked | boolean |
| `reason` Why not, in git's own words | string |

Try it answered in this tab

curl

```bash
curl -X POST "$SANDBOX/git/root/rebase" \
 -H "x-intentic-control: $INTENTIC_TOKEN" \
 -H "content-type: application/json" \
 -d '{"sha":"9f2c1ab3d4e5f60718293a4b5c6d7e8f90a1b2c3"}'
```

TypeScript

```typescript
import { sandbox } from "@intentic/sandbox-client";

const result = await sandbox.git.rebase({
 "repo": "root",
 "sha": "9f2c1ab3d4e5f60718293a4b5c6d7e8f90a1b2c3"
});
```

**POST`/git/{repo}/reset` Move 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 |
| --- | --- | --- |
| `repo` required Which repository | string | address |
| `sha` required Which commit to move the branch… | string | body |
| `mode` required How much to take with it:… | "soft" | "mixed" | "hard" | body |

### What comes back

| Field | Type |
| --- | --- |
| `ok` Whether it worked | boolean |
| `reason` Why not, in git's own words | string |

Try it answered in this tab

curl

```bash
curl -X POST "$SANDBOX/git/root/reset" \
 -H "x-intentic-control: $INTENTIC_TOKEN" \
 -H "content-type: application/json" \
 -d '{"sha":"9f2c1ab3d4e5f60718293a4b5c6d7e8f90a1b2c3","mode":"soft"}'
```

TypeScript

```typescript
import { sandbox } from "@intentic/sandbox-client";

const result = await sandbox.git.reset({
 "repo": "root",
 "sha": "9f2c1ab3d4e5f60718293a4b5c6d7e8f90a1b2c3",
 "mode": "soft"
});
```

**GET`/git/{repo}/file-diff` One 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 |
| --- | --- | --- |
| `repo` required Which repository | string | address |
| `path` required The file, relative to the repository… | string | query |
| `side` required Which comparison you want | "staged" | "unstaged" | "conflicted" | query |

### What comes back

| Field | Type |
| --- | --- |
| `before` The whole file as it was | string |
| `after` The whole file as it is… | string |
| `binary` The file is not text, so… | boolean |
| `truncated` The file was too large to… | boolean |

Try it answered in this tab

curl

```bash
curl "$SANDBOX/git/root/file-diff?path=src%2Fapp.ts&side=staged" \
 -H "x-intentic-control: $INTENTIC_TOKEN"
```

TypeScript

```typescript
import { sandbox } from "@intentic/sandbox-client";

const result = await sandbox.git.fileDiff({
 "repo": "root",
 "path": "src/app.ts",
 "side": "staged"
});
```

**GET`/git/{repo}/status` One 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 |
| --- | --- | --- |
| `repo` required Which repository | string | address |

### What comes back

| Field | Type |
| --- | --- |
| `branch` The checked-out branch | string |
| `dirty` Whether anything is uncommitted | boolean |
| `files` Every path with something pending, staged… | string[] |

Try it answered in this tab

curl

```bash
curl "$SANDBOX/git/root/status" \
 -H "x-intentic-control: $INTENTIC_TOKEN"
```

TypeScript

```typescript
import { sandbox } from "@intentic/sandbox-client";

const result = await sandbox.git.status({
 "repo": "root"
});
```

**POST`/git/{repo}/commit` Commit the pending changes**

Records a commit with your message. Give it a list of paths to commit only those, or leave it out to commit everything pending. The answer carries the commit it created.

### What you send

| Field | Type | Where |
| --- | --- | --- |
| `repo` required Which repository | string | address |
| `message` required The commit message | string | body |
| `all` Stage every change in the repository… | boolean | body |
| `paths` Stage exactly these paths, then commit… | string[] | body |

### What comes back

| Field | Type |
| --- | --- |
| `committed` Whether a commit was actually recorded | boolean |
| `changes` What this repository looks like now,… | object |
| `repo` | string |
| `branch` The checked-out branch | string |
| `conflicted` Paths a merge or rebase could… | object[] |
| `path` The path, relative to the repository… | string |
| `status` What happened to it | "added" | "modified" | "deleted" | "renamed" … (6) |
| `from` Where a renamed file came from | string |
| `additions` Lines added | number |
| `deletions` Lines removed | number |
| `operation` What halted, when something did | "merge" | "rebase" | "cherry-pick" | "revert" |
| `staged` What a plain commit would record… | object[] |
| `path` The path, relative to the repository… | string |
| `status` What happened to it | "added" | "modified" | "deleted" | "renamed" … (6) |
| `from` Where a renamed file came from | string |
| `additions` Lines added | number |
| `deletions` Lines removed | number |
| `unstaged` Edits on disk that are not… | object[] |
| `path` The path, relative to the repository… | string |
| `status` What happened to it | "added" | "modified" | "deleted" | "renamed" … (6) |
| `from` Where a renamed file came from | string |
| `additions` Lines added | number |
| `deletions` Lines removed | number |
| `truncated` How many changes were cut from… | number |
| `remote` Where this repository stands against its… | object |
| `remote` The remote this branch pushes to | string |
| `branch` The checked-out branch | string |
| `upstream` The branch on the remote this… | string |
| `ahead` Commits you have that the remote… | number |
| `behind` Commits the remote has that you… | number |
| `origins` Which conversation put each path here,… | object |
| `error` Why the repository could not be… | string |
| `originAgents` Who the conversations named in those… | object |

Try it answered in this tab

curl

```bash
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","all":false,"paths":["src/app.ts","README.md"]}'
```

TypeScript

```typescript
import { sandbox } from "@intentic/sandbox-client";

const result = await sandbox.git.commit({
 "repo": "root",
 "message": "Fix the flaky parser test",
 "all": false,
 "paths": [
 "src/app.ts",
 "README.md"
 ]
});
```

**POST`/git/{repo}/discard` Throw away pending changes**

Restores files to their committed state and deletes untracked ones. Give it paths to discard only those. The daemon checkpoints the workspace first, so this is recoverable from the timeline.

### What you send

| Field | Type | Where |
| --- | --- | --- |
| `repo` required Which repository | string | address |
| `paths` Which paths to throw away | string[] | body |

### What comes back

| Field | Type |
| --- | --- |
| `ok` Always true | true |

Try it answered in this tab

curl

```bash
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"]}'
```

TypeScript

```typescript
import { sandbox } from "@intentic/sandbox-client";

const result = await sandbox.git.discard({
 "repo": "root",
 "paths": [
 "src/app.ts",
 "README.md"
 ]
});
```

**POST`/git/{repo}/stage` Mark paths for the next commit**

Adds paths to the index. Nothing on disk changes, so this is always safe and always reversible with the unstage call.

### What you send

| Field | Type | Where |
| --- | --- | --- |
| `repo` required Which repository | string | address |
| `paths` required The paths to move | string[] | body |

### What comes back

| Field | Type |
| --- | --- |
| `ok` Always true | true |

Try it answered in this tab

curl

```bash
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"]}'
```

TypeScript

```typescript
import { sandbox } from "@intentic/sandbox-client";

const result = await sandbox.git.stage({
 "repo": "root",
 "paths": [
 "src/app.ts",
 "README.md"
 ]
});
```

**POST`/git/{repo}/unstage` Take paths back out of the next commit**

Removes paths from the index and leaves the file itself untouched. The exact reverse of staging.

### What you send

| Field | Type | Where |
| --- | --- | --- |
| `repo` required Which repository | string | address |
| `paths` required The paths to move | string[] | body |

### What comes back

| Field | Type |
| --- | --- |
| `ok` Always true | true |

Try it answered in this tab

curl

```bash
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"]}'
```

TypeScript

```typescript
import { sandbox } from "@intentic/sandbox-client";

const result = await sandbox.git.unstage({
 "repo": "root",
 "paths": [
 "src/app.ts",
 "README.md"
 ]
});
```

**GET`/git/{repo}/branches` Local 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 |
| --- | --- | --- |
| `repo` required Which repository | string | address |

### What comes back

| Field | Type |
| --- | --- |
| `branches` Branches in this repository | object[] |
| `name` The branch name | string |
| `current` Whether this is the one checked… | boolean |
| `upstream` The branch on the remote it… | string |
| `ahead` Commits this branch has that its… | number |
| `behind` Commits its remote counterpart has that… | number |
| `gone` The branch it followed no longer… | boolean |
| `at` When its tip was committed, in… | number |
| `remotes` Branches on its remotes, as last… | object[] |
| `name` The full name, such as origin/main | string |
| `remote` Just the remote part, so a… | string |
| `branch` Just the branch part | string |
| `at` When its tip was committed, in… | number |

Try it answered in this tab

curl

```bash
curl "$SANDBOX/git/root/branches" \
 -H "x-intentic-control: $INTENTIC_TOKEN"
```

TypeScript

```typescript
import { sandbox } from "@intentic/sandbox-client";

const result = await sandbox.git.branches({
 "repo": "root"
});
```

**POST`/git/{repo}/branches` Create 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 |
| --- | --- | --- |
| `repo` required Which repository | string | address |
| `name` required The new branch's name | string | body |
| `start` Where to start it: a commit… | string | body |
| `checkout` Switch to it as well as… | boolean | body |

### What comes back

| Field | Type |
| --- | --- |
| `ok` Always true | true |

Try it answered in this tab

curl

```bash
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}'
```

TypeScript

```typescript
import { sandbox } from "@intentic/sandbox-client";

const result = await sandbox.git.createBranchAt({
 "repo": "root",
 "name": "nightly changelog",
 "start": "…",
 "checkout": true
});
```

**POST`/git/{repo}/branches/delete` Delete 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 |
| --- | --- | --- |
| `repo` required Which repository | string | address |
| `name` required The branch to delete | string | body |
| `force` Delete it even though it holds… | boolean | body |

### What comes back

| Field | Type |
| --- | --- |
| `ok` Always true | true |

Try it answered in this tab

curl

```bash
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}'
```

TypeScript

```typescript
import { sandbox } from "@intentic/sandbox-client";

const result = await sandbox.git.deleteBranch({
 "repo": "root",
 "name": "nightly changelog",
 "force": false
});
```

**GET`/git/{repo}/remote` Sync 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 |
| --- | --- | --- |
| `repo` required Which repository | string | address |

### What comes back

| Field | Type |
| --- | --- |
| `remote` The remote this branch pushes to | string |
| `branch` The checked-out branch | string |
| `upstream` The branch on the remote this… | string |
| `ahead` Commits you have that the remote… | number |
| `behind` Commits the remote has that you… | number |

Try it answered in this tab

curl

```bash
curl "$SANDBOX/git/root/remote" \
 -H "x-intentic-control: $INTENTIC_TOKEN"
```

TypeScript

```typescript
import { sandbox } from "@intentic/sandbox-client";

const result = await sandbox.git.remote({
 "repo": "root"
});
```

**POST`/git/{repo}/fetch` Refresh 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 |
| --- | --- | --- |
| `repo` required Which repository | string | address |

### What comes back

| Field | Type |
| --- | --- |
| `ok` Whether it worked | boolean |
| `reason` Why not, in git's own words | string |

Try it answered in this tab

curl

```bash
curl -X POST "$SANDBOX/git/root/fetch" \
 -H "x-intentic-control: $INTENTIC_TOKEN"
```

TypeScript

```typescript
import { sandbox } from "@intentic/sandbox-client";

const result = await sandbox.git.fetch({
 "repo": "root"
});
```

**POST`/git/{repo}/pull` Bring 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 |
| --- | --- | --- |
| `repo` required Which repository | string | address |

### What comes back

| Field | Type |
| --- | --- |
| `ok` Whether it worked | boolean |
| `reason` Why not, in git's own words | string |

Try it answered in this tab

curl

```bash
curl -X POST "$SANDBOX/git/root/pull" \
 -H "x-intentic-control: $INTENTIC_TOKEN"
```

TypeScript

```typescript
import { sandbox } from "@intentic/sandbox-client";

const result = await sandbox.git.pull({
 "repo": "root"
});
```

**POST`/git/{repo}/push` Send commits to the remote**

Pushes the current branch, setting its upstream on first push. A rejected push, a missing remote and missing credentials all come back as reported outcomes rather than failures.

### What you send

| Field | Type | Where |
| --- | --- | --- |
| `repo` required Which repository | string | address |
| `branch` Which branch to push | string | body |

### What comes back

| Field | Type |
| --- | --- |
| `ok` Whether it worked | boolean |
| `reason` Why not, in git's own words | string |

Try it answered in this tab

curl

```bash
curl -X POST "$SANDBOX/git/root/push" \
 -H "x-intentic-control: $INTENTIC_TOKEN" \
 -H "content-type: application/json" \
 -d '{"branch":"main"}'
```

TypeScript

```typescript
import { sandbox } from "@intentic/sandbox-client";

const result = await sandbox.git.push({
 "repo": "root",
 "branch": "main"
});
```

**GET`/git/{repo}/files` Every 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 |
| --- | --- | --- |
| `repo` required Which repository | string | address |

### What comes back

| Field | Type |
| --- | --- |
| `files` Every path git tracks, relative to… | string[] |

Try it answered in this tab

curl

```bash
curl "$SANDBOX/git/root/files" \
 -H "x-intentic-control: $INTENTIC_TOKEN"
```

TypeScript

```typescript
import { sandbox } from "@intentic/sandbox-client";

const result = await sandbox.git.files({
 "repo": "root"
});
```

**GET`/git/{repo}/file` Read 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 |
| --- | --- | --- |
| `repo` required Which repository | string | address |
| `path` required The file to read, relative to… | string | query |

### What comes back

| Field | Type |
| --- | --- |
| `path` The path, as asked for | string |
| `content` The file's contents as they stand… | string |

Try it answered in this tab

curl

```bash
curl "$SANDBOX/git/root/file?path=src%2Fapp.ts" \
 -H "x-intentic-control: $INTENTIC_TOKEN"
```

TypeScript

```typescript
import { sandbox } from "@intentic/sandbox-client";

const result = await sandbox.git.readFile({
 "repo": "root",
 "path": "src/app.ts"
});
```

**PUT`/git/{repo}/file` Write 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 |
| --- | --- | --- |
| `repo` required Which repository | string | address |
| `path` required Where to write, relative to the… | string | body |
| `content` required The file's whole new contents | string | body |

### What comes back

| Field | Type |
| --- | --- |
| `ok` Always true | true |

Try it answered in this tab

curl

```bash
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"}'
```

TypeScript

```typescript
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-file` Write, 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 |
| --- | --- | --- |
| `repo` required Which repository | string | address |
| `path` required Which file, relative to the repository | string | body |
| `content` required Its whole new contents | string | body |
| `message` required The commit message | string | body |

### What comes back

| Field | Type |
| --- | --- |
| `ok` Whether the whole thing went through | boolean |
| `wrote` The file was written | boolean |
| `committed` The commit was recorded | boolean |
| `pushed` It reached the remote | boolean |
| `branch` Which branch it happened on | string |
| `defaultBranch` Which branch the repository considers its… | string |
| `reason` Why it stopped where it did | string |

Try it answered in this tab

curl

```bash
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"}'
```

TypeScript

```typescript
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"
});
```

More in The workspace

[Previous ← Workspace](https://intentic.dev/api/workspace/)[Next History →](https://intentic.dev/api/history/)
