# Document the HEAD propagation limitation and the workarounds

`d5bc3f5`·

Soph·2mo ago·1 file·+29 added/-0 removed

usage.md gains a "HEAD / Default Branch" section explaining:
- where to find the source's HEAD in JSON/human output
- why the target's HEAD isn't reconciled (no portable receive-pack symref-update; go-git alpha doesn't ship the capability anyway)
- two mitigations: match the default at init time (preferred), or set HEAD manually post-sync via `git symbolic-ref` or the host's API

The JSON Output section lists the new `execution.sourceHead` / `sourceHead` fields.

Refs #45.

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>

## Sessions

bd2959cd5c8aView transcript

## Changes

1

- docs

- Musage.md+29

``` 
221 unmodified lines

...
260
261
262
263
264
265
266

221 unmodified lines
  <target-url>
```

## HEAD / Default Branch

git-sync surfaces the source's symref HEAD target — the source's default branch — in the result's `execution.sourceHead` field (JSON) and as a `source-head: <ref>` line in human output. `probe` exposes the same field.

```
$ git-sync sync --json … | jq .execution.sourceHead
"refs/heads/main"
```

The target's default branch is **not** automatically reconciled. git's `push --mirror` doesn't propagate HEAD either; standard receive-pack has no portable mechanism for setting a remote symref's target (only newer git versions support a `symref-update` capability, and go-git's current alpha doesn't send it). The result: if you `git init --bare` your target with a default branch that differs from the source's, the mirror's HEAD will dangle even after a successful sync.

Practical mitigations, in order of preference:

1. **Match the default at init time.** `git init --bare --initial-branch=<source-default>` (or the equivalent on your host) avoids the problem entirely. Compare against `git-sync probe <source-url>`'s `sourceHead` to know what to pass.
2. **Set HEAD manually post-sync.** On a self-hosted bare repo: `git symbolic-ref HEAD refs/heads/<source-default>`. On hosted providers (GitHub, GitLab, etc.), use the provider's API or web UI to set the default branch.

Detecting the mismatch automatically is tracked as a follow-up; doing so requires a separate `upload-pack` round-trip against the target since the `receive-pack` advertisement we already query doesn't include HEAD.

## JSON Output

Add `--json` to any command to emit machine-readable output instead of the default text format.
- refs and hashes are serialized as strings, not raw byte arrays
- top-level keys include `plans`, `pushed`, `skipped`, `blocked`, `deleted`, `warned`, `dryRun`, `protocol`, and `stats`, plus `relay`, `relayMode`, `relayReason`, `batching`, `batchCount`, `plannedBatchCount`, and `tempRefs`
- each item in `plans` includes stable string fields such as `branch`, `sourceRef`, `targetRef`, `sourceHash`, `targetHash`, `kind`, `action`, and `reason`
- `execution.sourceHead` (sync/plan/replicate/bootstrap) and `sourceHead` (probe) carry the source's symref HEAD target when advertised
