# Concepts

otisk is a git host, and everything you know about git still applies. What
differs is the layer above: how a piece of work is proposed, discussed and
accepted. If you have used GitHub or GitLab, roughly half of what you expect
is here under a different name, and the other half is deliberately absent.

If you have used Gerrit, you already know this model. otisk borrows it
wholesale, because it is the one that treats a commit as the unit of review.

## The short version

| GitHub or GitLab | otisk |
| --- | --- |
| A pull request, holding a branch of commits | A **change**, which is one commit |
| Push more commits to fix review comments | **Amend** the commit and push again |
| Force-push, and the old version is gone | Each push is a **revision**, kept forever |
| Merge, usually making a merge commit | **Land**, which only ever fast-forwards |
| Stacked PRs, and the tooling to survive them | A push of several commits, each its own change |
| Comments in the forge's database | Comments in the repository, as git objects |
| An account, a password, a token, a session | Your SSH key, and nothing else |
| A web interface | The command line, and a terminal interface |

## A change is a commit

On GitHub, the unit of review is a branch: you make a branch, put commits on
it, open a pull request, and the reviewer reads the diff of the whole branch.
The individual commits on it are usually noise — "fix typo", "address
review" — and get squashed away at the end.

In otisk, the unit of review is one commit. You write it, push it for review,
and when the reviewer asks for something you **amend that commit** and push it
again. There is no branch to name, no pull request to open, and no squash at
the end, because there was never anything to squash.

```sh
git commit -m "Write down an idea"
otisk change push                    # this commit is now change 1
```

What makes the amended commit *the same change* is a `Change-Id` trailer in
its message, added by a hook. The commit's SHA changes every time you amend;
the `Change-Id` does not. That trailer is the whole trick.

## Nothing you pushed is lost

Amending rewrites history, which on GitHub means force-pushing, which means
the version your reviewer read yesterday is gone — and the comments they left
point at line numbers in a diff that no longer exists.

Here, every push of a change is kept as an immutable ref:

```
refs/changes/1/1     what you pushed first
refs/changes/1/2     what you pushed after the review
refs/changes/1/3     ...
```

Those are called **revisions**, and they are ordinary git refs you can fetch,
diff and check out. `otisk change diff 1` shows what changed between the last
two — a range-diff, so you see how the change changed, not what the change
does.

A comment records the revision and line it was written about, so it keeps
pointing at what its author actually read.

## Landing is a fast-forward

Merging a pull request usually makes a merge commit, or squashes, or rebases,
depending on which button someone pressed. `main` ends up with a history that
is some mixture of what happened and what the tooling did.

Landing a change fast-forwards the branch to the change's latest revision. If
the branch has moved on, the land is refused, and you rebase and push a new
revision. That is the only thing landing does. The result is that `main` is a
straight line of reviewed commits, each of which is a change somebody read.

## Stacks are ordinary

Stacked pull requests are painful everywhere: the second PR's diff includes
the first, and rebasing one rewrites the others.

Here a push may carry several commits, and each becomes its own change:

```sh
git commit -m "Add a lexer"        # on top of the change before it
otisk change push                  # both commits, a change each
```

Each is reviewed on its own, and they land bottom-up — a change whose parent
has not landed is refused. Amending any of them and pushing again gives that
change a new revision and leaves the others alone.

## The conversation is in the repository

On GitHub, your code is in git and your review is in a database you cannot
clone. Export it and you get JSON, if the API still exists.

otisk keeps comments in the repository, as a commit per comment on
`refs/changes/<number>/meta`. They are stored, replicated and restored exactly
as your code is, and you can read them with git alone:

```sh
git fetch origin refs/changes/1/meta:refs/otisk/meta
git log refs/otisk/meta
```

The database has an index of them, which exists to answer questions quickly.
It is a fold of the log and can be thrown away and rebuilt.

## Your key is your account

There is nothing to log into. Authentication is the SSH key you already push
with: the host recognises its fingerprint and knows who you are. No password,
no token, no session, no OAuth app, no second factor to lose.

The consequence is that someone with access to the host must add your key
before you can do anything — there is no self-service sign-up, because there
is no sign-up.

## Disk is a cache

This one is invisible in use, and shapes everything underneath. A repository
lives in an object store; the copy on the server's disk is a cache that can be
deleted at any moment. A repository reaches disk only by being hydrated from
durable storage.

So a server that has never seen your repository is a normal state rather than
a disaster, and losing the disk is not an outage. [How it
works](how-it-works.md) has the detail.

## What you give up

Honestly, a lot:

- **No web interface.** No browsing code, no reading diffs in a browser, no
  sending someone a link to a line.
- **No issues**, no wikis, no project boards, no discussions.
- **No CI.** Nothing runs when you push.
- **No forks and no pull requests across repositories.** Review happens in
  the repository the work is for.
- **No organizations, teams, or fine-grained permissions.** There are
  accounts, and repositories they own.
- **Almost nobody else uses this.** Gerrit's model is well proven; this
  particular implementation of it is small, new, and maintained by one person.

What you get for that is a review model where a change is one reviewable
commit, nothing you pushed is ever lost, history is a straight line, and the
whole conversation is in git — plus a server whose durability story is "the
object store is the truth" rather than "do not lose the disk".

If that trade is not obviously worth it for you, it probably is not.
