Skip to main content

🌲 forest Animated isometric forest: data trees on a workspace platform, packets syncing up a git branch to a remote cloud

git-like data management for arbitrary data trees.

Site PyPI Python 3.10+ License: MIT Ruff mypy: strict

Forest is the data-side parallel to git's version control. Git tracks code in .git/; forest tracks large data — local layout plus remote sync — in .forest/. It borrows git's mental model and verbs (checkout, status, push, pull, remote, a HEAD-style pointer) so your git intuition carries over, but the two domains never overlap and neither requires the other.

Forest is domain-agnostic and self-contained: it manages any data trees, knows nothing about what the data means, and depends on no other project. It moves files and tracks their sync state; it does not validate or interpret their contents.

Documentation

Full docs live at tmsincomb.github.io/forest. Start with the hands-on tutorial — seven short chapters from forest init to a fully synced repo, with every output block captured from a real session and no cloud credentials required.

Model

Fixed-depth, no arbitrary nesting:

workspace → checkout → stage → unit → files
  • Workspace — a per-repo .forest/ control area (registry + active pointer).
  • Checkout — a named data view/focus registered in the workspace (like a git branch you stay rooted in). Switching is an O(1) pointer rewrite; data never moves.
  • Stage — a named data category inside a checkout, with a remote layout.
  • Unit — one addressable item within a stage (a subdirectory, a directory, or a file, per the stage's sync_by).

Install

pip install forest-cli

Requires rclone on $PATH for transfers. For a development install, clone the repo and pip install -e ".[dev]".

Quick start

No config files are hand-written. Onboarding is a few commands:

forest init                      # create the nameless .forest/ workspace container
forest checkout demo             # create if absent, register + activate 'demo'
forest remote add origin s3://my-bucket/prefix   # allowed before any local data exists
forest add raw ./data/raw        # register stage 'raw' and bind it to a local path
forest push                      # sync every bound stage to the active remote
  • forest init creates only .forest/config.yaml (version: 1, checkouts: {}) and a managed .gitignore. No root config, no checkout, no active pointer.
  • forest checkout <name> switches to the checkout, creating, registering, and activating it first (with .forest/checkouts/<name>/forest.yaml) when the name is not registered. forest checkout create <name> is the explicit form.
  • Remotes can be added before any local binding — useful when your data is remote-only at first.
  • forest add STAGE PATH registers a new stage and binds it to a local path in one step (use forest bind to rebind an existing stage).

Metadata layout

Everything forest owns lives under .forest/; your data does not.

.forest/
  config.yaml                     # workspace registry: version, checkouts{}
  HEAD                            # active checkout name (gitignored)
  checkouts/
    demo/
      forest.yaml                 # shared: stages, remotes, manifest
      local.yaml                  # user-local: active_remote, stage_paths (gitignored)
      sync_state.json             # user-local push/pull state (gitignored)

Shared metadata (config.yaml, each forest.yaml) is committed so a fresh clone bootstraps with bind + remote use + pull. User-local files (HEAD, local.yaml, sync_state.json) are gitignored.

Commands

Command Purpose
forest init Create the workspace container, or report setup status if it exists.
forest checkout create/list/current/remove <name> Manage checkouts; bare forest checkout <name> switches, creating first if needed. remove --yes skips the prompt for scripts.
forest add STAGE PATH [--sync-by MODE] Register a new stage and bind it to a local path; --sync-by picks unit discovery (subdirectory/directory/file).
forest bind [STAGE PATH] / forest unbind STAGE Manage local stage↔path bindings.
forest remote add/remove/list/use/show Manage remotes; use selects the active remote (optional while only one remote exists).
forest push / pull / status / diff / ls Sync and inspect against the active remote. Bare push/pull/status/diff cover every bound stage (unbound stages warn and skip); --all requires all stages bound.
forest flow Emit a Mermaid data-flow diagram of the active checkout.
forest prompt init bash|zsh Print an eval-able snippet that shows the active checkout in your shell prompt.

Run any command with -C <path> to operate on another repo without cd.

Forest syncs all files in a data unit, skipping OS junk (.DS_Store, AppleDouble ._*, *.tmp). It applies no content-based include/exclude rules. Push and pull skip a clean unit when source hashes prove its content and path are unchanged; a backend that cannot provide a usable hash transfers safely.

Terminal output

On an interactive terminal forest renders rich output — colored status tables, file trees, panels, and live progress bars during push/pull (rclone JSON stats streamed to a bar; needs rclone ≥ 1.49, older versions transfer fine without a bar). When stdout is piped or redirected, output falls back to the plain, machine-parseable format, so scripts never see ANSI codes or box drawing.

Overrides: --color/--no-color per invocation, FOREST_OUTPUT=rich|plain|auto in the environment, and the NO_COLOR/FORCE_COLOR conventions. Precedence: flag > FOREST_OUTPUT > NO_COLOR > FORCE_COLOR > TTY detection. --verbose shows rclone's raw transfer log instead of a progress bar; --quiet suppresses everything but errors.

Shell prompt

Show the active checkout in your prompt (bash and zsh), the way git prompts show the current branch:

# ~/.bashrc
eval "$(forest prompt init bash)"

# ~/.zshrc
eval "$(forest prompt init zsh)"
~/lab/G004 $                # outside a workspace: unchanged
🌲 G004 ~/lab/G004 $        # inside a workspace with an active checkout

The snippet is pure shell — it reads .forest/HEAD directly and never invokes the forest CLI while rendering, so your prompt stays fast. Set FOREST_PROMPT_ICON to replace the 🌲.

Config reference

Checkout forest.yaml (shared, committed):

project: demo
remotes:
  origin:
    url: s3://my-bucket/prefix
    region: us-east-2          # optional; also endpoint, profile, key_file, known_hosts
stages:
  raw:
    remote_path: demo/raw      # optional; defaults to <checkout>/<stage>
    sync_by: subdirectory      # subdirectory | directory | file
    direction: both            # push | pull | both; bare push/pull skip
                               # wrong-direction stages, explicit selection errors

Checkout local.yaml (per-machine, gitignored):

active_remote: origin
stage_paths:
  raw: ../data/raw             # relative resolves from the workspace root

Environment variables

All optional, all off by default — forest is silent and sends nothing anywhere unless configured. Copy .env.example for a commented template. Logs are always secret-scrubbed.

Variable Default Effect
FOREST_LOG_FILE unset Append structured logs (JSON lines) to this file.
FOREST_LOG_FORMAT json json or text; set without FOREST_LOG_FILE to log to stderr.
FOREST_LOG_LEVEL INFO Standard logging level name.
FOREST_TRANSFER_RETRIES 2 Extra attempts for transient rclone failures; 0 disables.
FOREST_RETRY_BASE_DELAY 0.5 Initial retry backoff in seconds; doubles per attempt.
FOREST_OUTPUT auto Terminal rendering: rich, plain, or auto (rich on a TTY, plain when piped).
NO_COLOR / FORCE_COLOR unset Standard color conventions; FOREST_OUTPUT and --color/--no-color take precedence.

Dogfood: this repo runs forest

This repository manages its own examples/ and tests/data/ trees with forest — a live demonstration that .forest/ and .git/ coexist without overlapping. It was set up with exactly the quick-start commands:

forest init
forest checkout demo
forest remote add origin s3://forest-cli-test --region us-east-2
forest add examples ./examples                        # two-way stage (direction: both)
forest add data ./tests/data --sync-by directory      # two small test files
forest push                                           # seeds both stages

After the seeding push, data was flipped to direction: pull in the checkout's forest.yaml — it is a read-only reference mirror now: a bare forest push skips it with a note, and forest push --stage data refuses with exit 1 (a new stage can start there directly with forest add ... --direction pull). The examples stage keeps syncing both ways.

Inspect the result:

git ls-files .forest        # what a clone gets: config.yaml + checkouts/demo/forest.yaml
cat .gitignore              # forest-managed: HEAD, local.yaml, sync_state.json stay local
forest ls                   # stage overview: data shows (directory, pull)
forest status               # sync state of both stages

A fresh clone bootstraps the local half (HEAD, bindings, sync state are gitignored) with:

forest checkout demo               # select the checkout; clones have no HEAD
forest bind examples ./examples
forest bind data ./tests/data
forest pull --force                # first pull: no local sync state yet, so
                                   # forest refuses to touch the git-tracked
                                   # copies without it

The single configured remote is used automatically. Pulling needs AWS credentials for the bucket; the layout is the demonstration.

Notes

  • Single active machine (v1). HEAD/local.yaml/sync_state.json are git-invisible but may be synced by a file-syncing tool; forest assumes one active machine and uses atomic writes plus a per-checkout flock for intra-machine write races.
  • Real filenames. Forest stores data under real paths, not a content-addressed blob store.
  • See docs/adr/ for the design decisions behind the workspace/checkout model.

Metadata

Release files for forest-cli 1.1.0

For a detailed explanation of source distributions (sdists) and built distributions (wheels), please see the package formats documentation.

Source distribution (sdist)

Source distribution for forest-cli 1.1.0
File Size Uploaded Provenance Actions
forest_cli-1.1.0.tar.gz 180.6 kB With provenance Details for forest_cli-1.1.0.tar.gz

Built distribution (wheel)

Table of built distributions (wheels) for forest-cli 1.1.0
File Interpreter ABI Platform Actions
forest_cli-1.1.0-py3-none-any.whl Python 3 none any Details for forest_cli-1.1.0-py3-none-any.whl

Total release size: 251.8 kB

Release files / forest_cli-1.1.0.tar.gz

Download URL forest_cli-1.1.0.tar.gz
Size 180.6 kB
Tags Source
SHA-256 checksum
How to use checksums
9f503acae5a13ddb9123e8f58cc6b317f043924f824797014e8504e70e247d82
BLAKE2b-256 checksum
How to use checksums
1cb033f80f2b502071da54edd2b511bc0fa6d55b6470227832e854b1698d52af
Upload date
Uploaded using Trusted Publishing?
What is trusted publishing?
Yes
Uploaded via twine/7.0.0 CPython/3.13.14

Provenance

Provenance describes where a file came from. On PyPI, provenance is shared via attestations, which provide a verifiable record of the build or publishing details. View details, limitations and caveats.

PyPI Publish Attestation

PyPI verified that this artifact, at this checksum, originated from the publisher listed below.

Signed by GitHub Actions, verified by PyPI on Sep 8, 2026.

Transparency log

Release files / forest_cli-1.1.0-py3-none-any.whl

Download URL forest_cli-1.1.0-py3-none-any.whl
Size 71.3 kB
Tags Python 3
SHA-256 checksum
How to use checksums
24a6cd55a71816b9ebd69e6437447dd0a6f66415a7859d6af96961b5dcd99327
BLAKE2b-256 checksum
How to use checksums
66fd9b3159912bddfacd2027eb0b3f62954d28eea089aba5a112d3851c04d2cc
Upload date
Uploaded using Trusted Publishing?
What is trusted publishing?
Yes
Uploaded via twine/7.0.0 CPython/3.13.14

Provenance

Provenance describes where a file came from. On PyPI, provenance is shared via attestations, which provide a verifiable record of the build or publishing details. View details, limitations and caveats.

PyPI Publish Attestation

PyPI verified that this artifact, at this checksum, originated from the publisher listed below.

Signed by GitHub Actions, verified by PyPI on Sep 8, 2026.

Transparency log

Security for forest-cli 1.1.0

Provenance

Provenance describes where a file came from. On PyPI, provenance is shared via attestations, which provide a verifiable record of the build or publishing details. View details, limitations and caveats.

Full provenance

All 2 files in this release have been uploaded with provenance metadata.

Select from 2 files

Attestation for forest_cli-1.1.0.tar.gz

PyPI Publish Attestation

PyPI verified that this artifact, at this checksum, originated from the publisher listed below.

Signed by GitHub Actions, verified by PyPI on Sep 8, 2026.

Transparency log

A note on package security

PyPI is a repository for distributing packages, not a service for auditing them. While third-party security researchers and our staff do their best to detect and remove malicious packages, PyPI cannot guarantee the quality, security or suitability of any given package.

To learn more about protecting yourself from malicious actors, visit our package security documentation.

Anthropic, PBC Visionary sponsor Bloomberg Visionary sponsor Hudson River Trading Visionary sponsor Meta Visionary sponsor NVIDIA Visionary sponsor Microsoft Sustainability sponsor Depot Continuous Integration AWS Cloud computing and Security Sponsor Datadog Monitoring Fastly CDN Google Download Analytics Sentry Error logging StatusPage Status page