Docs / Concepts

The two-branch model

sgit's central architectural idea: every clone works on a private branch, and sharing is an explicit act.

Clone branches and named branches

        agent A's machine                    the server                    agent B's machine
  ┌───────────────────────┐        ┌───────────────────────────┐        ┌───────────────────────┐
  │  clone branch (A)     │  push  │   named branch "main"     │  pull  │  clone branch (B)     │
  │  key: local only ─────┼───────▶│   key: shared via vault   │◀───────┼─ key: local only      │
  └───────────────────────┘        └───────────────────────────┘        └───────────────────────┘

Run sgit status --explain any time — it prints this model with your vault's actual branch names filled in.

Why it works this way

Merging and conflicts

When histories diverge, sgit runs a genuine three-way merge (common ancestor + ours + theirs) per file. Non-conflicting changes merge automatically; conflicting paths get .conflict files, and:

$ sgit resolve --show
  notes/plan.md   CONFLICT (both changed)
    base   │ ship in Q3
    ours   │ ship in Q4          ← your clone branch
    theirs │ ship in Q3, beta Q2 ← the named branch
    verdict: genuine conflict  (not one-sided, not identical)

The verdict line matters in multi-writer vaults: it distinguishes genuine conflicts from one-sided or identical changes, so you (or an agent) only spend attention where a human decision is actually required. sgit merge-abort backs out of a merge cleanly.