> ## Documentation Index
> Fetch the complete documentation index at: https://mintlify.com/nullclaw/nullclaw/llms.txt
> Use this file to discover all available pages before exploring further.

# Git Operations

> Structured Git tool with command sanitization and workspace scoping

# Git Operations

The `git_operations` tool provides structured Git operations with comprehensive input sanitization and workspace scoping.

**Supported operations:**

* `status` — Show working tree status
* `diff` — Show file differences
* `log` — View commit history
* `branch` — List branches
* `commit` — Create commit
* `add` — Stage files
* `checkout` — Switch branches
* `stash` — Stash changes

**Security features:**

* Command injection prevention
* Workspace path scoping with `cwd` override
* Commit message truncation
* Branch name validation

***

## Parameters

<ParamField path="operation" type="string" required>
  Git operation: `status`, `diff`, `log`, `branch`, `commit`, `add`, `checkout`, or `stash`
</ParamField>

<ParamField path="message" type="string">
  Commit message (required for `commit` operation)
</ParamField>

<ParamField path="paths" type="string">
  File paths to stage (required for `add` operation)
</ParamField>

<ParamField path="branch" type="string">
  Branch name (required for `checkout` operation)
</ParamField>

<ParamField path="files" type="string">
  Files to diff (defaults to `.` for all files)
</ParamField>

<ParamField path="cached" type="boolean">
  Show staged changes for `diff` operation (default: false)
</ParamField>

<ParamField path="limit" type="integer">
  Number of log entries (default: 10, max: 1000)
</ParamField>

<ParamField path="cwd" type="string">
  Repository directory (absolute path within allowed paths; defaults to workspace)
</ParamField>

***

## Configuration

```zig theme={null}
const gt = try allocator.create(git.GitTool);
gt.* = .{
    .workspace_dir = "/path/to/workspace",
    .allowed_paths = &.{},  // Additional repo paths
};
```

***

## Operations

### status

Show working tree status in porcelain v2 format.

**Usage:**

```json theme={null}
{
  "tool": "git_operations",
  "operation": "status"
}
```

**Response:**

```
# branch.oid 7a3b8c1d2e4f5a6b7c8d9e0f1a2b3c4d5e6f7a8b
# branch.head main
# branch.upstream origin/main
# branch.ab +0 -0
1 .M N... 100644 100644 100644 da39a3e... da39a3e... src/main.zig
? untracked.txt
```

**Command:**

```bash theme={null}
git status --porcelain=2 --branch
```

***

### diff

Show file differences.

**Usage (unstaged changes):**

```json theme={null}
{
  "tool": "git_operations",
  "operation": "diff",
  "files": "src/main.zig"
}
```

**Response:**

```diff theme={null}
diff --git a/src/main.zig b/src/main.zig
index 1234567..abcdefg 100644
--- a/src/main.zig
+++ b/src/main.zig
@@ -1,3 +1,4 @@
 const std = @import("std");
+const config = @import("config.zig");
 
 pub fn main() !void {
```

**Usage (staged changes):**

```json theme={null}
{
  "tool": "git_operations",
  "operation": "diff",
  "cached": true
}
```

**Command:**

```bash theme={null}
git diff --unified=3 --cached -- .
```

***

### log

View commit history.

**Usage:**

```json theme={null}
{
  "tool": "git_operations",
  "operation": "log",
  "limit": 5
}
```

**Response:**

```
7a3b8c1d|Alice|alice@example.com|2026-03-01 12:00:00 -0800|Add git operations tool
6f5e4d3c|Bob|bob@example.com|2026-03-01 11:30:00 -0800|Update documentation
5e4d3c2b|Alice|alice@example.com|2026-02-28 16:45:00 -0800|Fix memory leak in shell tool
4d3c2b1a|Bob|bob@example.com|2026-02-28 14:20:00 -0800|Add browser tool tests
3c2b1a9e|Alice|alice@example.com|2026-02-28 10:15:00 -0800|Initial commit
```

**Format:** `hash|author|email|date|message`

**Command:**

```bash theme={null}
git log -5 --pretty=format:%H|%an|%ae|%ad|%s --date=iso
```

***

### branch

List branches with current branch indicator.

**Usage:**

```json theme={null}
{
  "tool": "git_operations",
  "operation": "branch"
}
```

**Response:**

```
main|*
feature-browser|
fix-memory-leak|
```

**Format:** `branch_name|*` (asterisk indicates current branch)

**Command:**

```bash theme={null}
git branch --format=%(refname:short)|%(HEAD)
```

***

### commit

Create a commit with staged changes.

**Usage:**

```json theme={null}
{
  "tool": "git_operations",
  "operation": "commit",
  "message": "Add git operations tool with input sanitization"
}
```

**Response:**

```
Committed: Add git operations tool with input sanitization
```

**Command:**

```bash theme={null}
git commit -m "Add git operations tool with input sanitization"
```

**Commit message truncation:**

* Messages truncated to 2000 bytes
* UTF-8 boundary-aware truncation
* Prevents extremely long commit messages

**Example: Long message**

```json theme={null}
{
  "tool": "git_operations",
  "operation": "commit",
  "message": "[2000+ characters]"
}
```

Truncated at UTF-8 boundary near 2000 bytes.

<Warning>
  Empty commit messages are rejected: "Commit message cannot be empty"
</Warning>

***

### add

Stage files for commit.

**Usage:**

```json theme={null}
{
  "tool": "git_operations",
  "operation": "add",
  "paths": "src/tools/git.zig"
}
```

**Response:**

```
Staged: src/tools/git.zig
```

**Multiple files:**

```json theme={null}
{
  "tool": "git_operations",
  "operation": "add",
  "paths": "src/*.zig"
}
```

**Response:**

```
Staged: src/*.zig
```

**Command:**

```bash theme={null}
git add -- src/tools/git.zig
```

Note: `--` separator prevents path injection

***

### checkout

Switch to a different branch.

**Usage:**

```json theme={null}
{
  "tool": "git_operations",
  "operation": "checkout",
  "branch": "feature-browser"
}
```

**Response:**

```
Switched to branch: feature-browser
```

**Command:**

```bash theme={null}
git checkout feature-browser
```

**Branch name validation:**\
Branch names are validated to prevent injection:

* Blocked: `;`, `|`, `` ` ``, `$()`
* Allowed: `a-z`, `A-Z`, `0-9`, `/`, `-`, `_`, `.`

**Example: Blocked injection**

```json theme={null}
{
  "tool": "git_operations",
  "operation": "checkout",
  "branch": "main; rm -rf /"
}
```

**Response:**

```
Unsafe git arguments detected
```

***

### stash

Stash uncommitted changes.

**Push/save stash:**

```json theme={null}
{
  "tool": "git_operations",
  "operation": "stash",
  "action": "push"
}
```

**Response:**

```
Saved working directory and index state WIP on main: 7a3b8c1 Add git tool
```

**Pop stash:**

```json theme={null}
{
  "tool": "git_operations",
  "operation": "stash",
  "action": "pop"
}
```

**Response:**

```
On branch main
Changes not staged for commit:
  modified:   src/main.zig
Dropped refs/stash@{0} (7a3b8c1d)
```

**List stashes:**

```json theme={null}
{
  "tool": "git_operations",
  "operation": "stash",
  "action": "list"
}
```

**Response:**

```
stash@{0}: WIP on main: 7a3b8c1 Add git tool
stash@{1}: WIP on feature: 6f5e4d3 Update docs
```

**Default action:** `push`

***

## Command Sanitization

All string arguments are sanitized before passing to Git to prevent command injection.

### Blocked Patterns

**Dangerous git options:**

* `--exec=cmd` — Execute arbitrary command
* `--upload-pack=cmd` — Remote command execution
* `--receive-pack=cmd` — Remote command execution
* `--pager=cmd` — Pager command injection
* `--editor=cmd` — Editor command injection
* `-c key=value` — Config injection
* `--no-verify` — Bypass hooks

**Shell metacharacters:**

* `;` — Command separator
* `|` — Pipe
* `>` `<` — Redirection
* `` ` `` — Backtick execution
* `$()` — Command substitution

### Examples

**Blocked: Config injection**

```json theme={null}
{
  "tool": "git_operations",
  "operation": "commit",
  "message": "-c core.sshCommand=evil"
}
```

**Response:**

```
Unsafe git arguments detected
```

**Blocked: Command substitution**

```json theme={null}
{
  "tool": "git_operations",
  "operation": "add",
  "paths": "$(whoami)"
}
```

**Response:**

```
Unsafe git arguments detected
```

**Allowed: Normal paths**

```json theme={null}
{
  "tool": "git_operations",
  "operation": "add",
  "paths": "src/main.zig"
}
```

**Allowed** ✓

***

## Working Directory Control

### Default: workspace\_dir

By default, git operations run in `workspace_dir`:

```json theme={null}
{
  "tool": "git_operations",
  "operation": "status"
}
```

Runs `git status` in `/workspace`

### Custom cwd (within workspace)

```json theme={null}
{
  "tool": "git_operations",
  "operation": "log",
  "cwd": "/workspace/subproject"
}
```

Runs `git log` in `/workspace/subproject`

### Custom cwd (outside workspace)

Requires `allowed_paths` configuration:

```zig theme={null}
gt.* = .{
    .workspace_dir = "/workspace",
    .allowed_paths = &.{
        "/data/repos",
        "/home/user/projects",
    },
};
```

```json theme={null}
{
  "tool": "git_operations",
  "operation": "status",
  "cwd": "/data/repos/project-a"
}
```

**Allowed** (in allowed\_paths)

```json theme={null}
{
  "tool": "git_operations",
  "operation": "status",
  "cwd": "/tmp/repo"
}
```

**Blocked:**

```
cwd is outside allowed areas
```

### cwd Validation Rules

* `cwd` must be an absolute path
* `cwd` must be within `workspace_dir` or `allowed_paths`
* `cwd` is resolved with `realpathAlloc()` to prevent symlink escapes
* Relative `cwd` is rejected: "cwd must be an absolute path"

***

## Error Handling

### Repository Not Found

```json theme={null}
{
  "tool": "git_operations",
  "operation": "status",
  "cwd": "/workspace/not-a-repo"
}
```

**Response:**

```
fatal: not a git repository (or any of the parent directories): .git
```

### Nothing to Commit

```json theme={null}
{
  "tool": "git_operations",
  "operation": "commit",
  "message": "Empty commit"
}
```

**Response:**

```
On branch main
nothing to commit, working tree clean
```

### Branch Not Found

```json theme={null}
{
  "tool": "git_operations",
  "operation": "checkout",
  "branch": "nonexistent-branch"
}
```

**Response:**

```
error: pathspec 'nonexistent-branch' did not match any file(s) known to git
```

### No Files to Add

```json theme={null}
{
  "tool": "git_operations",
  "operation": "add",
  "paths": "nonexistent.txt"
}
```

**Response:**

```
fatal: pathspec 'nonexistent.txt' did not match any files
```

***

## Use Cases

### Check Status Before Commit

```json theme={null}
{
  "tool": "git_operations",
  "operation": "status"
}
```

### Stage and Commit Changes

```json theme={null}
[
  {
    "tool": "git_operations",
    "operation": "add",
    "paths": "src/tools/git.zig"
  },
  {
    "tool": "git_operations",
    "operation": "commit",
    "message": "Add git operations tool"
  }
]
```

### View Recent Commits

```json theme={null}
{
  "tool": "git_operations",
  "operation": "log",
  "limit": 10
}
```

### Switch Branch

```json theme={null}
{
  "tool": "git_operations",
  "operation": "checkout",
  "branch": "feature-branch"
}
```

### Stash Before Switching

```json theme={null}
[
  {
    "tool": "git_operations",
    "operation": "stash",
    "action": "push"
  },
  {
    "tool": "git_operations",
    "operation": "checkout",
    "branch": "main"
  }
]
```

### Review Staged Changes

```json theme={null}
{
  "tool": "git_operations",
  "operation": "diff",
  "cached": true
}
```

***

## Source

`src/tools/git.zig:10-335`

## Testing

Run git tool tests:

```bash theme={null}
zig build test --summary all
```

Tests cover:

* All operations (status, diff, log, branch, commit, add, checkout, stash)
* Command sanitization (injection prevention)
* Working directory validation
* Error cases
* Commit message truncation
* Branch name validation
