2e84dece
Add a README
a73x 2026-08-09 14:57
Commit message
README.md
| Old | New | ||
|---|---|---|---|
| @@ -0,0 +1,177 @@ | |||
| 1 | # git-collab | ||
| 2 | |||
| 3 | Distributed issues and code review over Git. | ||
| 4 | |||
| 5 | Issues, patches, reviews and comments are stored as signed events in the | ||
| 6 | repository itself, under `refs/collab/*`. They travel with `git fetch` and | ||
| 7 | `git push`, so collaboration works between any two clones — and there is no | ||
| 8 | account to create, no API to call, and nothing to migrate if you move host. | ||
| 9 | |||
| 10 | An optional server adds a read-only web UI, SSH remotes and release artifacts, | ||
| 11 | but nothing depends on it. Two people with a shared remote have everything. | ||
| 12 | |||
| 13 | ## Why | ||
| 14 | |||
| 15 | Review lives in the wrong place. It is the record of *why* code looks the way | ||
| 16 | it does, and it usually ends up in a database belonging to whoever hosts the | ||
| 17 | repository. Issues, comments and reviews are as much a part of a project's | ||
| 18 | history as its commits, and they should be as portable. | ||
| 19 | |||
| 20 | What that buys, concretely: | ||
| 21 | |||
| 22 | - **Offline.** File an issue, review a patch and reply to a comment on a plane. | ||
| 23 | Reconcile on the ground. | ||
| 24 | - **Attributable.** Every event is signed with Ed25519 and verified on sync. | ||
| 25 | You choose which keys you trust. | ||
| 26 | - **Yours.** Clone the repository and you have the whole conversation. | ||
| 27 | |||
| 28 | ## Revision-aware review | ||
| 29 | |||
| 30 | The part that is deliberately unlike a pull request. | ||
| 31 | |||
| 32 | A patch has numbered revisions. Comments anchor to the revision they were | ||
| 33 | written on, so they never drift onto lines that have since moved. When an author | ||
| 34 | rebases or amends, the next revision is recorded automatically, and you can diff | ||
| 35 | revision against revision — an interdiff — to see exactly what the author | ||
| 36 | changed in response to review, rather than re-reading the whole patch. | ||
| 37 | |||
| 38 | ```console | ||
| 39 | $ git-collab patch log a1b2c3d4 # every revision, with a file-change summary | ||
| 40 | $ git-collab patch diff a1b2c3d4 --between 1 2 # interdiff: what changed between rounds | ||
| 41 | $ git-collab patch diff a1b2c3d4 --revision 2 # revision 2 against the base | ||
| 42 | ``` | ||
| 43 | |||
| 44 | Merging is not a command. A patch is a branch; when its commits become reachable | ||
| 45 | from the base branch, `git-collab` notices and marks the patch merged. Merge | ||
| 46 | however you already merge. | ||
| 47 | |||
| 48 | ## Install | ||
| 49 | |||
| 50 | Requires Rust 1.88 or newer. | ||
| 51 | |||
| 52 | ```console | ||
| 53 | $ make install # installs git-collab and git-collab-server, plus man pages | ||
| 54 | ``` | ||
| 55 | |||
| 56 | ## Getting started | ||
| 57 | |||
| 58 | ```console | ||
| 59 | $ cd your-repo | ||
| 60 | $ git-collab init-key # generate an Ed25519 signing key | ||
| 61 | $ git-collab init # add collab refspecs to your remotes | ||
| 62 | |||
| 63 | $ git-collab issue open -t "Parser drops trailing newline" | ||
| 64 | $ git-collab issue list | ||
| 65 | |||
| 66 | $ git checkout -b fix-parser | ||
| 67 | $ ...work, commit... | ||
| 68 | $ git-collab patch create -t "Fix trailing newline in parser" | ||
| 69 | |||
| 70 | $ git-collab sync # fetch, reconcile, push | ||
| 71 | ``` | ||
| 72 | |||
| 73 | Reviewing someone else's patch: | ||
| 74 | |||
| 75 | ```console | ||
| 76 | $ git-collab sync | ||
| 77 | $ git-collab patch list | ||
| 78 | $ git-collab patch checkout a1b2c3d4 # local branch at the latest revision | ||
| 79 | $ git-collab patch comment a1b2c3d4 --file src/parse.rs --line 42 -m "off by one?" | ||
| 80 | $ git-collab patch review a1b2c3d4 --verdict request-changes -m "see inline" | ||
| 81 | ``` | ||
| 82 | |||
| 83 | `git-collab dashboard` opens a TUI over the same data if you would rather browse | ||
| 84 | than type. | ||
| 85 | |||
| 86 | ## Commands | ||
| 87 | |||
| 88 | | | | | ||
| 89 | |---|---| | ||
| 90 | | `issue` | open, list, show, comment, edit, label, assign, close | | ||
| 91 | | `patch` | create, list, show, diff, comment, review, revise, log, checkout, close | | ||
| 92 | | `sync` | fetch, reconcile and push collab refs | | ||
| 93 | | `status` | project overview | | ||
| 94 | | `dashboard` | interactive TUI | | ||
| 95 | | `search` | full-text across issues and patches | | ||
| 96 | | `log` | raw event stream, chronological | | ||
| 97 | | `key` | manage trusted signing keys | | ||
| 98 | | `whoami`, `identity` | your identity and its aliases | | ||
| 99 | | `release` | publish, list and delete artifacts on a server | | ||
| 100 | |||
| 101 | Every command takes `--help`, and `man git-collab` covers the same ground. | ||
| 102 | |||
| 103 | ## Trust | ||
| 104 | |||
| 105 | Sync verifies every signature it fetches. Until you add a trusted key, valid | ||
| 106 | signatures are accepted with a warning — enough to get started, not enough to | ||
| 107 | rely on. | ||
| 108 | |||
| 109 | ```console | ||
| 110 | $ git-collab key add --self # trust your own key | ||
| 111 | $ git-collab key add <base64-pubkey> --label alice | ||
| 112 | $ git-collab key list | ||
| 113 | ``` | ||
| 114 | |||
| 115 | Events signed by an untrusted key are reported rather than silently applied. | ||
| 116 | Trusted keys live in `.git/collab/trusted-keys`, with an optional global list. | ||
| 117 | |||
| 118 | ## Conflict resolution | ||
| 119 | |||
| 120 | Two people can edit the same issue while disconnected. Events carry a Lamport | ||
| 121 | clock, and reconciliation orders them by `(clock, oid)` — deterministic on every | ||
| 122 | machine, with no dependence on wall-clock time or on who synced first. Both | ||
| 123 | edits survive; the ordering is just agreed. | ||
| 124 | |||
| 125 | ## Server | ||
| 126 | |||
| 127 | `git-collab-server` serves repositories over SSH and gives them a read-only web | ||
| 128 | UI: repository list, commits, tree and blob browser, diffs, and the issues and | ||
| 129 | patches from `refs/collab/*` rendered as pages. | ||
| 130 | |||
| 131 | ```console | ||
| 132 | $ git-collab-server --config /etc/git-collab/server.toml | ||
| 133 | ``` | ||
| 134 | |||
| 135 | ```toml | ||
| 136 | repos_dir = "/srv/git" | ||
| 137 | http_bind = "0.0.0.0:8080" | ||
| 138 | ssh_bind = "0.0.0.0:2222" | ||
| 139 | authorized_keys = "/etc/git-collab/authorized_keys" | ||
| 140 | site_title = "my repos" | ||
| 141 | ``` | ||
| 142 | |||
| 143 | Per-repository policy lives in `<repo>.git/.collab/server.toml` and controls | ||
| 144 | visibility, anonymous clone and per-key read/write access. A repository with no | ||
| 145 | policy file is public and world-writable to any key in `authorized_keys` — set a | ||
| 146 | policy before serving anything you care about. | ||
| 147 | |||
| 148 | `make docker` builds a container image. | ||
| 149 | |||
| 150 | ### Releases | ||
| 151 | |||
| 152 | ```console | ||
| 153 | $ git-collab release publish v1.2.0 dist/app-x86_64.tar.gz | ||
| 154 | $ git-collab release list | ||
| 155 | ``` | ||
| 156 | |||
| 157 | Artifacts upload over SSH and download over HTTP, with server-computed SHA-256 | ||
| 158 | checksums, range requests and resumable downloads. | ||
| 159 | |||
| 160 | ## Storage | ||
| 161 | |||
| 162 | Everything lives in ordinary git objects. | ||
| 163 | |||
| 164 | ``` | ||
| 165 | refs/collab/issues/<id> one commit per event, DAG-ordered | ||
| 166 | refs/collab/patches/<id> | ||
| 167 | refs/collab/archive/... closed items, kept out of the working set | ||
| 168 | ``` | ||
| 169 | |||
| 170 | Each event commit's tree holds the event as canonical JSON alongside its | ||
| 171 | detached signature and public key. Nothing is stored outside the object | ||
| 172 | database, so `git gc`, `git fsck` and every other git tool work unchanged. | ||
| 173 | |||
| 174 | ## Status | ||
| 175 | |||
| 176 | Early. The format has changed before and may change again; there is no | ||
| 177 | compatibility guarantee yet. | ||