# zit 0.3.39

zit 0.3.39 lets a forge show a conflicting merge the way zit sees it and
record the author's resolution of it, both without a checkout. `zit
merge-tree` gains `-F json`, which reports every conflicting file and the
regions of its merge, and `--commit`, which records the merge with
resolutions the caller supplies.

No artifact, tag namespace, wire, or on-disk repository format changed.

## The merge as JSON

```
zit merge-tree BRANCH --into TARGET -F json
```

prints one JSON document and writes nothing. It gives the verdict (`clean`,
`up-to-date`, `conflict` or `unrelated`), the base, current (TARGET) and other
(BRANCH) check-ins, and every conflicting file. A file carries its kind
(`text`, `binary`, `add-add`, `delete-modify`, `modify-delete` or
`unreadable`) and each side's blob and mode, with `null` where the path is
absent. A text file also carries the regions of the merge: `same`, `current`,
`other`, `both` or `conflict`, each as half-open line ranges in base, current
and other. The regions cover each file in order, so a page can draw all three
panes and the merged result from them without merging again.

The regions come out of the same loop that produces zit's merged bytes, so
the page cannot show a merge different from the one `zit merge` makes. The
order of the names picks the sides: to show a branch as "ours", name the
branch as TARGET.

The exit status is the same as without `-F json`: 0 clean or up to date, 5
for conflicts, 1 for unrelated check-ins. Every verdict prints a document, so
a caller parses one format.

A store that lacks the content of a file both sides changed reports that file
as `unreadable` instead of failing.

## Settling a conflict

```
zit merge-tree BRANCH --into TARGET --commit --resolved DIR [--delete PATH]...
    -m MESSAGE [--user NAME] --expect-current HASH --expect-other HASH
```

records the merge of BRANCH into TARGET on TARGET's branch. DIR holds the
resolved content of each conflicting path; `--delete PATH` resolves one by
removing it. Every other path is the merge's own. The check-in is the one
`zit merge` and `zit commit` in a checkout would make with the same answers,
and its `check-in HASH on BRANCH` line goes to stdout as `commit` prints it.

Nothing is written when:

- the answers are not exactly the conflicting paths (exit 1);
- an answer still holds a conflict marker (exit 1);
- the store lacks the content of a conflicting file (exit 1);
- TARGET or BRANCH has moved since the page was drawn, that is, either is no
  longer the check-in `--expect-current` or `--expect-other` names (exit 5,
  so the caller reads again and redraws).

The merge runs in a working area of its own beside the repository, removed
afterwards, and the check-in syncs the way any check-in does.

## Compatibility

- `zit merge-tree` with `--out` or `--tar` behaves as before, byte for byte.
- `-F json` and `--commit` exclude `--out`, `--tar` and each other; giving
  more than one is a usage error. `--resolved`, `--delete`, `-m`, `--user`
  and the `--expect` options are usage errors without `--commit`.

## Qualification

`zig fmt --check`, `zig build test`, `e2e`, `smoke` (19 scenarios),
`git-import-e2e`, `git-export-e2e`, `dst`, and every fuzz target, whose
three-way merge target now also rebuilds each side of a merge from its
regions, plus the three-runner battery.
