# `homeboy git`

## Synopsis

```sh
homeboy git <COMMAND>
```

Git operations for Homeboy components, worktrees, portable checkouts, and GitHub issue / pull request workflows.

Most commands emit Homeboy's structured JSON envelope when appropriate. See the [JSON output contract](../architecture/output-system.md). Git JSON output includes a top-level `variant` discriminator for the public wrapper shape: `single`, `bulk`, `issue`, `pr`, `pr_refresh`, `find`, `policy`, or `land`. Some subcommands also accept `--json` for bulk input.

Follow-up: git has many subcommand-specific payload shapes inside those wrappers; the current public contract tags the wrapper variants, and per-subcommand golden fixtures can be added incrementally where consumers need stricter fixtures.

## Component Resolution

Most core git verbs can run without a positional component ID:

```sh
homeboy git status
homeboy git commit -m "Update docs"
homeboy git push
```

Homeboy resolves the target checkout in this order:

1. **Explicit component ID plus `--path`**: trust both values. Use this when a command needs a known component identity but should operate on a specific checkout.
2. **`--path <path>` only**: operate on that checkout, discovering the component from a portable `homeboy.json` at the path or its git root. If no portable config exists, Homeboy derives the component ID from the path basename.
3. **Explicit component ID only**: resolve the component from Homeboy's registry.
4. **Neither ID nor `--path`**: auto-detect from the current directory using the registry, then a portable `homeboy.json` in the current checkout or git root.

Use `--path` for worktrees, CI runners, ad-hoc clones, or headless scripts where the process CWD is not already the checkout you want:

```sh
homeboy git status --path /Users/user/Developer/homeboy@docs-refresh-git-command
homeboy git push --path /tmp/homeboy-ci-checkout
```

## Core Verbs

### Status

```sh
homeboy git status [component_id] [--path <path>]
```

Shows git status for one checkout. `component_id` is optional when Homeboy can detect the component from CWD or `--path`.

Bulk status is available through `--json`:

```sh
homeboy git status --json '{"component_ids":["homeboy","sample-plugin"]}'
```

### Commit

```sh
homeboy git commit [component_id] [message-or-spec]
homeboy git commit -m "Update docs"
homeboy git commit --staged-only -m "Use staged changes only"
homeboy git commit --files README.md docs/index.md -m "Update docs"
homeboy git commit --exclude Cargo.lock -m "Update docs"
```

By default, `commit` stages all changes before committing. Use `--staged-only`, `--files`, `--include`, or `--exclude` for narrower staging.

`commit` also accepts a JSON spec. The spec can be passed positionally, through `--json`, from stdin with `-`, or from a file with `@file.json`:

```sh
homeboy git commit '{"message":"Update docs","include_files":["README.md"]}'
homeboy git commit --json '@commit.json'
```

Homeboy auto-detects single vs bulk commit specs by checking for a top-level `components` array.

### Push

```sh
homeboy git push [component_id] [--tags] [--force-with-lease] [--path <path>]
```

`push --force-with-lease` is the safe post-rebase force-push path. It refuses to overwrite the remote if it has commits the local ref has not seen. Plain `--force` is intentionally not exposed.

```sh
homeboy git push
homeboy git push --tags
homeboy git push --force-with-lease
```

Bulk push is available through `--json`:

```sh
homeboy git push --json '{"component_ids":["homeboy","sample-plugin"],"tags":true}'
```

### Pull

```sh
homeboy git pull [component_id] [--path <path>]
```

Pulls remote changes for one checkout. Like `status`, `push`, and `commit`, the component ID is optional when CWD or `--path` can identify the component.

### Rebase

```sh
homeboy git rebase [component_id] [--onto <ref>] [--continue | --abort] [--path <path>]
```

Without `--onto`, `rebase` uses the current branch's tracked upstream (`@{upstream}`), matching `git pull --rebase` semantics. Use `--onto <ref>` to choose the target explicitly:

```sh
homeboy git rebase --onto origin/main
homeboy git rebase --continue
homeboy git rebase --abort
```

On conflict, Homeboy returns a failed result with git's stderr. Resolve conflicts manually, then re-run with `--continue` or `--abort`.

### Cherry-pick

```sh
homeboy git cherry-pick [refs...] [--pr <number>...] [--continue | --abort] [--path <path>]
```

`cherry-pick` accepts SHAs, branch names, ranges such as `<a>..<b>`, and repeatable `--pr <number>` flags. PR numbers are resolved with `gh pr view <n> --json commits`.

```sh
homeboy git cherry-pick abc1234 def5678
homeboy git cherry-pick feature-a..feature-b
homeboy git cherry-pick --pr 123 --pr 124
homeboy git cherry-pick --continue
homeboy git cherry-pick --abort
```

Use `-c, --component-id <id>` when running from outside the target checkout without `--path`.

### Tag

```sh
homeboy git tag [component_id] [tag_name] [-m <message>] [--path <path>]
```

If `tag_name` is omitted, Homeboy tags `v<component version>` from `homeboy version show`.

## GitHub Issue Workflows

```sh
homeboy git issue create <component_id> --title <title> [--body <body> | --body-file <path>] [--label <label>...]
homeboy git issue comment <component_id> --number <n> [--body <body> | --body-file <path>]
homeboy git issue find <component_id> [--title <title>] [--label <label>...] [--state open|closed|all] [--limit <n>]
homeboy git issue close <component_id> --number <n> [--reason completed|not-planned] [--comment <body> | --comment-file <path>]
homeboy git issue edit <component_id> --number <n> [--title <title>] [--body <body> | --body-file <path>] [--add-label <label>...] [--remove-label <label>...]
```

Issue commands require a component ID, but they also accept `--path` to discover component metadata from a portable checkout:

```sh
homeboy git issue find homeboy --state open --limit 10
homeboy git issue create homeboy --path /tmp/homeboy --title "docs: clarify git workflow" --body-file /tmp/body.md
```

Use `close --reason not-planned` for intentional wontfix decisions. Homeboy's reconciliation tooling treats GitHub's not-planned state as the durable signal not to re-file the issue.

## GitHub Pull Request Workflows

```sh
homeboy git pr create <component_id> --base <base> --head <head> --title <title> [--body <body> | --body-file <path>] [--draft]
homeboy git pr edit <component_id> --number <n> [--title <title>] [--body <body> | --body-file <path>]
homeboy git pr find <component_id> [--base <base>] [--head <head>] [--state open|closed|merged|all] [--limit <n>]
homeboy git pr readiness <component_id> --number <n>
homeboy git pr comment <component_id> --number <n> [comment mode flags]
homeboy git pr refresh <component_id> <number-or-url> [--strategy auto|rebase|merge|ff-only] [--check <cmd>...] [--push]
homeboy git pr land <repo> <pr>... [--merge-method squash|merge|rebase]
```

Like issue commands, PR commands accept `--path` to discover component metadata from a portable checkout:

```sh
homeboy git pr create homeboy --base main --head docs-refresh-git-command --title "docs: refresh git command workflows" --body-file /tmp/pr.md
homeboy git pr find homeboy --head docs-refresh-git-command --state open
homeboy git pr readiness homeboy --number 123 --output /tmp/pr-readiness.json
```

### PR Merge Readiness

`homeboy git pr readiness` is a read-only explanation command. It fetches GitHub PR metadata with `gh pr view`, reports GitHub's raw `mergeStateStatus`, and adds a Homeboy interpretation without attempting a merge as the discovery mechanism.

The command exits `0` only when the interpreted state is `mergeable_now` and no blockers are present. Other interpreted states exit nonzero so automation can gate on readiness while still reading the structured JSON result from `--output`.

Interpreted states:

| State | Meaning | Usual guidance |
|-------|---------|----------------|
| `mergeable_now` | GitHub reports `CLEAN`, the PR is not draft, and checks are terminal green. | Merge policy may proceed. |
| `conflicted` | GitHub reports `DIRTY` or the branch is `BEHIND`. | Rebase or update the PR branch, resolve conflicts, push, then re-run readiness. |
| `waiting_on_required_checks` | Required checks are pending, or GitHub reports `CLEAN` before checks have appeared on a new head. | Wait for checks to report on the current head. |
| `failing_required_checks` | Branch protection or required checks block merge. | Fix failing required checks or branch-rule blockers. |
| `waiting_on_optional_checks` | GitHub reports `UNSTABLE` while checks are still pending. | Wait for non-blocking checks if your workflow treats them as merge evidence. |
| `failing_optional_checks` | GitHub reports `UNSTABLE` with failed or inconclusive checks. | Inspect optional check failures and decide whether they are acceptable. |
| `unknown` | GitHub reports `UNKNOWN`, no raw merge state, hooks are still settling, or Homeboy does not recognize the combination. | Wait briefly and re-run readiness; do not probe by attempting a merge. |

Machine-readable output includes the raw state, interpreted state, blockers, and guidance:

```json
{
  "variant": "pr_readiness",
  "action": "pr.readiness",
  "number": 123,
  "ci_state": "terminal_green",
  "ci_summary": "1 check(s): 1 terminal-green, 0 failed/unknown, 0 pending",
  "readiness": {
    "raw_merge_state": "UNKNOWN",
    "interpreted_state": "unknown",
    "mergeable": false,
    "blockers": [
      {
        "kind": "mergeability_unknown",
        "message": "GitHub has not computed mergeability for the current PR head yet.",
        "guidance": "Wait briefly and re-run readiness; do not attempt a merge just to discover state."
      }
    ],
    "check_guidance": "Check state alone is insufficient while mergeability is UNKNOWN. 1 check(s): 1 terminal-green, 0 failed/unknown, 0 pending",
    "conflict_guidance": "Conflict state is unknown until GitHub recomputes mergeability."
  }
}
```

### PR Refresh

`homeboy git pr refresh` updates a PR branch from its current base and emits a structured summary for merge-train recovery after sibling PRs land.

```sh
homeboy git pr refresh homeboy 5806
homeboy git pr refresh homeboy https://github.com/Extra-Chill/homeboy/pull/5806 --strategy rebase
homeboy git pr refresh homeboy 5806 --check "git diff --check" --push
```

Safety behavior:

1. Refuses to start when the worktree is already dirty.
2. Checks out the PR through `gh pr checkout`, fetches the current base, then applies the selected strategy.
3. `--strategy auto` honors `branch.<name>.rebase` or `pull.rebase`, falling back to `rebase`.
4. Reports conflicted files from `git status --porcelain` and exits non-zero when blockers remain.
5. Runs lightweight checks only after a clean refresh. When no `--check` is supplied, it runs `git diff --check`.
6. Never pushes by default. `--push` publishes only a clean, check-passing branch and uses `git push --force-with-lease`; plain destructive force-push is not exposed.

### PR Landing Train

`homeboy git pr land` lands a list of PRs one at a time. It inspects the next PR immediately before merging, merges only clean PRs with successful reported checks, then rechecks the next PR after each merge. The command pauses at the first blocker and emits a final fleet status table.

```sh
homeboy git pr land Extra-Chill/homeboy 123 124 125
homeboy git pr land Extra-Chill/homeboy https://github.com/Extra-Chill/homeboy/pull/123 --dry-run
```

Safety behavior:

- PRs merge sequentially by default; there is no parallel landing mode.
- A PR with failing, pending, or unreported required checks blocks the train.
- A `CLEAN` merge state without reported successful checks is not considered ready.
- If GitHub reports a base-branch-modified merge race, Homeboy recomputes readiness and retries within `--max-base-retries`.
- Dirty dependent PRs are refreshed only when an explicit helper program is supplied with `--refresh-helper`; helper arguments are passed directly, not through a shell.

Refresh helper example:

```sh
homeboy git pr land Extra-Chill/homeboy 123 124 \
  --refresh-helper homeboy-pr-refresh \
  --refresh-helper-arg {repo} \
  --refresh-helper-arg {number}
```

Supported helper placeholders are `{repo}`, `{number}`, `{url}`, and `{head_sha}`.

### PR Comments

`homeboy git pr comment` supports three comment modes:

1. **Plain comment**: no marker flags. Homeboy appends a fresh comment.
2. **Sticky whole-body comment**: `--key <key>` finds or updates one comment tagged with `<!-- homeboy:key=<key> -->`. The provided body replaces the whole managed comment body.
3. **Sectioned managed comment**: `--comment-key <outer> --section-key <inner>` updates one section inside a shared comment tagged with `<!-- homeboy:comment-key=<outer> -->`. Other sections are preserved.

Sectioned comments are used by Homeboy Action to keep lint, test, audit, and tooling metadata in one managed PR comment without clobbering sibling sections:

```sh
homeboy git pr comment homeboy \
  --number 123 \
  --comment-key homeboy-ci-results \
  --section-key lint \
  --section-order lint,test,audit \
  --header "## Homeboy Results" \
  --footer-file /tmp/tooling.md \
  --body-file /tmp/lint.md
```

`--key` mode and `--comment-key` / `--section-key` mode are mutually exclusive. In sectioned mode, existing headers and footers are preserved when omitted; passing `--footer` or `--footer-file` replaces the stored footer.

## JSON Input Schemas

### SingleCommitSpec

```json
{
  "id": "homeboy",
  "message": "Update git docs",
  "staged_only": false,
  "include_files": ["docs/commands/git.md"]
}
```

Notes:

- `id` is optional when the component is supplied positionally or auto-detected from CWD.
- `staged_only` defaults to `false`.
- `include_files` stages only the listed paths.
- `exclude_files` stages all changes and then unstages the excluded paths.

### BulkCommitInput

```json
{
  "components": [
    { "id": "homeboy", "message": "Update git docs" },
    { "id": "sample-plugin", "message": "Update workflow docs" }
  ]
}
```

### BulkIdsInput

```json
{
  "component_ids": ["homeboy", "sample-plugin"],
  "tags": true,
  "force_with_lease": false
}
```

`tags` and `force_with_lease` are only used by `push`.

## JSON Output

> Note: command output is wrapped in the global JSON envelope described in the [JSON output contract](../architecture/output-system.md). The examples below show the `data` payload.

### Single Component Output

```json
{
  "component_id": "homeboy",
  "path": "/Users/user/Developer/homeboy@docs-refresh-git-command",
  "action": "status|commit|push|pull|tag|rebase|cherry-pick",
  "success": true,
  "exit_code": 0,
  "stdout": "<stdout>",
  "stderr": "<stderr>"
}
```

### Bulk Output

```json
{
  "action": "status|commit|push|pull",
  "results": [
    {
      "component_id": "homeboy",
      "path": "/path/to/homeboy",
      "action": "commit",
      "success": true,
      "exit_code": 0,
      "stdout": "[main abc1234] Update git docs\n 1 file changed",
      "stderr": ""
    }
  ],
  "summary": {
    "total": 1,
    "succeeded": 1,
    "failed": 0
  }
}
```

Notes:

- `commit` returns a successful result with `stdout` set to `Nothing to commit, working tree clean` when there are no changes.
- Bulk operations continue processing all components even if some fail; the summary reports total, succeeded, and failed counts.
- Bulk outputs are `BulkGitOutput { action, results, summary }`, where `results` is a list of `GitOutput` objects.

## Exit Code

- Single mode: exit code matches the underlying `git` or `gh` command.
- Bulk mode (`--json`): `0` if all components succeeded; `1` if any failed.

## Examples

```sh
# Auto-detect the component from the current checkout
homeboy git status
homeboy git commit -m "Update docs"
homeboy git push

# Operate on a worktree or CI checkout without changing CWD
homeboy git status --path /Users/user/Developer/homeboy@docs-refresh-git-command
homeboy git commit --path /Users/user/Developer/homeboy@docs-refresh-git-command -m "Update docs"

# Use explicit component IDs when operating outside a component checkout
homeboy git status homeboy
homeboy git pull homeboy
homeboy git tag homeboy v1.0.0 -m "Release 1.0.0"

# Rebase, then safely update the remote branch
homeboy git rebase --onto origin/main
homeboy git push --force-with-lease

# Pick commits by ref or by GitHub PR number
homeboy git cherry-pick abc1234
homeboy git cherry-pick --pr 123

# GitHub issue and PR helpers
homeboy git issue find homeboy --label audit --state open
homeboy git pr create homeboy --base main --head docs-refresh-git-command --title "docs: refresh git command workflows" --body-file /tmp/pr.md
homeboy git pr comment homeboy --number 123 --key docs-check --body-file /tmp/comment.md
```

## Related

- [stack](stack.md)
- [version](version.md)
