documentation/REVIEW.md · main · 2026-07-10

Review Extension

Pull requests and review feedback stored as commits on the gitmsg/review branch. PRs live on the author's repository and are discovered by reviewers via fetch/follow.

Spec: GITREVIEW.md — wire format for pull requests, feedback, review states, and version tracking.

Initialize

gitsocial review init                  # creates refs/gitmsg/review/config and the gitmsg/review branch
gitsocial review init -b <branch>      # initialize on a custom branch
gitsocial review config get / set / list

Pull requests

gitsocial review pr create "Add dark mode" \
    --base '#branch:main' --head '#branch:dark-mode' \
    --reviewers [email protected],[email protected] \
    --closes <issue-ref> \
    --stack                              # auto-set depends-on from base→head matching

gitsocial review pr list
gitsocial review pr show <ref>
gitsocial review pr update <ref>         # capture new branch tips as a new version
gitsocial review pr sync <ref> [--strategy rebase|merge]
gitsocial review pr merge <ref> [--strategy fast-forward|squash|rebase|merge]
gitsocial review pr close <ref>
gitsocial review pr retract <ref>
gitsocial review pr draft <ref>          # open → draft
gitsocial review pr ready <ref>          # draft → open
gitsocial review pr diff <ref>           # range-diff between PR versions
gitsocial review pr stack <ref>          # show the full stack
gitsocial review pr rebase-stack <ref>   # cascade rebase PRs above
gitsocial review pr sync-stack <ref>     # snapshot tips for all PRs in stack

--base and --head accept any ref (#branch:<name> for local, <url>#branch:<name> for cross-forge — see §2 Cross-Forge Contribution). --depends-on sets stack relationships explicitly when --stack auto-detection isn't enough.

Feedback

gitsocial review feedback approve <pr-ref> [-m "LGTM"]
gitsocial review feedback request-changes <pr-ref> -m "Reasoning..."
gitsocial review feedback comment "Consider caching this" \
    --pr <pr-ref> --commit <12-char-sha> --file path/to.go \
    --new-line 42 [--new-line-end 50] [--old-line 40] [--suggest]

Reviews are tied to the version the reviewer saw; force-push doesn't auto-dismiss them. See Version Tracking and Review Staleness.

Forks

gitsocial review fork add <fork-url>     # alias for `gitsocial fork add`
gitsocial review fork list
gitsocial review fork remove <fork-url>

PRs from registered forks appear in pr list and trigger fork-pr notifications. See Fork PR Discovery for the full flow.

The TUI's review section (R from any screen — Pull Requests) provides the PR list, detail, files-changed, interdiff, history, and feedback flows.


Flows — Table of Contents

The remainder of this document walks through usage scenarios in detail.


Why GitSocial Reviews

GitSocial stores PRs as commits on a gitmsg/review branch inside the repo itself. The repo is the source of truth. Everything else follows from that.

Cross-forge PRs. base and head fields are URLs, not internal IDs. A GitLab contributor can submit a PR targeting a GitHub upstream. On merge, fork PRs are copied to upstream with the original author preserved — surviving fork deletion.

Rebase-resilient identity. The PR references head="#branch:feature" — the branch name doesn't change when its commits are rebased. State changes (edits, merge, close) are new commits linked via edits. Force-push can't destroy review context because review data lives on a separate branch.

Version tracking. Every pr update records the hash of the base and head branch tips. The edits chain is the version history — walking it gives every recorded code snapshot. pr diff runs git range-diff between any two versions. Feedback timestamps are compared against version timestamps to determine which version a reviewer saw — pure rebases (patches identical) are distinguished from actual code changes. Reviews are never auto-dismissed.

Per-PR merge strategies. Fast-forward, squash, rebase, and merge commit — chosen per-PR via --strategy, not a repo-wide setting.

Comparison

Pain PointGitHub/GitLabGitSocial
Cross-forge PRImpossibleProtocol-native via URLs
PR identity survives rebaseTied to commit SHAsPR commit on review branch = permanent anchor
History of code changesForce-push = gonebase-tip/head-tip snapshots in edits chain
Review lost on rebaseComments hidden/outdatedEdits chain preserves all versions; range-diff shows what changed
What changed between reviews?GitLab has version diffs; GitHub has no built-in comparisonpr diff shows range-diff between any two versions
Review dismissal on rebaseUnpredictable auto-dismissRange-diff detects actual code changes vs. pure rebase; never auto-dismiss
Merge strategyRepo-level config; choose from enabled optionsAll 4 strategies per-PR via --strategy
Update branchMerge vs rebase dilemmapr sync with rebase or merge, auto-captures tips
Fork PR, fork deletedReview orphanedPR copied to upstream on merge

Flows

1. Same-Repo Pull Request

Alice and Bob work on the same repo. Alice proposes a change, Bob reviews it.

    Alice                           Bob
      │                              │
      ●  push dark-mode branch       │
      ●  create PR                   │
      │  open, base=main             │
      │  head=dark-mode              │
      │                              │
      │                              ●  fetch gitmsg/review
      │                              ●  see open PR
      │                              ●  post inline review
      │                              │
      ●  read review, push fix       │
      │                              │
      │                              ●  approve
      │                              │
      ●  merge (state=merged)        │
      ●  closes linked issues        │

Messages

Alice creates pull request:

Add dark mode support

GitMsg: ext="review"; type="pull-request"; state="open"; base="#branch:main"; base-tip="f1e2d3c4b5a6"; closes="#commit:abc123456789@gitmsg/pm"; head="#branch:dark-mode"; head-tip="a1b2c3d4e5f6"; reviewers="[email protected]"; v="0.1.0"

Bob posts inline review:

Consider caching this value to avoid recomputation on every render.

GitMsg: ext="review"; type="feedback"; pull-request="#commit:aaa111222333@gitmsg/review"; commit="def456789abc"; file="src/theme.js"; line-start="42"; side="right"; v="0.1.0"
GitMsg-Ref: ext="review"; type="pull-request"; author="Alice"; email="[email protected]"; time="2025-01-20T10:00:00Z"; ref="#commit:aaa111222333@gitmsg/review"; v="0.1.0"
 > Add dark mode support

Bob approves:

LGTM!

GitMsg: ext="review"; type="feedback"; pull-request="#commit:aaa111222333@gitmsg/review"; review-state="approved"; v="0.1.0"
GitMsg-Ref: ext="review"; type="pull-request"; author="Alice"; email="[email protected]"; time="2025-01-20T10:00:00Z"; ref="#commit:aaa111222333@gitmsg/review"; v="0.1.0"
 > Add dark mode support

Alice merges (edits original pull request commit):

Add dark mode support

GitMsg: ext="review"; type="pull-request"; edits="#commit:aaa111222333@gitmsg/review"; state="merged"; base="#branch:main"; head="#branch:dark-mode"; merge-base="f1e2d3c4b5a6"; merge-head="a1b2c3d4e5f6"; v="0.1.0"

2. Cross-Forge Contribution

Alice works on GitLab, Bob maintains the upstream repo on GitHub. Alice forks, proposes changes, Bob reviews on GitHub.

    GitLab (alice)                 GitHub (bob)
      │                              │
      ●  push dark-mode branch       │
      ●  create PR                   │
      │  head=gitlab/alice/repo      │
      │  base=github/bob/repo        │
      │                              │
      │                              ●  follow alice's repo
      │                              ●  fetch gitmsg/review
      │                              ●  see cross-forge PR
      │                              ●  post review
      │                              │
      ●  read review (fetch bob)     │
      ●  push fix                    │
      │                              │
      │                              ●  approve
      │                              ●  merge

Messages

Alice creates cross-forge pull request (on her GitLab repo's gitmsg/review branch):

Add dark mode support

GitMsg: ext="review"; type="pull-request"; state="open"; base="https://github.com/bob/repo#branch:main"; base-tip="f1e2d3c4b5a6"; head="https://gitlab.com/alice/repo#branch:dark-mode"; head-tip="a1b2c3d4e5f6"; v="0.1.0"

Bob reviews (on his GitHub repo's gitmsg/review branch):

Solid approach. One suggestion on the CSS transitions.

GitMsg: ext="review"; type="feedback"; pull-request="https://gitlab.com/alice/repo#commit:bbb222333444@gitmsg/review"; review-state="approved"; v="0.1.0"
GitMsg-Ref: ext="review"; type="pull-request"; author="Alice"; email="[email protected]"; time="2025-01-20T10:00:00Z"; ref="https://gitlab.com/alice/repo#commit:bbb222333444@gitmsg/review"; v="0.1.0"
 > Add dark mode support

Bob merges (on his GitHub repo):

Step 1 — Copy fork PR to upstream (preserves original author via GitMsg-Ref):

Add dark mode support

GitMsg: ext="review"; type="pull-request"; state="open"; base="#branch:main"; base-tip="f1e2d3c4b5a6"; head="https://gitlab.com/alice/repo#branch:dark-mode"; head-tip="a1b2c3d4e5f6"; v="0.1.0"
GitMsg-Ref: ext="review"; type="pull-request"; author="Alice"; email="[email protected]"; time="2025-01-20T10:00:00Z"; ref="https://gitlab.com/alice/repo#commit:bbb222333444@gitmsg/review"; v="0.1.0"
 > Add dark mode support

Step 2 — Merge edit references the local copy:

Add dark mode support

GitMsg: ext="review"; type="pull-request"; edits="#commit:ccc333444555@gitmsg/review"; state="merged"; base="#branch:main"; head="https://gitlab.com/alice/repo#branch:dark-mode"; merge-base="f1e2d3c4b5a6"; merge-head="a1b2c3d4e5f6"; v="0.1.0"

3. Fork PR Discovery

A maintainer registers forks so PRs (and issues) from contributors are automatically discovered during fetch, appear in pr list, and trigger notifications.

    Maintainer (upstream)               Contributor (fork)
      │                                      │
      ●  gitsocial fork add                  │
      │  <fork-url>                          │
      │                                      │
      │                                      ●  push feature branch
      │                                      ●  create PR
      │                                      │  base=#branch:main
      │                                      │  head=#branch:feature
      │                                      │
      ●  gitsocial fetch                     │
      │  → fetches fork's gitmsg/* branches  │
      │  → all extension processors          │
      │                                      │
      ●  pr list shows fork PR               │
      ●  notification: "fork-pr"             │
      │                                      │
      ●  review / merge                      │

How It Works

  1. Register fork: gitsocial fork add https://github.com/contributor/repo
  2. Fetch: During gitsocial fetch, all gitmsg/* branches from each registered fork are fetched and processed through all extension processors (review, PM, social, release)
  3. Discovery: Fork PRs with a base that is a local ref (#branch:main) or explicitly targets the workspace URL are included in pr list
  4. Notifications: New fork PRs appear as fork-pr notifications; feedback on workspace PRs appears as feedback/approved/changes-requested notifications

Fork Config

Forks are stored in the core config (refs/gitmsg/core/config):

{
  "version": "0.1.0",
  "forks": [
    "https://github.com/contributor/repo"
  ]
}

4. Review with Suggestions

Reviewer proposes concrete code changes that the author can apply directly.

     Bob                           Alice
      │                              │
      ●  create PR                   │
      │                              │
      │                              ●  post suggestion
      │                              │
      ●  apply suggestion            │
      ●  push updated branch         │
      │                              │
      │                              ●  approve

Messages

Alice suggests a change: ~~~ Use a CSS custom property for the transition

transition: background-color var(--theme-transition, 200ms) ease;

GitMsg: ext="review"; type="feedback"; pull-request="#commit:aaa111222333@gitmsg/review"; commit="def456789abc"; file="src/theme.css"; line-start="18"; side="right"; suggestion="true"; v="0.1.0" GitMsg-Ref: ext="review"; type="pull-request"; author="Bob"; email="[email protected]"; time="2025-01-20T10:00:00Z"; ref="#commit:aaa111222333@gitmsg/review"; v="0.1.0"

Refactor theme transitions

~~~


5. Multi-Reviewer Approval

A pull request requires multiple reviewers. Aggregation determines merge readiness.

    Alice                   Bob                       Carol
      │                      │                        │
      ●  create PR           │                        │
      │  reviewers=          │                        │
      │  bob,carol           │                        │
      │                      │                        │
      │                      ●  changes-requested     │
      │                      │                        │
      ●  push fix            │                        │
      │                      │                        │
      │                      ●  approved              │
      │                      │                        ●  approved
      │                      │                        │
      ●  merge               │                        │

Aggregation Rules


6. Pull Request Linked to Issues

A pull request closes issues when merged. The closes field contains commit references to PM issues.

Messages

Pull request that closes two issues:

Add dark mode support

Implements theme switching with system preference detection.

GitMsg: ext="review"; type="pull-request"; state="open"; base="#branch:main"; base-tip="f1e2d3c4b5a6"; closes="#commit:abc123456789@gitmsg/pm,#commit:def456789012@gitmsg/pm"; head="#branch:dark-mode"; head-tip="a1b2c3d4e5f6"; v="0.1.0"
GitMsg-Ref: ext="pm"; type="issue"; author="Alice"; email="[email protected]"; time="2025-01-06T10:00:00Z"; ref="#commit:abc123456789@gitmsg/pm"; v="0.1.0"
 > Add dark mode support
GitMsg-Ref: ext="pm"; type="issue"; author="Bob"; email="[email protected]"; time="2025-01-07T09:00:00Z"; ref="#commit:def456789012@gitmsg/pm"; v="0.1.0"
 > Support system theme preference

When the pull request transitions to state="merged", implementations auto-close the referenced issues.


7. Discussion Threads on a Pull Request

General discussion (not anchored to code) uses GitSocial comments. Nested replies use reply-to.

    Alice               Bob                   Carol
      │                  │                      │
      ●  create PR       │                      │
      │                  │                      │
      │                  ●  "Should we also     │
      │                  │   handle prefers-    │
      │                  │   contrast?"         │
      │                  │                      │
      │                  │                      ●  "Yes, good idea.
      │                  │                      │   I can follow up
      │                  │                      │   in a separate PR."
      │                  │                      │
      ●  "Filed          │                      │
      │   issue #42"     │                      │

Messages

Bob comments on the pull request:

Should we also handle prefers-contrast?

GitMsg: ext="social"; type="comment"; original="#commit:aaa111222333@gitmsg/review"; v="0.1.0"
GitMsg-Ref: ext="review"; type="pull-request"; author="Alice"; email="[email protected]"; time="2025-01-20T10:00:00Z"; ref="#commit:aaa111222333@gitmsg/review"; v="0.1.0"
 > Add dark mode support

Carol replies to Bob's comment:

Yes, good idea. I can follow up in a separate PR.

GitMsg: ext="social"; type="comment"; original="#commit:aaa111222333@gitmsg/review"; reply-to="#commit:ccc333444555@gitmsg/social"; v="0.1.0"
GitMsg-Ref: ext="social"; type="comment"; author="Bob"; email="[email protected]"; time="2025-01-20T11:00:00Z"; ref="#commit:ccc333444555@gitmsg/social"; v="0.1.0"
 > Should we also handle prefers-contrast?

8. Pull Request Lifecycle

Full state machine for a pull request:

         create
           │
           v
        ┌──────┐           ┌────────┐
        │ open │─────────> │ closed │
        └──┬───┘           └────────┘
           │               reopen via
           v               new PR
       ┌────────┐
       │ merged │
       └────────┘

9. Viewing Diffs in TUI

The TUI provides a files changed view accessible from the PR detail via d. It shows syntax-highlighted unified diffs with colored added/removed lines and line number gutters.

Inline feedback from the diff view pre-fills the file, line number, and side parameters when creating feedback commits, producing the same file, line-start, and side headers as CLI-created inline reviews.


10. Version Tracking and Review Staleness

When an author rebases or pushes new commits, pr update captures new branch tips as a version. Reviewers see version-aware staleness — using git range-diff to distinguish pure rebases from actual code changes.

    Alice                               Bob
      │                                  │
      ●  create PR (head=dark-mode)      │
      │  base-tip="aaa", head-tip="bbb"  │
      │                                  │
      │                                  ●  review, request changes
      │                                  │
      ●  push fix                        │
      ●  pr update                       │
      │  base-tip="aaa", head-tip="ccc"  │
      │                                  │
      │                                  ●  fetch → sees new version
      │                                  ●  pr show:
      │                                  │  "changes requested
      │                                  │   (reviewed original,
      │                                  │   current is latest,
      │                                  │   code changed) [stale]"
      │                                  │
      │                                  ●  pr diff → range-diff
      │                                  │  shows what changed
      │                                  ●  approve

Messages

Alice creates PR (version 0 = original):

Add dark mode support

GitMsg: ext="review"; type="pull-request"; state="open"; base="#branch:main"; base-tip="aaa111bbb222"; head="#branch:dark-mode"; head-tip="bbb222ccc333"; reviewers="[email protected]"; v="0.1.0"

Alice runs pr update after pushing fixes (version 1 = latest):

Add dark mode support

GitMsg: ext="review"; type="pull-request"; edits="#commit:aaa111222333@gitmsg/review"; state="open"; base="#branch:main"; base-tip="aaa111bbb222"; head="#branch:dark-mode"; head-tip="ccc333ddd444"; v="0.1.0"

Updates are explicit — the author signals "new code is ready" by running pr update. Not every WIP push triggers a version.

Version-Aware Staleness Detection

The reviewer's feedback has a timestamp. The PR's edits chain has timestamps and head-tip values. By comparing them, we know which version the reviewer saw. When the head-tip changed, git range-diff determines if the patches actually changed:


11. Merge Strategies

Four merge strategies available per-PR via --strategy flag. Teams pick per-PR instead of fighting over a repo-wide default.

    Alice
      │
      ●  pr merge <ref>                    # fast-forward (default)
      ●  pr merge <ref> --strategy squash  # squash all commits
      ●  pr merge <ref> --strategy rebase  # replay commits onto base
      ●  pr merge <ref> --strategy merge   # force merge commit

After a successful merge, the base branch is pushed to origin so the remote code agrees with the merged state published on gitmsg/review. A failed base push is reported as a warning (the merge itself already happened locally); push the base manually to recover.

Messages

Merge edit (same for all strategies — the git operation differs, but the review record is the same):

Add dark mode support

GitMsg: ext="review"; type="pull-request"; edits="#commit:aaa111222333@gitmsg/review"; state="merged"; base="#branch:main"; base-tip="ddd444eee555"; head="#branch:dark-mode"; head-tip="eee555fff666"; merge-base="ddd444eee555"; merge-head="eee555fff666"; v="0.1.0"

merge-base and merge-head are captured before the merge (lost after fast-forward). They enable reconstructing the original diff of a merged PR.


12. Branch Sync

Keep the head branch up-to-date with the base branch. pr sync rebases or merges, then auto-captures new tips.

    Alice
      │
      ●  pr show → "3 commits behind main"
      │
      ●  pr sync <ref>          # rebase head onto base (default)
      ●  pr sync <ref> --strategy merge    # merge base into head
      │
      │  → auto-runs pr update
      │  → new version with updated tips

After sync, the version history shows the rebase via range-diff. Reviewers can see what changed (if anything) between the pre-sync and post-sync versions.


13. Stacked Pull Requests

Decompose a large change into an ordered chain of small PRs. Each PR builds on the one below it — the bottom targets main, each subsequent PR targets the previous PR's branch. The depends-on field makes the relationship explicit so it survives branch renames and works across forges.

    Alice                              Bob
      │                                 │
      ●  push auth-middleware           │
      ●  pr create PR1                  │
      │  base=main, head=auth-middleware│
      │                                 │
      ●  push auth-routes               │
      ●  pr create PR2 --stack          │
      │  base=auth-middleware           │
      │  head=auth-routes               │
      │  → auto-sets depends-on=PR1     │
      │                                 │
      ●  push auth-tests                │
      ●  pr create PR3 --stack          │
      │  → auto-sets depends-on=PR2     │
      │                                 │
      │                                 ●  pr stack <PR2>
      │                                 │  shows full chain
      │                                 ●  review PR1, approve
      │                                 │
      ●  pr merge PR1                   │
      │  → PR2 auto-retargets to main   │
      │                                 │
      ●  pr rebase-stack PR2            │
      │  → rebases PR2, PR3 onto main   │
      │                                 ●  review PR2, approve
      │                                 │
      ●  pr merge PR2                   │
      │  → PR3 auto-retargets           │

Messages

Alice creates PR1 (root of stack):

Add auth middleware

GitMsg: ext="review"; type="pull-request"; state="open"; base="#branch:main"; base-tip="a1b2c3d4e5f6"; head="#branch:auth-middleware"; head-tip="b2c3d4e5f6a7"; v="0.1.0"

Alice creates PR2 (stacked on PR1):

Add API routes for auth

GitMsg: ext="review"; type="pull-request"; state="open"; base="#branch:auth-middleware"; base-tip="b2c3d4e5f6a7"; head="#branch:auth-routes"; head-tip="c3d4e5f6a7b8"; depends-on="#commit:abc123456789@gitmsg/review"; v="0.1.0"
GitMsg-Ref: ext="review"; type="pull-request"; author="Alice"; email="[email protected]"; time="2025-01-20T10:00:00Z"; ref="#commit:abc123456789@gitmsg/review"; v="0.1.0"
 > Add auth middleware

Alice merges PR1 — PR2 gets auto-retargeted:

After PR1 is merged, an edit commit is created for PR2 updating its base from #branch:auth-middleware to #branch:main:

Add API routes for auth

GitMsg: ext="review"; type="pull-request"; edits="#commit:def456789012@gitmsg/review"; state="open"; base="#branch:main"; base-tip="d4e5f6a7b8c9"; head="#branch:auth-routes"; head-tip="c3d4e5f6a7b8"; depends-on="#commit:abc123456789@gitmsg/review"; v="0.1.0"

The depends-on ref still points to the now-merged PR1 — the relationship is preserved as history. Only base changes to reflect the new merge target.

Stack Operations

View the full stack from any member:

gitsocial review pr stack <any-member-ref>
Stack (3 PRs):
  ✓ #1  Add auth middleware          main ← auth-middleware         [merged]
  ● #2  Add API routes for auth      auth-middleware ← auth-routes  [open]
  ● #3  Add auth integration tests   auth-routes ← auth-tests       [open]

Cascade rebase — when you update a PR in the middle of a stack, rebase all PRs above it:

gitsocial review pr rebase-stack <pr-ref>

Walks up the chain via depends-on. For each dependent: rebases its head branch onto its base and creates an edit with updated tips. Stops on first conflict with a clear error identifying which PR failed.

Sync stack tips — snapshot current branch tips for all open PRs (no rebase):

gitsocial review pr sync-stack <pr-ref>

Merge Ordering

pr merge refuses to merge a PR if any of its depends-on targets is unmerged. This enforces bottom-up order — you cannot merge PR2 while PR1 is still open.

Cross-Forge Stacks

Because depends-on uses the standard ref format with optional repo URLs, stacks can span forges:

PR1 on GitHub:  base="#branch:main"; depends-on=(none)
PR2 on GitLab:  base="https://github.com/org/repo#branch:auth-middleware";
                 depends-on="https://github.com/org/repo#commit:abc123@gitmsg/review"

No forge needs to understand "stacks" natively. Stack metadata lives in the git commit, readable by any GitSocial implementation regardless of hosting.

Platform Import

When importing PRs from GitHub or GitLab via gitsocial import review, stack relationships are auto-detected by matching base branches to head branches across imported PRs (same-repo, open PRs only). Each child PR gets an edit commit adding depends-on pointing to the parent.

The detection logic is platform-agnostic: both FetchReview adapters populate BaseBranch, HeadBranch, and HeadRepo uniformly on ImportPR, so the same matching rules apply regardless of source.

TUI Support