# Making changes

The everyday loop: see what changed, checkpoint it, publish it, and stay in sync with `main`.

## See what changed

```bash
oak status                 # modified, added, deleted files
oak status --short         # Git-compatible short format (same as --porcelain)
oak status --json --compact  # bounded, machine-readable — what agents should use
```

```bash
oak diff                   # working tree vs. your last checkpoint
oak diff src/ README.md    # only these paths
oak diff --branch          # everything on this branch (commits + uncommitted) vs. where it forked from main
oak diff --stat            # per-file +/- counts
oak diff --name-only       # just the paths
oak diff --print           # plain unified diff to stdout instead of the viewer
oak diff --check           # whitespace errors and leftover conflict markers; exits 1 if any
```

On a terminal, `oak diff` opens a full-screen viewer — a file tree beside the diff — that takes arrow keys, `less`/vi keys, and emacs keys. Press `?` inside it for every binding. Piped, or with `--print` / `--json`, it prints instead. Set `OAK_DIFF_TOOL` to use an external diff tool.

`oak diff` can also compare branches and commits without checking anything out — see [Reviewing branches](/docs/reviewing#read-a-diff-without-switching).

## Checkpoint with oak commit

```bash
oak commit                 # snapshot every change in the working tree
oak commit src/parser/     # only changes under these paths
oak commit --push          # checkpoint, then publish the branch
```

There's no staging area and **no commit message** — `-m` is refused with a pointer to `oak desc`. Commits are checkpoints you can return to; what the branch *does* is written once, as its [description](/docs/branches#descriptions). Commit as often as is useful: they're all squashed into one commit on `main` at merge time.

`oak commit` only writes to your local repo. Nothing leaves your machine until you push.

## Publish with oak push

```bash
oak push
```

Pushes the current branch's new commits — and its description — to the server. Only the content chunks the server doesn't already have are uploaded, so re-pushing a large binary with a small change sends only the changed chunks.

| Situation | What to do |
|---|---|
| First push of a repo made with `oak init` | `oak push --repo <org>/<name>` (or answer the prompt on a terminal). See [Quickstart](/docs/quickstart#4-publish-the-branch). |
| Push was rejected because the remote branch moved | `oak pull`, then push again. |
| You really mean to overwrite the remote branch | `oak push --force`. |
| You want to see what would be sent first | `oak push --plan --json` (read-only). |

Pushing to `main` is always refused — `main` only changes by [merging](/docs/merging).

## Stay in sync with oak pull

```bash
oak pull
```

`oak pull` does two things: it fetches any new commits on **your** branch from the server (pushed from another machine, or by an agent), then merges the latest **`main`** into your branch. Conflicts surface here, on your branch, where you can take your time — see [Merging and conflicts](/docs/merging#resolving-conflicts).

| Command | What it does |
|---|---|
| `oak pull` | Fetch your branch, then merge `main` into it. |
| `oak pull --branch-only` | Fetch your branch only; skip merging `main`. |
| `oak fetch` | Refresh your local copy of `main` without touching your branch or working tree. |
| `oak pull --force` | Throw away local commits the server doesn't have and match the remote branch. |

## Undo things

```bash
oak restore path/to/file      # put a file back to your last checkpoint
oak restore -s 3f9fab02 file  # …or to how it was at another commit
oak reset                     # discard every uncommitted change (asks first; -f to skip)
oak reset src/                # …or just under a path
```

To go back to an earlier checkpoint as a starting point, `oak checkout <hash>` detaches HEAD there; `oak switch -c` from that state starts a new branch. To reshape a branch's commits — drop one, reorder them, or split them into separate branches — use `oak split` (Oak's answer to `git rebase -i`).

## Ignore files

Put gitignore-syntax patterns in **`.oakignore`** at the repo root. If there's no `.oakignore`, Oak reads `.gitignore` instead — so an imported Git repo keeps working unchanged. (When both exist, only `.oakignore` counts.)

Always ignored, whatever the files say: `.oak/` (except the tracked config files below), `.git/`, and OS clutter such as `.DS_Store`, `._*` AppleDouble files, `Thumbs.db`, and `desktop.ini`.

`oak init` offers a ready-made `.oakignore` when it detects a Godot, Unity, or Unreal project.

## Tracked files under .oak/

The `.oak/` directory holds your local repository database, which is never committed. A few files inside it are ordinary, versioned files that the server reads:

| Path | Purpose |
|---|---|
| `.oak/workflows/*.yml` | [CI workflows](/docs/ci-workflows) |
| `.oak/PERMISSIONS` | [Path permissions](/docs/path-permissions) |
| `.oak/attributes` | Reserved for per-path attributes |

## Hooks

Executable files at `.oak/hooks/<event>` run at fixed points, like Git hooks:

| Hook | When | On failure |
|---|---|---|
| `pre-commit` | Before `oak commit` writes anything | Aborts the commit |
| `post-commit` | After a successful commit | Prints a warning |

Hooks run from the repo root with `OAK_HOOK` set to the event name. They're **local only** — never pushed — so each person chooses what runs on their machine; files ending in `.sample` are ignored. Skip them for one commit with `oak commit --no-verify`. Hooks don't run inside a [mount](/docs/mounts).

A typical Rust `pre-commit`:

```bash
#!/bin/sh
set -e
cargo fmt --all
cargo clippy --workspace -- -D warnings
```

## Large files

There's nothing to configure. Every file is split into content-defined chunks and stored once per organization, so large binaries, game assets, and datasets are just files: pushes and pulls move only the chunks that changed, and the same asset in two repos of one org is stored once. A single push can carry up to 25 GiB; see [Plans and limits](/docs/limits).

## Exit codes

Every command uses the same exit codes, so scripts can branch without parsing output — for example `5` means "conflicts" and `3` means "repository locked, retry". The full table is in the [CLI reference](/docs/cli#exit-codes).
