Skip to content

Git Operations

View as Markdown

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.

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 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.

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

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.

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}")

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

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)

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.

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")

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 first.

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")

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

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")

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

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"])

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 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.

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}")
ParameterTypeDescription
authorstrSets the author name of the commit. This parameter is required. An empty value raises BadRequestError before the request is sent.
emailstrSets 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:

FieldDescription
hashHolds the full SHA of the new commit.

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.

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

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.

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")

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

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

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.

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")
ParameterTypeDescription
modestrSets the reset mode: mixed, soft, hard, merge, or keep. The default is mixed.
targetstrSets the commit to reset to. The default is the current commit.
filesSequence[str]Sets the files to reset. Files are accepted only in mixed mode. Other modes reject files with 400.

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.

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"])
ParameterTypeDescription
filesSequence[str]Sets the files to restore. This parameter is required and must list at least one file.
stagedboolRestores the index entry.
worktreeboolRestores the working tree file. This is the default when neither staged nor worktree is set.
sourcestrSets the commit, branch, or tag to restore from. By default, the working tree restores from the index, and the index restores from HEAD.

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.

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

Add a remote to a repository and list the configured remotes. Listed URLs mask embedded credentials with asterisks. Push always targets the remote named origin.

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

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.

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")
ParameterTypeDescription
scopestrSets the configuration scope, global or local. The default is global.
pathstrSets the repository path. It is required when the scope is local and ignored for global.

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 call instead.

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")
ParameterTypeDescription
scopestrSets the configuration scope, global or local. The default is global.
pathstrSets the repository path. It is required when the scope is local and ignored for global.

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.

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"
)