# Git Operations

Git operations are available through the Git module of a sandbox. Operations run through the Daytona API, so your application works with repositories in a sandbox directly, without executing shell commands inside it. The sandbox image must include Git.

The Git module covers cloning and initializing repositories, checking status, managing branches and remotes, staging, committing, resetting and restoring changes, pushing and pulling with authentication, inspecting commit history, and reading and writing Git configuration. Private repositories authenticate with personal access tokens passed per operation. Clone, push, and pull accept a per-call timeout; the default is 30 minutes.

## Use cases

Git operations are used when a sandbox works on code that lives in a Git repository.

- **Coding agents**: an agent clones a repository, changes code, commits, and pushes a branch for review. Every step runs through the API, with no shell commands in the sandbox.
- **Branch checks**: a sandbox checks out a branch and runs its tests in isolation. Each branch gets its own sandbox, so one run does not affect another.
- **Private repositories**: a username and a personal access token passed with each clone, push, or pull authenticate against private repositories. The credentials are not written to the repository configuration or the remote URL.
- **Change inspection**: status, branches, and commit history are read without a shell, for example to show an agent's changes before a commit.
- **Rollback of failed attempts**: reset and restore discard uncommitted changes or unstage files. An agent returns the repository to a known commit and tries again.
- **Commit attribution**: each commit made through the Git module takes its author name and email from the call. An application attributes each commit to the agent or to the user the agent works for.
- **New projects**: a repository is initialized in the sandbox, committed, and pushed to a new remote.

## Clone repositories

Clone a repository into a sandbox directory. A relative destination resolves against the daemon working directory: the image's, or the default user's home when the image declares none.

You can clone a specific branch or a specific commit. A commit takes precedence over a branch and leaves the repository on a detached HEAD at that commit. If a clone times out, Daytona removes the partly cloned destination, unless the destination existed before the call.


```python
from daytona import Daytona

daytona = Daytona()
sandbox = daytona.v2.create()

sandbox.git.clone(url="https://github.com/user/repo.git", path="workspace/repo")
```


```typescript
import { Daytona } from '@daytona/sdk';

const daytona = new Daytona();
const sandbox = await daytona.v2.create();

await sandbox.git.clone('https://github.com/user/repo.git', { path: 'workspace/repo' });
```


```bash
curl 'https://toolbox.daytona.com/toolbox/{sandboxId}/git/clone' \
  --request POST \
  --header 'Content-Type: application/json' \
  --header 'Authorization: Bearer YOUR_API_KEY' \
  --data '{
  "url": "https://github.com/user/repo.git",
  "path": "workspace/repo"
}'
```

| **Field** | **Type** | **Description**                          |
| --------- | -------- | ---------------------------------------- |
| **`url`**     | **`string`** | Sets the URL of the repository to clone. This field is required. A missing value returns **`400`**. |
| **`path`**    | **`string`** | Sets the destination directory in the sandbox. This field is required. A missing value returns **`400`**. |


## Get repository status

Get the status of a repository. The status reports the current branch, whether it has an upstream branch, how many commits it is ahead of and behind that upstream, and the state of each changed or untracked file in the index and the working tree. The ahead and behind counts are zero when the branch has no upstream. A renamed file also carries its original path.


```python
from daytona import Daytona

daytona = Daytona()
sandbox = daytona.v2.create()
sandbox.git.clone(url="https://github.com/user/repo.git", path="workspace/repo")

status = sandbox.git.status("workspace/repo")
print(f"Current branch: {status.current_branch}")  # "HEAD" when detached
print(f"Commits ahead: {status.ahead}, behind: {status.behind}")

# staging and worktree hold the porcelain status codes
# (M, A, D, R, ?, ...) or a space when the file is unchanged there.
for file in status.file_status or []:
    print(f"{file.staging}{file.worktree} {file.name}")
```


```typescript
import { Daytona } from '@daytona/sdk';

const daytona = new Daytona();
const sandbox = await daytona.v2.create();
await sandbox.git.clone('https://github.com/user/repo.git', { path: 'workspace/repo' });

const status = await sandbox.git.status('workspace/repo');
console.log(`Current branch: ${status.currentBranch}`); // "HEAD" when detached
console.log(`Commits ahead: ${status.ahead}, behind: ${status.behind}`);

// staging and worktree hold the porcelain status codes
// (M, A, D, R, ?, ...) or a space when the file is unchanged there.
status.fileStatus?.forEach(file => {
  console.log(`${file.staging}${file.worktree} ${file.name}`);
});
```


```bash
curl 'https://toolbox.daytona.com/toolbox/{sandboxId}/git/status?path=workspace/repo' \
  --header 'Authorization: Bearer YOUR_API_KEY'
```

```json
{
  "currentBranch": "main",
  "branchPublished": true,
  "ahead": 1,
  "behind": 0,
  "fileStatus": [
    { "name": "src/main.py", "extra": "", "staging": "M", "worktree": " " },
    { "name": "docs/new.md", "extra": "docs/old.md", "staging": "R", "worktree": " " },
    { "name": "notes.txt", "extra": "", "staging": "?", "worktree": "?" }
  ]
}
```

The response has the following fields:

| **Field** | **Description** |
| --- | --- |
| **`branchPublished`** | The value is **`true`** when the current branch has an upstream branch. |
| **`extra`** | Holds the original path of a renamed file. The value is empty for other files. |


## Branch operations

### List branches

List a repository's local branches, by name only; remote-tracking branches are excluded.


```python
from daytona import Daytona

daytona = Daytona()
sandbox = daytona.v2.create()
sandbox.git.clone(url="https://github.com/user/repo.git", path="workspace/repo")

response = sandbox.git.list_branches("workspace/repo")
for branch in response.items:
    print(branch)
```


```typescript
import { Daytona } from '@daytona/sdk';

const daytona = new Daytona();
const sandbox = await daytona.v2.create();
await sandbox.git.clone('https://github.com/user/repo.git', { path: 'workspace/repo' });

const response = await sandbox.git.listBranches('workspace/repo');
response.items.forEach(branch => console.log(branch));
```


```bash
curl 'https://toolbox.daytona.com/toolbox/{sandboxId}/git/branches?path=workspace/repo' \
  --header 'Authorization: Bearer YOUR_API_KEY'
```


### Create branches

Create a branch. The new branch starts at the current commit, and the repository stays on the branch it was on. To switch to the new branch, [check it out](#checkout-branches).


```python
from daytona import Daytona

daytona = Daytona()
sandbox = daytona.v2.create()
sandbox.git.clone(url="https://github.com/user/repo.git", path="workspace/repo")

# Create a new branch
sandbox.git.create_branch("workspace/repo", "new-feature")
```


```typescript
import { Daytona } from '@daytona/sdk';

const daytona = new Daytona();
const sandbox = await daytona.v2.create();
await sandbox.git.clone('https://github.com/user/repo.git', { path: 'workspace/repo' });

// Create new branch
await sandbox.git.createBranch('workspace/repo', 'new-feature');
```


```bash
curl 'https://toolbox.daytona.com/toolbox/{sandboxId}/git/branches' \
  --request POST \
  --header 'Content-Type: application/json' \
  --header 'Authorization: Bearer YOUR_API_KEY' \
  --data '{
  "path": "workspace/repo",
  "name": "new-feature"
}'
```


### Checkout branches

Check out a branch. Checkout switches the working tree to the named branch. It does not create a branch from scratch; to start a new branch, [create it](#create-branches) first.


```python
from daytona import Daytona

daytona = Daytona()
sandbox = daytona.v2.create()
sandbox.git.clone(url="https://github.com/user/repo.git", path="workspace/repo")
sandbox.git.create_branch("workspace/repo", "feature-branch")

# Checkout a branch
sandbox.git.checkout_branch("workspace/repo", "feature-branch")
```


```typescript
import { Daytona } from '@daytona/sdk';

const daytona = new Daytona();
const sandbox = await daytona.v2.create();
await sandbox.git.clone('https://github.com/user/repo.git', { path: 'workspace/repo' });
await sandbox.git.createBranch('workspace/repo', 'feature-branch');

// Checkout a branch
await sandbox.git.checkoutBranch('workspace/repo', 'feature-branch');
```


```bash
curl 'https://toolbox.daytona.com/toolbox/{sandboxId}/git/checkout' \
  --request POST \
  --header 'Content-Type: application/json' \
  --header 'Authorization: Bearer YOUR_API_KEY' \
  --data '{
  "path": "workspace/repo",
  "branch": "feature-branch"
}'
```


### Delete branches

Delete a branch. Deletion is forced, so unmerged commits do not block it.


```python
from daytona import Daytona

daytona = Daytona()
sandbox = daytona.v2.create()
sandbox.git.clone(url="https://github.com/user/repo.git", path="workspace/repo")
sandbox.git.create_branch("workspace/repo", "old-feature")

# Delete a branch
sandbox.git.delete_branch("workspace/repo", "old-feature")
```


```typescript
import { Daytona } from '@daytona/sdk';

const daytona = new Daytona();
const sandbox = await daytona.v2.create();
await sandbox.git.clone('https://github.com/user/repo.git', { path: 'workspace/repo' });
await sandbox.git.createBranch('workspace/repo', 'old-feature');

// Delete a branch
await sandbox.git.deleteBranch('workspace/repo', 'old-feature');
```


```bash
curl 'https://toolbox.daytona.com/toolbox/{sandboxId}/git/branches' \
  --request DELETE \
  --header 'Content-Type: application/json' \
  --header 'Authorization: Bearer YOUR_API_KEY' \
  --data '{
  "path": "workspace/repo",
  "name": "old-feature"
}'
```


## Stage changes

Stage at least one file for the next commit. Paths are relative to the repository root.


```python
from daytona import Daytona

daytona = Daytona()
sandbox = daytona.v2.create()
sandbox.git.clone(url="https://github.com/user/repo.git", path="workspace/repo")
sandbox.fs.write_text("workspace/repo/README.md", "# Updated\n")

sandbox.git.add("workspace/repo", ["README.md"])
```


```typescript
import { Daytona } from '@daytona/sdk';

const daytona = new Daytona();
const sandbox = await daytona.v2.create();
await sandbox.git.clone('https://github.com/user/repo.git', { path: 'workspace/repo' });
await sandbox.fs.writeText('workspace/repo/README.md', '# Updated\n');

await sandbox.git.add('workspace/repo', ['README.md']);
```


```bash
curl 'https://toolbox.daytona.com/toolbox/{sandboxId}/git/add' \
  --request POST \
  --header 'Content-Type: application/json' \
  --header 'Authorization: Bearer YOUR_API_KEY' \
  --data '{
  "path": "workspace/repo",
  "files": ["README.md"]
}'
```


## Commit changes

Commit the staged changes. Every commit requires an author name and email. They apply to that commit only and are not written to the repository configuration, so the identity from [configure user](#configure-user) does not replace them. A commit with nothing staged fails.

Repository hooks do not run for commits or for any other operation of the Git module.


```python
from daytona import Daytona

daytona = Daytona()
sandbox = daytona.v2.create()
sandbox.git.clone(url="https://github.com/user/repo.git", path="workspace/repo")
sandbox.fs.write_text("workspace/repo/README.md", "# Updated\n")

# Stage and commit changes
sandbox.git.add("workspace/repo", ["README.md"])
commit = sandbox.git.commit(
    "workspace/repo",
    "Update documentation",
    author="John Doe",
    email="john@example.com",
)
print(f"Commit hash: {commit.hash}")
```

| **Parameter** | **Type** | **Description**                                                               |
| ------------- | -------- | ----------------------------------------------------------------------------- |
| **`author`**      | **`str`**    | Sets the author name of the commit. This parameter is required. An empty value raises **`BadRequestError`** before the request is sent. |
| **`email`**       | **`str`**    | Sets the author email of the commit. This parameter is required. An empty value raises **`BadRequestError`** before the request is sent. |

The response has the following fields:

| **Field** | **Description** |
| --- | --- |
| **`hash`** | Holds the full SHA of the new commit. |


```typescript
import { Daytona } from '@daytona/sdk';

const daytona = new Daytona();
const sandbox = await daytona.v2.create();
await sandbox.git.clone('https://github.com/user/repo.git', { path: 'workspace/repo' });
await sandbox.fs.writeText('workspace/repo/README.md', '# Updated\n');

// Stage and commit changes
await sandbox.git.add('workspace/repo', ['README.md']);
const commit = await sandbox.git.commit(
  'workspace/repo',
  'Update documentation',
  { author: 'John Doe', email: 'john@example.com' }
);
console.log(`Commit hash: ${commit.hash}`);
```

| **Parameter** | **Type** | **Description**                                                                                |
| ------------- | -------- | ---------------------------------------------------------------------------------------------- |
| **`author`**      | **`string`** | Sets the author name of the commit. This parameter is required. An empty value throws a **`DaytonaError`** with status **`400`** before the request is sent. |
| **`email`**       | **`string`** | Sets the author email of the commit. This parameter is required. An empty value throws a **`DaytonaError`** with status **`400`** before the request is sent. |

The response has the following fields:

| **Field** | **Description** |
| --- | --- |
| **`hash`** | Holds the full SHA of the new commit. |


```bash
curl 'https://toolbox.daytona.com/toolbox/{sandboxId}/git/commit' \
  --request POST \
  --header 'Content-Type: application/json' \
  --header 'Authorization: Bearer YOUR_API_KEY' \
  --data '{
  "path": "workspace/repo",
  "message": "Update documentation",
  "author": "John Doe",
  "email": "john@example.com"
}'
```

| **Field** | **Type** | **Description**                         |
| --------- | -------- | --------------------------------------- |
| **`message`** | **`string`** | Sets the commit message. This field is required. An empty value returns **`400`**. |
| **`author`**  | **`string`** | Sets the author name of the commit. This field is required. An empty value returns **`400`**. |
| **`email`**   | **`string`** | Sets the author email of the commit. This field is required. An empty value returns **`400`**. |

The response has the following fields:

| **Field** | **Description** |
| --- | --- |
| **`hash`** | Holds the full SHA of the new commit. |


## Remote operations

### Push changes

Push the current branch to the remote named origin. The branch is pushed to the branch of the same name on the remote, so a new local branch does not need an upstream configured first. Push fails when the repository is on a detached HEAD, and when the remote branch has commits that the local branch does not have. For private repositories, see [authenticate credentials](#authenticate-credentials).


```python
sandbox.git.push("workspace/repo")
```


```typescript
await sandbox.git.push('workspace/repo');
```


```bash
curl 'https://toolbox.daytona.com/toolbox/{sandboxId}/git/push' \
  --request POST \
  --header 'Content-Type: application/json' \
  --header 'Authorization: Bearer YOUR_API_KEY' \
  --data '{ "path": "workspace/repo" }'
```


### Pull changes

Pull changes into the current branch from its upstream branch. Pull only fast-forwards: when the local and remote branches have diverged, pull fails with a conflict instead of creating a merge commit. For private repositories, see [authenticate credentials](#authenticate-credentials).


```python
from daytona import Daytona

daytona = Daytona()
sandbox = daytona.v2.create()
sandbox.git.clone(url="https://github.com/user/repo.git", path="workspace/repo")

sandbox.git.pull("workspace/repo")
```


```typescript
import { Daytona } from '@daytona/sdk';

const daytona = new Daytona();
const sandbox = await daytona.v2.create();
await sandbox.git.clone('https://github.com/user/repo.git', { path: 'workspace/repo' });

await sandbox.git.pull('workspace/repo');
```


```bash
curl 'https://toolbox.daytona.com/toolbox/{sandboxId}/git/pull' \
  --request POST \
  --header 'Content-Type: application/json' \
  --header 'Authorization: Bearer YOUR_API_KEY' \
  --data '{ "path": "workspace/repo" }'
```


## Initialize a repository

Initialize a new repository at a path, optionally bare and with a named initial branch.


```python
from daytona import Daytona

daytona = Daytona()
sandbox = daytona.v2.create()

sandbox.git.init("workspace/repo")
```


```typescript
import { Daytona } from '@daytona/sdk';

const daytona = new Daytona();
const sandbox = await daytona.v2.create();

await sandbox.git.init('workspace/repo');
```


```bash
curl 'https://toolbox.daytona.com/toolbox/{sandboxId}/git/init' \
  --request POST \
  --header 'Content-Type: application/json' \
  --header 'Authorization: Bearer YOUR_API_KEY' \
  --data '{ "path": "workspace/repo" }'
```


## Reset changes

Reset the current branch to a commit. The mode also controls index and working tree resets. The target defaults to the current commit, so resetting only files unstages them.


```python
from daytona import Daytona

daytona = Daytona()
sandbox = daytona.v2.create()
sandbox.git.clone(url="https://github.com/user/repo.git", path="workspace/repo")

# Reset HEAD and the index to the previous commit, keep the working tree
sandbox.git.reset("workspace/repo", target="HEAD~1")
```

| **Parameter** | **Type**        | **Description**                                                     |
| ------------- | --------------- | ------------------------------------------------------------------- |
| **`mode`**        | **`str`**           | Sets the reset mode: **`mixed`**, **`soft`**, **`hard`**, **`merge`**, or **`keep`**. The default is **`mixed`**. |
| **`target`**      | **`str`**           | Sets the commit to reset to. The default is the current commit. |
| **`files`**       | **`Sequence[str]`** | Sets the files to reset. Files are accepted only in **`mixed`** mode. Other modes reject files with **`400`**. |


```typescript
import { Daytona } from '@daytona/sdk';

const daytona = new Daytona();
const sandbox = await daytona.v2.create();
await sandbox.git.clone('https://github.com/user/repo.git', { path: 'workspace/repo' });

// Reset HEAD and the index to the previous commit, keep the working tree
await sandbox.git.reset('workspace/repo', { target: 'HEAD~1' });
```

| **Parameter** | **Type**   | **Description**                                                            |
| ------------- | ---------- | -------------------------------------------------------------------------- |
| **`mode`**        | **`string`**   | Sets the reset mode: **`'mixed'`**, **`'soft'`**, **`'hard'`**, **`'merge'`**, or **`'keep'`**. The default is **`'mixed'`**. |
| **`target`**      | **`string`**   | Sets the commit to reset to. The default is the current commit. |
| **`files`**       | **`string[]`** | Sets the files to reset. Files are accepted only in **`'mixed'`** mode. Other modes reject files with **`400`**. |


```bash
curl 'https://toolbox.daytona.com/toolbox/{sandboxId}/git/reset' \
  --request POST \
  --header 'Content-Type: application/json' \
  --header 'Authorization: Bearer YOUR_API_KEY' \
  --data '{
  "path": "workspace/repo",
  "target": "HEAD~1"
}'
```

| **Field** | **Type**   | **Description**                                                     |
| --------- | ---------- | ------------------------------------------------------------------- |
| **`mode`**    | **`string`**   | Sets the reset mode: **`mixed`**, **`soft`**, **`hard`**, **`merge`**, or **`keep`**. The default is **`mixed`**. |
| **`target`**  | **`string`**   | Sets the commit to reset to. The default is the current commit. |
| **`files`**   | **`string[]`** | Sets the files to reset. Files are accepted only in **`mixed`** mode. Other modes reject files with **`400`**. |


## Restore files

Restore files in the working tree, the index, or both; at least one file is required. Restoring the working tree discards uncommitted changes; restoring the index unstages them.


```python
from daytona import Daytona

daytona = Daytona()
sandbox = daytona.v2.create()
sandbox.git.clone(url="https://github.com/user/repo.git", path="workspace/repo")
sandbox.fs.write_text("workspace/repo/README.md", "# Updated\n")

# Restore a file in the working tree
sandbox.git.restore("workspace/repo", ["README.md"])
```

| **Parameter** | **Type**        | **Description**                                                                                        |
| ------------- | --------------- | ------------------------------------------------------------------------------------------------------ |
| **`files`**       | **`Sequence[str]`** | Sets the files to restore. This parameter is required and must list at least one file. |
| **`staged`**      | **`bool`**          | Restores the index entry.                                                                              |
| **`worktree`**    | **`bool`**          | Restores the working tree file. This is the default when neither **`staged`** nor **`worktree`** is set. |
| **`source`**      | **`str`**           | Sets the commit, branch, or tag to restore from. By default, the working tree restores from the index, and the index restores from **`HEAD`**. |


```typescript
import { Daytona } from '@daytona/sdk';

const daytona = new Daytona();
const sandbox = await daytona.v2.create();
await sandbox.git.clone('https://github.com/user/repo.git', { path: 'workspace/repo' });
await sandbox.fs.writeText('workspace/repo/README.md', '# Updated\n');

// Restore a file in the working tree
await sandbox.git.restore('workspace/repo', ['README.md']);
```

| **Parameter** | **Type**   | **Description**                                                                                        |
| ------------- | ---------- | ------------------------------------------------------------------------------------------------------ |
| **`files`**       | **`string[]`** | Sets the files to restore. This parameter is required and must list at least one file. |
| **`staged`**      | **`boolean`**  | Restores the index entry.                                                                              |
| **`worktree`**    | **`boolean`**  | Restores the working tree file. This is the default when neither **`staged`** nor **`worktree`** is set. |
| **`source`**      | **`string`**   | Sets the commit, branch, or tag to restore from. By default, the working tree restores from the index, and the index restores from **`HEAD`**. |


```bash
curl 'https://toolbox.daytona.com/toolbox/{sandboxId}/git/restore' \
  --request POST \
  --header 'Content-Type: application/json' \
  --header 'Authorization: Bearer YOUR_API_KEY' \
  --data '{
  "path": "workspace/repo",
  "files": ["src/main.py"]
}'
```

| **Field**  | **Type**   | **Description**                                                                                        |
| ---------- | ---------- | ------------------------------------------------------------------------------------------------------ |
| **`files`**    | **`string[]`** | Sets the files to restore. This parameter is required and must list at least one file. |
| **`staged`**   | **`boolean`**  | Restores the index entry.                                                                              |
| **`worktree`** | **`boolean`**  | Restores the working tree file. This is the default when neither **`staged`** nor **`worktree`** is set. |
| **`source`**   | **`string`**   | Sets the commit, branch, or tag to restore from. By default, the working tree restores from the index, and the index restores from **`HEAD`**. |


## Get commit history

Get the commit history of the current branch: every commit reachable from the current commit, newest first, in one response. Each entry carries the full commit hash, the first line of the message, the author name and email, and the author timestamp in RFC 3339 format.


```python
from daytona import Daytona

daytona = Daytona()
sandbox = daytona.v2.create()
sandbox.git.clone(url="https://github.com/user/repo.git", path="workspace/repo")

# Commits are returned newest first
history = sandbox.git.history("workspace/repo")
for commit in history.items:
    print(f"{commit['hash']}: {commit['message']}")
    print(f"  {commit['author']} <{commit['email']}> at {commit['timestamp']}")
```


```typescript
import { Daytona } from '@daytona/sdk';

const daytona = new Daytona();
const sandbox = await daytona.v2.create();
await sandbox.git.clone('https://github.com/user/repo.git', { path: 'workspace/repo' });

// Commits are returned newest first
const history = await sandbox.git.history('workspace/repo');
history.items.forEach(commit => {
  console.log(`${commit.hash}: ${commit.message}`);
  console.log(`  ${commit.author} <${commit.email}> at ${commit.timestamp}`);
});
```


```bash
curl 'https://toolbox.daytona.com/toolbox/{sandboxId}/git/history?path=workspace/repo' \
  --header 'Authorization: Bearer YOUR_API_KEY'
```

```json
[
  {
    "hash": "3f2a9c1e8b7d6a5f4e3d2c1b0a9f8e7d6c5b4a39",
    "message": "Update documentation",
    "author": "John Doe",
    "email": "john@example.com",
    "timestamp": "2026-09-13T09:41:12+00:00"
  }
]
```


## Manage remotes

Add a remote to a repository and list the configured remotes. Listed URLs mask embedded credentials with asterisks. [Push](#push-changes) always targets the remote named origin.


```python
from daytona import Daytona

daytona = Daytona()
sandbox = daytona.v2.create()
sandbox.git.init("workspace/repo")

# Add a remote
sandbox.git.add_remote("workspace/repo", "origin", "https://github.com/user/repo.git")

# List remotes
remotes = sandbox.git.list_remotes("workspace/repo")
for remote in remotes.items:
    print(f"{remote['name']}: {remote['url']}")
```


```typescript
import { Daytona } from '@daytona/sdk';

const daytona = new Daytona();
const sandbox = await daytona.v2.create();
await sandbox.git.init('workspace/repo');

// Add a remote
await sandbox.git.addRemote('workspace/repo', 'origin', 'https://github.com/user/repo.git');

// List remotes
const remotes = await sandbox.git.listRemotes('workspace/repo');
remotes.items.forEach(remote => {
  console.log(`${remote.name}: ${remote.url}`);
});
```


```bash
# Add a remote
curl 'https://toolbox.daytona.com/toolbox/{sandboxId}/git/remotes' \
  --request POST \
  --header 'Content-Type: application/json' \
  --header 'Authorization: Bearer YOUR_API_KEY' \
  --data '{
  "name": "origin",
  "path": "workspace/repo",
  "url": "https://github.com/user/repo.git"
}'

# List remotes
curl 'https://toolbox.daytona.com/toolbox/{sandboxId}/git/remotes?path=workspace/repo' \
  --header 'Authorization: Bearer YOUR_API_KEY'
```


## Configure Git

Set and get Git configuration values. Global values apply to every repository for the user that Git runs as in the sandbox: the sandbox default user, or root when no default user is set. Local values apply to one repository. A key must contain a section and a name separated by a dot. Getting a key that is not set returns no value instead of an error.


```python
from daytona import Daytona

daytona = Daytona()
sandbox = daytona.v2.create()

# Set a config value at the global scope
sandbox.git.set_config("core.editor", "vim")

# Get a config value (None when unset)
editor = sandbox.git.get_config("core.editor")
```

| **Parameter** | **Type** | **Description**                                           |
| ------------- | -------- | --------------------------------------------------------- |
| **`scope`**       | **`str`**    | Sets the configuration scope, **`global`** or **`local`**. The default is **`global`**. |
| **`path`**        | **`str`**    | Sets the repository path. It is required when the scope is **`local`** and ignored for **`global`**. |


```typescript
import { Daytona } from '@daytona/sdk';

const daytona = new Daytona();
const sandbox = await daytona.v2.create();

// Set a config value at the global scope
await sandbox.git.setConfig('core.editor', 'vim');

// Get a config value (null when unset)
const editor = await sandbox.git.getConfig('core.editor');
```

| **Parameter** | **Type**         | **Description**                                               |
| ------------- | ---------------- | ------------------------------------------------------------- |
| **`scope`**       | **`GitConfigScope`** | Sets the configuration scope, **`'global'`** or **`'local'`**. The default is **`'global'`**. |
| **`path`**        | **`string`**         | Sets the repository path. It is required when the scope is **`'local'`** and ignored for **`'global'`**. |


```bash
# Set a config value
curl 'https://toolbox.daytona.com/toolbox/{sandboxId}/git/config' \
  --request POST \
  --header 'Content-Type: application/json' \
  --header 'Authorization: Bearer YOUR_API_KEY' \
  --data '{
  "key": "core.editor",
  "value": "vim",
  "scope": "global"
}'

# Get a config value
curl 'https://toolbox.daytona.com/toolbox/{sandboxId}/git/config?key=core.editor&scope=global' \
  --header 'Authorization: Bearer YOUR_API_KEY'
```

| **Field** | **Type** | **Description**                                           |
| --------- | -------- | --------------------------------------------------------- |
| **`scope`**   | **`string`** | Sets the configuration scope, **`global`** or **`local`**. The default is **`global`**. |
| **`path`**    | **`string`** | Sets the repository path. It is required when the scope is **`local`** and ignored for **`global`**. |

The response has the following fields:

| **Field** | **Description** |
| --- | --- |
| **`value`** | Holds the configuration value that a get returns. The value is **`null`** when the key is not set. |


## Configure user

Set the user name and email that Git records in commits made with Git commands inside the sandbox, such as commits from processes you run. Commits made through the Git module take the author name and email from each [commit](#commit-changes) call instead.


```python
from daytona import Daytona

daytona = Daytona()
sandbox = daytona.v2.create()

# Configure the global Git user
sandbox.git.set_config("user.name", "John Doe")
sandbox.git.set_config("user.email", "john@example.com")
```

| **Parameter** | **Type** | **Description**                                           |
| ------------- | -------- | --------------------------------------------------------- |
| **`scope`**       | **`str`**    | Sets the configuration scope, **`global`** or **`local`**. The default is **`global`**. |
| **`path`**        | **`str`**    | Sets the repository path. It is required when the scope is **`local`** and ignored for **`global`**. |


```typescript
import { Daytona } from '@daytona/sdk';

const daytona = new Daytona();
const sandbox = await daytona.v2.create();

// Configure the global Git user
await sandbox.git.setConfig('user.name', 'John Doe');
await sandbox.git.setConfig('user.email', 'john@example.com');
```

| **Parameter** | **Type**         | **Description**                                               |
| ------------- | ---------------- | ------------------------------------------------------------- |
| **`scope`**       | **`GitConfigScope`** | Sets the configuration scope, **`'global'`** or **`'local'`**. The default is **`'global'`**. |
| **`path`**        | **`string`**         | Sets the repository path. It is required when the scope is **`'local'`** and ignored for **`'global'`**. |


```bash
curl 'https://toolbox.daytona.com/toolbox/{sandboxId}/git/config' \
  --request POST \
  --header 'Content-Type: application/json' \
  --header 'Authorization: Bearer YOUR_API_KEY' \
  --data '{
  "key": "user.name",
  "value": "John Doe",
  "scope": "global"
}'

curl 'https://toolbox.daytona.com/toolbox/{sandboxId}/git/config' \
  --request POST \
  --header 'Content-Type: application/json' \
  --header 'Authorization: Bearer YOUR_API_KEY' \
  --data '{
  "key": "user.email",
  "value": "john@example.com",
  "scope": "global"
}'
```

| **Field** | **Type** | **Description**                                           |
| --------- | -------- | --------------------------------------------------------- |
| **`scope`**   | **`string`** | Sets the configuration scope, **`global`** or **`local`**. The default is **`global`**. |
| **`path`**    | **`string`** | Sets the repository path. It is required when the scope is **`local`** and ignored for **`global`**. |


## Authenticate credentials

Authenticate clone, push, and pull against private repositories with a username and a personal access token as the password. Credentials apply only to their call; they are not written to the repository configuration or remote URL and do not appear on the Git command line.

Credentials are used only when both a username and a password are set. Otherwise, Git runs without credentials and does not prompt, so operations on a private repository fail.


```python
from daytona import Daytona

daytona = Daytona()
sandbox = daytona.v2.create()
sandbox.git.clone(
    url="https://github.com/user/repo.git",
    path="workspace/repo",
    username="git",
    password="personal_access_token",
)

sandbox.git.push(
    "workspace/repo",
    username="git",
    password="personal_access_token"
)
```


```typescript
import { Daytona } from '@daytona/sdk';

const daytona = new Daytona();
const sandbox = await daytona.v2.create();
await sandbox.git.clone('https://github.com/user/repo.git', {
  path: 'workspace/repo',
  username: 'git',
  password: 'personal_access_token',
});

await sandbox.git.push('workspace/repo', {
  username: 'git',
  password: 'personal_access_token'
});
```


```bash
curl 'https://toolbox.daytona.com/toolbox/{sandboxId}/git/push' \
  --request POST \
  --header 'Content-Type: application/json' \
  --header 'Authorization: Bearer YOUR_API_KEY' \
  --data '{
  "path": "workspace/repo",
  "username": "git",
  "password": "personal_access_token"
}'
```