Docs menu

Making changes

.md

Status, diff, checkpoints, push and pull, ignore files, hooks, and undoing things.

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

See what changed#

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
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.

Checkpoint with oak commit#

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. 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#

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.

SituationWhat to do
First push of a repo made with oak initoak push --repo <org>/<name> (or answer the prompt on a terminal). See Quickstart.
Push was rejected because the remote branch movedoak pull, then push again.
You really mean to overwrite the remote branchoak push --force.
You want to see what would be sent firstoak push --plan --json (read-only).

Pushing to main is always refused β€” main only changes by merging.

Stay in sync with oak pull#

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.

CommandWhat it does
oak pullFetch your branch, then merge main into it.
oak pull --branch-onlyFetch your branch only; skip merging main.
oak fetchRefresh your local copy of main without touching your branch or working tree.
oak pull --forceThrow away local commits the server doesn't have and match the remote branch.

Undo things#

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:

PathPurpose
.oak/workflows/*.ymlCI workflows
.oak/PERMISSIONSPath permissions
.oak/attributesReserved for per-path attributes

Hooks#

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

HookWhenOn failure
pre-commitBefore oak commit writes anythingAborts the commit
post-commitAfter a successful commitPrints 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.

A typical Rust pre-commit:

#!/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.

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.

Something here wrong or missing? Run oak feedback -m "…" or email [email protected].